What was bothering me is that the exceptions provided by JPA (Hibernate) and Spring do not actually return the current version of the failed object. So if a User needs to decide what to do, he obviously needs to see the updated, most current version. Just retarded an error to his call seems retarded to me. I mean you are already at database level in a transaction so getting the new current value directly has no cost...
I created a new Exception that holds a reference to the newest version of the entity that failed to update:
public class EntityVersionConflictException {
@Getter
private final Object currentVersion;
public EntityVersionConflictException(
ObjectOptimisticLockingFailureException lockEx,
Object currentVersion){
super(lockEx);
this.currentVersion = currentVersion;
}
public Object getConflictingVersion() {
return ((OptimisticLockException)getCause().getCause()).getEntity();
}
public Class getEntityClass() {
return getCause().getPersistentClass();
}
@Override
public ObjectOptimisticLockingFailureException getCause(){
return (ObjectOptimisticLockingFailureException)super.getCause();
}
}
and the according Service method
try {
return getRepository().save(entity);
} catch (ObjectOptimisticLockingFailureException lockEx) {
// should only happen when updating existing entity (eg. merging)
// and because entites do not use CascadeType.MERGE
// the entity causing the issue will always be the of class
// entity.getClass()
// NOTE: for some reason lockEx.getPersistentClass() returns null!!!
// hence comparing by class name...
if (lockEx.getPersistentClassName().equals(entityClass.getName())) {
T currentVersion = getById(entity.getId());
throw new EntityVersionConflictException(lockEx, currentVersion);
} else {
throw lockEx;
}
}
Note the comments. In case of CascadeType.MERGE this will not work like this, the logic would have to be much more complex. I have 1 service per entity type so that service would have to hold reference to all other services and so forth.