0

I need help to understand how ORM works. Here is the scenario that is very common. I have two main tables Organization and RelatedParty which have to be in many to many relation. But there is also relation_type attribute that defines what kind of relation exists between Organization and Relatedparty.

relational database entity model

Here are my entity classes: Organization:

@Entity
@Table(name = "organization", catalog = "...", schema = "")
@XmlRootElement
public class Organization implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "organization_id", nullable = false)
    private Integer organizationId;
    @Column(name = "organization_name", nullable = false)
    private String organizationName;
    @OneToMany(cascade = {CascadeType.ALL}, mappedBy = "organization")
    private List<Organdrelatedparty> organdrelatedpartyList;
...
//getter setter methods

Organdrelatedparty: which uses composite primary key OrgandrelatedpartyPK

@Entity
@Table(name = "organdrelatedparty", catalog = "...", schema = "")
@XmlRootElement
public class Organdrelatedparty implements Serializable {
    private static final long serialVersionUID = 1L;
    @EmbeddedId
    protected OrgandrelatedpartyPK organdrelatedpartyPK;
    @JoinColumn(name = "relatedParty_id", referencedColumnName = "relatedParty_id", nullable = false, insertable = false, updatable = false)
    @ManyToOne(optional = false, cascade= {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH})
    private Relatedparty relatedparty;
    @JoinColumn(name = "orgRelation_id", referencedColumnName = "orgRelation_id", nullable = false)
    @ManyToOne(optional = false)
    private ParOrgrelationtype orgRelationid;
    @JoinColumn(name = "organization_id", referencedColumnName = "organization_id", nullable = false, insertable = false, updatable = false)
    @ManyToOne(optional = false)
    private Organization organization;
...
//getter setter methods

OrgandrelatedpartyPK

@Embeddable
public class OrgandrelatedpartyPK implements Serializable {
    @Basic(optional = false)
    @NotNull
    @Column(name = "relatedParty_id", nullable = false)
    private int relatedPartyid;
    @Basic(optional = false)
    @NotNull
    @Column(name = "organization_id", nullable = false)
    private int organizationId;
...
//getter setter methods

RelatedParty: which is in unidirectional oneToMany relationship with organdRelatedParty class. In other word that relatedParty entity has no knowledge about organdRelatedParty entity that is on the other side.

@Entity
@Table(name = "relatedparty", catalog = "...", schema = "")
@XmlRootElement
public class Relatedparty implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "relatedParty_id", nullable = false)
    private Integer relatedPartyid;
    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 100)
    @Column(name = "firstName", nullable = false, length = 100)
    private String firstName;
    @Size(max = 100)
    @Column(name = "lastName", length = 100)
    private String lastName;
    @Basic(optional = false)
    @NotNull
    @Column(name = "isForeign", nullable = false)
    private boolean isForeign;
...
//getter setter methods

For insertion, if I persist new Organization Entity, it cascades persist activity to new OrgandrelatedParty which also cascades persist activity to new RelatedParty. So all the related entities are persisted and it works fine.

For updating, User is expected to change existing organization and relatedParty entities and also add new relatedParty to organization. So we prefer to delete all OrgandrelatedParties first and add new relatedParties and edited relatedParties again after that.

This is our method that handles updating: We pass new organization and also all new and old relatedParties as a list to method: firs we delete all old OrgAndRelatedParties then we create again all relatedParties in list as new OrgandrelatedParties. This is main method to update organization.

 public void updateOrganization(Organization newOrganization, List<Relatedparty> newShareList) throws ControlException {
    try{
        tx.begin();
        this.updateOrgAndRelatedShares(newOrganization, newShareList);
        customerController.updateOrganization(newOrganization);
        tx.commit();
    }catch(ControlException ex){
...

customerController's updateOrganization method does first find old Organization by find method of entity manager then copies all attributes of new organization to old then merges old organization and flush:

public void updateOrganization(Organization newOrganization)
{
    Organization preOrganization = em.find(Organization.class, newOrganization.getOrganizationId);
    preOrganization.setOrganizationId(newOrganization.getOrganizationId);
    preOrganization.setOrganizationName(newOrganization.getOrganizationName);
    em.merge(preOrganization);
    em.flush();
}

here are other methods:

@TransactionAttribute(TransactionAttributeType.REQUIRED)
private void updateOrgAndRelatedShares(Organization org, List<Relatedparty> shareList)    throws ControlException
{   

    for(Iterator<Organdrelatedparty> it = org.getOrgandrelatedpartyList().iterator(); it.hasNext();)
    {   
        Organdrelatedparty op = it.next();
        it.remove();
        op.setOrganization(null);
        op.setRelatedparty(null);
        deleteOrgRelated(op);
    }
    org.getOrgandrelatedpartyList().clear();


    for(Relatedparty relatedParty: shareList){
        int parOrgRelationTypeId = relatedParty.getIsPerson() ? 1:2;
        createOrgAndRelatedParty(org, relatedParty, parOrgRelationTypeId);
    }

}
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void deleteOrgRelated(Organdrelatedparty org) throws ControlException{
    try{
    org = em.find(Organdrelatedparty.class, org.getOrgandrelatedpartyPK());
    em.remove(org);
    em.flush();
    }
    catch(Exception ex){
        Logger.getLogger(RelatedpartyController.class.getName()).log(Level.SEVERE, null, ex);
        throw new ControlException("Couln't delete org relation", ex);
    }
}
@TransactionAttribute(TransactionAttributeType.REQUIRED)
private void createOrgAndRelatedParty(Organization org, Relatedparty relatedParty,  int parOrgRelationTypeId) throws ControlException{
    if(findRelatedPartyByRegNum(relatedParty.getRegisterNumber()) == null || relatedParty.getRelatedPartyid() == null){
       createRelated(relatedParty);
    }else{
       relatedParty = updateRelatedParty(relatedParty);
    }
        Organdrelatedparty preOrp = new  Organdrelatedparty(relatedParty.getRelatedPartyid(), 
        preOrp.setOrganization(org);
        preOrp.setRelatedparty(relatedParty);
        preOrp.setOrgRelationid(prepareOrgandRelatedPartyType(parOrgRelationTypeId));
        org.getOrgandrelatedpartyList().add(preOrp);
    }

And my question is when I merge organization entity with new List organdrelatedpartyList it throws exception like this:

SEVERE: java.lang.IllegalArgumentException: Cannot merge an entity that has been removed: mn.bsoft.crasmonclient.model.Organdrelatedparty[ organdrelatedpartyPK=mn.bsoft.crasmonclient.model.OrgandrelatedpartyPK[ relatedPartyid=71, organizationId=19 ] ]

I found out that eclipseLink does persist operation first then remove operations. So I think that it tries to insert organdrelatedparty entity that has same composite id with entity which was not deleted previously from database. I flushes every time I remove old organdrelatedparties. But it doesn't help. What is the solution? Any idea guys.

I'm using jpa 2.0; eclipseLink as provider and glassfish 3.1.2

Odgiiv
  • 683
  • 1
  • 11
  • 32

1 Answers1

0

You seem to be making these a lot more complicated than they need to be.

Why don't you just remove the Organdrelatedparty that have been removed, instead of deleting all of them, then reincarnating some of them? Reincarnating objects, especially in the same transaction is normally a bad idea.

The error that is occurring is on merge() according to the code you included you are only call merge in updateOrgAndRelatedShares(), so I don't see how this object is removed at this point? Or is your code different than you show, please include the exception stack.

You updateOrganization() method is bad, it updates the objects Id, which you should never do. Also it calls merge for no reason, it already changed the object.

Also I would normally recommend using an IdClass instead of an EmbeddedId, and recommend using TABLE or SEQUENCE id generation instead if IDENTITY.

James
  • 17,965
  • 11
  • 91
  • 146