2

I am puzzled by a JPA behavior which I did not expect in that way (using Eclipselink).

I run on Wildfly 10 (JDK-8) a stateless session EJB (3.2). My method call is - per default - encapsulated in a transaction. Now my business method, when reading and updating a entity bean, did not recognize updates - especially the version number of the entity. So my call results in a

org.eclipse.persistence.exceptions.OptimisticLockException

My code looks simplified as this:

public ItemCollection process(MyData workitem) {

    ....
    // load document from jpa
    persistedDocument = manager.find(Document.class, id);
    logger.info("@version=" + persistedDocument.getVersion()); 
    // prints e.g. 3

    // change some data
    ....
    manager.flush();
    logger.info("@version=" + persistedDocument.getVersion()); 
    // prints e.g. 4

    ....
    // load document from jpa once again
    persistedDocument = manager.find(Document.class, id);

    logger.info("@version=" + persistedDocument.getVersion()); 
    // prints e.g. 3  (!!)

    // change some data
    ....
    manager.flush();
    // Throws OptimisticLockException !!
    // ...Document@1fbf7c8e] cannot be updated because it has changed or been deleted since it was last read

    ...
}

If I put the code (which changes the data and flush the entity bean) in a method annotated with

@TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW)

everything works as expected.

But why is the second call of the find() method in my code not reading the new version number? I would expect version 4 after the flush() and find() call.

Ralph
  • 4,500
  • 9
  • 48
  • 87
  • second call in the same transaction? – Neil Stockton Oct 29 '16 at 08:22
  • yes this all runs in one and the same stateless session EJB method – Ralph Oct 29 '16 at 08:23
  • I think I've had in another project, a similar code which, however, has many OneToMany relationships There seems to the behavior be different from now. – Ralph Oct 29 '16 at 08:26
  • When I run similar things with the JPA provider I use (DataNucleus) each flush will update the VERSION and the modified field in the datastore, consequently calls to getVersion() return the new version. The other thing to say is that em.find() in the same transaction on the same object should return the identical object so those calls are pointless – Neil Stockton Oct 29 '16 at 08:33
  • My code is more complex as shown in the example. The em.find() method is called from different logical modules using the same session bean. You are right that in other cases the repeated find() calls would be pointless. – Ralph Oct 29 '16 at 08:49
  • so look at what happens in the log when you do the subsequent "find" call, whether the VERSION has been set in the datastore before that, whether it is read in in an SQL call (or whether the object is pulled from L1/L2 cache) – Neil Stockton Oct 29 '16 at 08:53
  • Flush sends queued changes to the database, but my experience shows it does not necessarily enqueue changes in managed objects before doing so in Hibernate. By your description it seems to be the same in EclipseLink, which is the reference JPA implementation. – coladict Oct 30 '16 at 11:10

1 Answers1

2

After all it looks like calling

manager.clear();

solves the problem. I thought that detaching the object should do the same, but in my case only calling clear() did fix the problem.

More findings:

After all it seems not be a good idea to call the methods detach() and flush() form a service layer. I did this, because I wanted to get the new version id of my entity before I left my business method to return this id to the client. I changed my strategy in this case and I removed all the 'bad stuff' with detaching and flushing my entity beans. The code becomes more clear and after all the code complexity was reduced dramatically.

And of course the entityManager now behaves correctly. If I query again the same entity bean several times in one transaction, the entityManager returns the correct updated version.

So the answer to my own question is: Leave the methods flush() and clear(), as long as there is no really good reason for it to use them.

Community
  • 1
  • 1
Ralph
  • 4,500
  • 9
  • 48
  • 87