I'm using eclipselink v 2.3.2 (supplied with glassfish v.3.2)
I've got this stack while trying to merge object graph into database:
Exception [EclipseLink-7197] (Eclipse Persistence Services - 2.3.1.v20111018-r10243): org.eclipse.persistence.exceptions.ValidationException
Exception Description: Null or zero primary key encountered in unit of work clone [com.hospitality.hp.osloproject.entities.contacts.info.PhoneContactInfo@75c191a7], primary key [null]. Set descriptors IdValidation or the "eclipselink.id-validation" property.
at org.eclipse.persistence.exceptions.ValidationException.nullPrimaryKeyInUnitOfWorkClone(ValidationException.java:1439)
at org.eclipse.persistence.descriptors.changetracking.DeferredChangeDetectionPolicy.calculateChanges(DeferredChangeDetectionPolicy.java:107)
at org.eclipse.persistence.descriptors.changetracking.DeferredChangeDetectionPolicy.calculateChangesForExistingObject(DeferredChangeDetectionPolicy.java:54)
My entities model looks like these:
@Entity
public class PhoneContactInfo {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_PROFILE_ENTITES")
private Integer id;
...
}
@Entity
public class PersonContactableProfile extends ContactableProfile{
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_PROFILE_ENTITES")
private Integer id;
@OneToOne(cascade = {CascadeType.ALL}, orphanRemoval = true, optional = true)
private PhoneContactInfo phoneInfo;
@OneToOne(optional = false, cascade = CascadeType.ALL)
private Person person;
...
}
@Entity
public class UserContact {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_PROFILE_ENTITES")
private Integer id;
@OneToOne(optional = false, orphanRemoval = false, fetch = FetchType.EAGER)
private ContactableProfile contactable;
@ManyToOne(optional = false, fetch = FetchType.EAGER)
private Person personOwner;
...
}
@Entity
public class PersonToPersonLink {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_PROFILE_ENTITES")
private Integer id;
@ManyToOne(fetch = javax.persistence.FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH})
private Person relative;
@ManyToOne(fetch = javax.persistence.FetchType.LAZY)
private Person user;
...
}
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_PROFILE_ENTITES")
private Integer id;
@OneToMany(mappedBy = "personOwner", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private Set<UsersContact> contacts;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "user", orphanRemoval = true, fetch = FetchType.LAZY)
private Set<UserRelativeLink> relativeLinks;
@OneToOne(optional = false, cascade = {CascadeType.ALL}, mappedBy = "person", fetch = FetchType.LAZY)
private PersonContactableProfile personContactableProfile;
...
}
Every Person may have PersonContactable which is basically set of "ways to contact person" (e.g. phone number) - there is other types of contactable profiles in system, but this does not matter now. Every person can also may have collection of contacts and collection of relatives. UserContact entity, essentially, is record in one's contacts list. PersonToPersonLink keep link between two persons (e.g. relative). I have two Person instances, lets say Person1 and Person2. Person2 is already in Persons1's relstiveLinks collection (all data in database already). Then I take Person1, add new UserContact that points to Person2 PersonContactableProfile and also add new PhoneContactInfo to PersonContactableProfile of Person2 - I've got exception described above while saving Person1 graph (entityManager.merge(Person2)).
While investigating that issue i've found out why it happening - PhoneContactInfo instance added to cloneMapping collection of UnitOfWorkImpl twice!
First time during mapping traversal through contacts collection and second time during mapping traversal through relativeLinks collection. I do not understand if it's a bug? I also find out that line in MergeManager source code (trunk):
...
Object registeredObject = unitOfWork.internalRegisterObject(clone, descriptor);//should use cloneAndRegisterNewObject to avoid the exist check
...
Addition of second copy of PhoneContactInfo to cloneMapping collection happening exactly during existence check.
Can someone tell me how I can fix this behaviour?