Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JPA Version Entity merge

Tags:

java

jpa

h2

I know that there are some questions about this subject already but I think that this one is different.

Let's say I have this class:

@Entity
public class foo{
  @Id
  @GeneratedValue
  private long id;

  @Version
  private long version;

  private String description;
  ...
}

They I create some objects and persist them to a DB using JPA add().

Later, I get all from the repository using JPA all(); From that list I select one object and change the description.

Then I want to update that object in the repository using JPA merge() (see code).

The problem here is that it works the first time I try to change the description (Version value is now 2). The second time, a OptimisticLockException is raised saying that that object was changed meanwhile.

I'm using H2 has DB in embedded mode.

MERGE CODE:

//First: persist is tried, if the object already exists, an exception is raised and then this code is executed
try {
     tx = em.getTransaction();
     tx.begin();
     entity = em.merge(entity);
     tx.commit();
   } catch (PersistenceException pex) {
              //Do stuff
            }

What can be wrong where?

Thank you.

EDIT (more code)

//Foo b is obtained by getting all objects from db using JPA all() and then one object is selected from that list

b.changeDescription("Something new!");

//Call update method (Merge code already posted)

like image 232
Cátia Azevedo Avatar asked Apr 01 '26 10:04

Cátia Azevedo


1 Answers

I would assume that you are changing elements in the list from different clients or different threads. This is what causes an OptimisticLockException.

One thread, in it's own EntityManager, reads the Foo object and gets a @Version at the time of the read.

// select and update AnyEntity
EntityManager em1 = emf.createEntityManager();
EntityTransaction tx1 = em1.getTransaction();
tx1.begin();
AnyEntity firstEntity = em1.createQuery("select a from AnyEntity a", AnyEntity.class).getSingleResult();
firstEntity.setName("name1");
em1.merge(firstEntity);

Another client reads and updates the Foo object at the same time, before the first client has committed its changes to the database:

// select and update AnyEntity from a different EntityManager from a different thread or client
EntityManager em2 = emf.createEntityManager();
EntityTransaction tx2 = em2.getTransaction();
tx2.begin();
AnyEntity secondEntity = em2.createQuery("select a from AnyEntity a", AnyEntity.class).getSingleResult();
secondEntity.setName("name2");
em2.merge(secondEntity);

Now the first client commits its changes to the database:

// commit first change while second change still pending
tx1.commit();
em1.close();

And the second client gets an OptimisticLockException when it updates its changes:

// OptimisticLockException thrown here means that a change happened while AnyEntity was still "checked out"
try {
    tx2.commit();
    em2.close();
} catch (RollbackException ex ) {
    Throwable cause = ex.getCause();
    if (cause != null && cause instanceof OptimisticLockException) {
        System.out.println("Someone already changed AnyEntity.");
    } else {
        throw ex;
    }
}

Reference: Java - JPA - @Version annotation

like image 97
K.Nicholas Avatar answered Apr 03 '26 15:04

K.Nicholas



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!