JPA @Version attribute causes OptimistLockExceptions with consecutive updates

129 views Asked by At

I don't quite understand the following behaviour:

We take an Entity where we add a @Version field to use hibernate's Optimistic locking

@Entity
public class MyEntity {
    @Id
    @GeneratedValue
    public Long id;

    @Version
    public Long rowVersion;

    public String field;
}

We then create a @QuarkusTest and test if we can consecutively update and fetch the entity:

@QuarkusTest
public class RowVersionTest {

    @Inject
    EntityManager em;

    @Test
    public void testRowVersion(){
        MyEntity entity = createEntity();
        Long id = entity.id;

        for (int i=0; i < 10; i++){
            MyEntity entityById = getById(id);
            entityById.field += i;
            System.out.println("Before: " +entityById.field + " - RowVersion: " + entityById.rowVersion);
            update(entityById);
            System.out.println("After: " +entityById.field + " - RowVersion: " + entityById.rowVersion);
        }
    }

    @Transactional
    public MyEntity createEntity() {
        MyEntity newEntity = new MyEntity();
        newEntity.field = "Hello";
        em.persist(newEntity);
        return newEntity;
    }

    public MyEntity getById(Long id){
        TypedQuery<MyEntity> query = em.createQuery("Select e from MyEntity e where e.id = :id", MyEntity.class);
        query.setParameter("id", id);
        return query.getSingleResult();
    }

    @Transactional
    public void update(MyEntity entity){
        em.merge(entity);
    }
}

What I would expect to happen:

  1. We persist the entity and commit by setting the @Transactional boundary
  2. We fetch the entity from the database with the current rowversion
  3. We update (merge) the entity and change the field, thereby increase the rowversion in the database
  4. We repeat 2-3

What actually happens, is that we are only able to update the entity the first try. On the second try, we don't seem to get the latest rowVersion from the database

Before: Hello0 - RowVersion: 0
After: Hello0 - RowVersion: 0
Before: Hello01 - RowVersion: 0

jakarta.persistence.OptimisticLockException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [org.acme.MyEntity#1]

Why is this behaviour? Shouldn't we get the correct rowversion from the database every time, as we are running outside of Transaction boundary?

Full source: https://github.com/arnehehe/jpa-question/tree/main/code-with-quarkus

0

There are 0 answers