I would like to use @Version
for optimistic concurrency control with JPA & Hibernate.
I know how it works in the typical scenario of two parallel transactions. I also know that if I have a CRUD with 1:1 mapping between the form and entity, I can just pass version
along as a hidden field and use this to prevent concurrent modifications by users.
What about more interesting cases, which use DTOs or change command patterns? Is it possible to use @Version
in this scenario as well, and how?
Let me give you an example.
@Entity
public class MyEntity {
@Id private int id;
@Version private int version;
private String someField;
private String someOtherField;
// ...
}
Now let's say two users open the GUI for this, make some modifications and save changes (not at the same time, so the transactions don't overlap).
If I pass the entire entity around, the second transaction will fail:
@Transactional
public void updateMyEntity(MyEntity newState) {
entityManager.merge(newState);
}
That's good, but I don't like the idea of passing entities everywhere and sometimes would use DTOs, change commands etc.
For simplicity change command is a map, eventually used in a call like this on some service:
@Transactional
public void updateMyEntity(int entityId, int version, Map<String, Object> changes) {
MyEntity instance = loadEntity(entityId);
for(String field : changes.keySey()) {
setWithReflection(instance, field, changes.get(field));
}
// version is unused - can I use it somehow?
}
Obviously, if two users open my GUI, both make a change, and execute it one after another, in this case both changes will be applied, and the last one will "win". I would like this scenario to detect concurrent modification as well (the second user should get an exception).
How can I achieve it?