Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[eclipselink-users] Very strange "primary key [null]" validation exception

I've posted this problem on forum too.
http://www.eclipse.org/forums/index.php/m/893590/#msg_893590

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:

[code]
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)
[/code]

My entities model looks like these:
[code]
@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;

  ...
}
[/code]

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):

[code]
...
            Object registeredObject = unitOfWork.internalRegisterObject(clone, descriptor);//should use cloneAndRegisterNewObject to avoid the exist check
...
[/code]

Addition of second copy of PhoneContactInfo to cloneMapping collection happening exactly during existence check.

My example model is little bit oversiplified :) but basically I do like that:
[code]
/* ----------- server code (java): -------- */
... this method invoked by client to get person data
public Person getPersonById(Integer id){
   Person person = entityManager.findById(id, Person.class);
   // here come some operations to force loading lazy collections from db
   return person; // graniteds serialization
}
... this method is invoked to save graph
public void savePerson(Person person1) {
  /* person1 is came from client request deserialization - i.e. it is detached from persistence layer
     all object instances in passed graph contains all added by weawing fields and their values */ 
  entityManager.merge(person1); // exception happens not here, but on transaction commit which will call "calculateChanges", but PhoneContactInfo added twice here
}

/* ------- client code (actionscript): --------- */

var person1:Person = personService.getPersonById(1); // simplified, because service call is really async
var person2:Person = person1.relativeLinks[0].relative; // simplified, just assume we have only one relative in collection and it will be our "person2";

/* adding contact */
var userContact:UserContact = new UserContact();
userContact.personOwner = person1;
userContact.contactable= person2.personContactableProfile;
person1.contacts.add(userContact); // assume that collection is not null which is not true for real life

/* adding phone */
var phone:PhoneContactInfo = new PhoneContactInfo();
phone.value = '123123123'; // all contactinfos has value, forgot to add superclass definition in previous example
person2.personContactableProfile.phoneInfo = phone;

/* at this moment object graph that have person1 as "root" contains new userContact
   person2 (which is also part of this object graph) have brand new phone number
   time to submit all that stuff to server
*/
personService.savePerson(person1);

[/code]


complete stack trace:

WARNING: DTX5014: Caught exception in beforeCompletion() callback:
Local Exception Stack:
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@6bd3162f], 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)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.calculateChanges(UnitOfWorkImpl.java:643)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithChangeSet(UnitOfWorkImpl.java:1490)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.issueSQLbeforeCompletion(UnitOfWorkImpl.java:3143)
    at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.issueSQLbeforeCompletion(RepeatableWriteUnitOfWork.java:346)
    at org.eclipse.persistence.transaction.AbstractSynchronizationListener.beforeCompletion(AbstractSynchronizationListener.java:157)
    at org.eclipse.persistence.transaction.JTASynchronizationListener.beforeCompletion(JTASynchronizationListener.java:68)
    at com.sun.enterprise.transaction.JavaEETransactionImpl.commit(JavaEETransactionImpl.java:435)
    at com.sun.enterprise.transaction.JavaEETransactionManagerSimplified.commit(JavaEETransactionManagerSimplified.java:852)
    at com.sun.ejb.containers.BaseContainer.completeNewTx(BaseContainer.java:5114)
    at com.sun.ejb.containers.BaseContainer.postInvokeTx(BaseContainer.java:4879)
    at com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:2039)
    at com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:1990)
    at com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHandler.java:222)
    at com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate.invoke(EJBLocalObjectInvocationHandlerDelegate.java:88)
    at $Proxy256.savePerson(Unknown Source)
    at com.hospitality.hp.baseclient.profile.__EJB31_Generated__ProfileServiceBean__Intf____Bean__.savePerson(Unknown Source)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.granite.messaging.service.ServiceInvocationContext.invoke(ServiceInvocationContext.java:71)
    at org.granite.messaging.service.security.AbstractSecurityService.endAuthorization(AbstractSecurityService.java:67)
    at com.hospitality.hp.osloproject.security.graniteDS.SpringSecurity3ServiceHP.authorize(SpringSecurity3ServiceHP.java:283)
    at org.granite.messaging.service.ServiceInvoker.invoke(ServiceInvoker.java:214)
    at org.granite.messaging.amf.process.AMF3MessageProcessor.processRemotingMessage(AMF3MessageProcessor.java:136)
    at org.granite.messaging.amf.process.AMF3MessageProcessor.process(AMF3MessageProcessor.java:59)
    at org.granite.messaging.amf.process.AMF0MessageProcessor.process(AMF0MessageProcessor.java:78)
    at org.granite.messaging.webapp.AMFMessageServlet.doPost(AMFMessageServlet.java:59)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:754)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:847)
    at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1539)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:343)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:217)
    at org.granite.messaging.webapp.AMFMessageFilter.doFilter(AMFMessageFilter.java:117)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:217)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:279)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
    at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:655)
    at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:595)
    at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:98)
    at com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:91)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:162)
    at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:330)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:231)
    at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:174)
    at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:828)
    at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:725)
    at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1019)
    at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:225)
    at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137)
    at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104)
    at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90)
    at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79)
    at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54)
    at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59)
    at com.sun.grizzly.ContextTask.run(ContextTask.java:71)
    at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532)
    at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513)
    at java.lang.Thread.run(Thread.java:722)


Can someone tell me how I can fix this behaviour?


Back to the top