0
votes

the JPA optimistic locking doesn't throw an OptimisticLockException/StaleStateException where i would expect it.

Here is my setup: i am using spring boot with spring data envers. So my repository are versioned, which should not influence the optimistic locking behaviour. In my entities the property version (Long) is annotated with @Version. My application consists of 3 layers:

  • persistence-layer
  • business-layer
  • transfer-layer

To map objects between the layers i use mapstruct.

When a request is received by the controller in the transfer-layer, the JSON-Payload is mapped to an business-layer object to process business rules to it. The version is always mapped through the whole lifecycle. When i reach the persistence-layer, i use the ID of the object to find the corresponding entity in my database. The signature of my save-method looks like this:

@Transactional
public Entity saveEntity(BOEntity boEntity){
 Entity e = entityRepository.findById(boEntity.getId());
 entityMapper.updateEntity(boEntity, e);
 entityRepository.save(e);
}

When the same entity is loaded by my clients, (e.g. two browser-tabs) each of them has the same version of the entity. Changes are made and saved in both clients. The version is contained in the boEntity object and mapped into the entity. Due to the findById call the entity is managed. The entitymanager will try to merge the entity and succeeds in both requests to do so.

The state of the entity of the first request is merged (with version 1). Hibernate calls the executeUpdate method and writes to the database. The version is increased to 2. Now the second request delivers the entity in the former state with version 1. The save-method is called and the entity is retrieved from the persistence-context. It has the version 2, which is overwritten by the boEntity object with version 1.

When the entityManager now merges the entity, no exception is thrown. My expectation is the second request to fail because of an old version. Isn't it possible to overwrite the version of the entity?

I already read a lot of blog entries, but couldn't find any hint to do the trick.

1
"Isn't it possible to overwrite the version of the entity?" It's not meant for the programmer to touch.Kayaman
How do you load the BOEntity boEntity that you pass into the save method?eg04lt3r
The BOEntity comes as payload from the request. First I have a transfer object (DTO), which is mapped into the BOEntity (including the version). The BOEntity is written into the real Entity by mapstruct. Again, the version is mapped into the previously loaded Entity (so the version of the Entity is overwritten). Let's assume my BOEntity is my DTO (to keep it simple). Isn't it the right way, to load the Entity from the database by findById and then update the Entity by setting the fields of it? A version 2 would be overwritten by an older version and Hibernate would throw the exception?user1560066
I get the Exception when i create an new Entity with a corresponding ID to the one I would like to update. The Entity then is in not in managed state. When I call the save method, the Exception is thrown.user1560066

1 Answers

0
votes

The default JPA optimistic locking mechanism only works when a managed object is flushed but was changed in the meantime. What you want has to be coded manually. Just add the logic to your saveEntity method:

@Transactional
public Entity saveEntity(BOEntity boEntity){
 Entity e = entityRepository.findById(boEntity.getId());
 if (boEntity.getVersion() != e.getVersion()) {
  throw new OptimisticLockException();
 }
 entityMapper.updateEntity(boEntity, e);
 entityRepository.save(e);
}