0

I am working on a project and due to certain issue I am changing pessimistic locking to optimistic locking. While doing so i am getting an error while updating the entity in the code as i tried it as a standalone application so there is no chance that two threads are updating it simultaneously. I checked the value of version in the different part of the code. Its showing it as 0. While calling flush or commit its giving an excepion that it is updated to 1.

Note: I already added @version int version_id field in entity for optimisticlocking.

The code is as below:

            WorkItem tmp_workItem=entityManager.find(WorkItem.class , workItem.getEntityKey());

        logger.info("Before merge"+ tmp_workItem.getVersion());

        entityManager.merge(workItem);
        tmp_workItem=entityManager.find(WorkItem.class , workItem.getEntityKey());
        logger.info("After merge"+tmp_workItem.getVersion()+" "+workItem.getVersion());
        //logger.info(entityManager.getLockMode(WorkItem.class).toString());
        entityManager.flush();
        logger.info("After flush"+tmp_workItem.getVersion());
        response = true;

This code is throwing an exception :

getVersion : 1
] cannot be updated because it has changed or been deleted since it was last read. 
Class> com.csg.ib.cobra.domain.WorkItem Primary Key> [9553]
    at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.commitToDatabase(RepeatableWriteUnitOfWork.java:549)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithPreBuiltChangeSet(UnitOfWorkImpl.java:1559

The Values of version in logger are :

before merge : 0 0 after merge : 0 0 before flush: 0 0

Then how it can get incresed by 1 while calling entityManger.flush()

)

AjayLohani
  • 872
  • 1
  • 6
  • 26

1 Answers1

0

In general, in contrast with the id field, which is updated on persist operation, version field is updated when data is actually flushed to the database, either using commit or flush. This is the reason why version field value is 0 until entityManager.flush is called. You could alternatively be using transaction.commit if using resource local transaction.

Regarding the exception that you are getting in the application code, it would be useful to have a more complete code example, maybe in the form of a JUnit test. I have created the following simple tests to demonstrate correct behavior, hope it will be helpful:

@Test
public void testOptimisticLocking() {
    OptimisticEntity entity = new OptimisticEntity();
    entity.setName("Some name");

    EntityTransaction transaction = entityManager.getTransaction();
    transaction.begin();
    try {
        entityManager.persist(entity);
        transaction.commit();
    } catch (Exception x) {
        transaction.rollback();
        Assert.fail("Failed to commit: " + x.getMessage());
    }

    int id = entity.getId();
    int version = entity.getVersion();
    Assert.assertTrue(id > 0);
    Assert.assertTrue(version > 0);

    entity = entityManager.find(OptimisticEntity.class, id);
    Assert.assertNotNull("Entity could not be retrieved", entity);
    Assert.assertEquals("Entity version retrieved not expected", version, entity.getVersion());

    entity.setName("Another name");
    transaction.begin();
    try {
        entityManager.merge(entity);
        transaction.commit();
    } catch (Exception x) {
        transaction.rollback();
        Assert.fail("Failed to merge: " + x.getMessage());
    }        
    Assert.assertEquals("Entity version not incremented after merge", version+1, entity.getVersion());
}

@Test
public void testOptimisticLockingOneTransaction() {
    OptimisticEntity entity = new OptimisticEntity();
    entity.setName("Some name");

    EntityTransaction transaction = entityManager.getTransaction();
    transaction.begin();
    try {
        entityManager.persist(entity);

        int id = entity.getId();
        int version = entity.getVersion();
        Assert.assertTrue(id > 0);
        Assert.assertEquals(0, version);

        OptimisticEntity retrievedEntity = entityManager.find(OptimisticEntity.class, id);
        Assert.assertNotNull("Entity could not be retrieved", retrievedEntity);
        Assert.assertEquals("Entity version retrieved not expected", 0, retrievedEntity.getVersion());

        entity.setName("Another name");
        entityManager.merge(entity);
        Assert.assertEquals("Entity version changed after merge", 0, entity.getVersion());

        retrievedEntity = entityManager.find(OptimisticEntity.class, id);
        Assert.assertNotNull("Entity could not be retrieved", retrievedEntity);
        Assert.assertEquals("Entity version retrieved not expected", 0, retrievedEntity.getVersion());

        entityManager.flush();
        Assert.assertEquals("Entity version not incremented after flush", 1, entity.getVersion());
        transaction.commit();
    } catch (Exception x) {
        transaction.rollback();
        Assert.fail("An error occurred: " + x.getMessage());
    }
}
  • Thanks for your valuable reply. That's true. But there is only one thread ececuting this entity at that time so how it can get updated and there is a mismatch of values. If just before the flush it is 0 then how it can be 1 at flush function call. – AjayLohani Dec 17 '14 at 04:33
  • I have been trying to reproduce this issue but have been unsuccessful so far. Could you share some unit test demonstrating this? Are you using latest version? What database are you using? – Petros Splinakis Dec 19 '14 at 14:22