JPA: OptimisticLockException and Cascading

前端 未结 2 2125
刺人心
刺人心 2021-02-10 23:03

In my current project I use Spring Data JPA with Hibernate but consider this as a more general question that should also cover \"plain\" JPA.

I\'m uncertain how I should

相关标签:
2条回答
  • 2021-02-10 23:29

    The whole point of optimistic locking is to be able to tell the end user: Hey, you tried to save this important piece of information, but someone else saved it behind your back, so you'd better refresh the information, decide if you still want to save it and potentially enter some new values, and then retry.

    Just like with SVN, if you try to commit a file and someone else committed a new version before, SVN forces you to update your working copy and resole potential conflicts.

    So I would do the same as what JPA does : it lets the caller decide what to do by throwing the exception. This exception should be handled in the presentation layer.

    0 讨论(0)
  • 2021-02-10 23:38

    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.

    0 讨论(0)
提交回复
热议问题