Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OptimisticLockException when using JPA merge()

I have a rest application where one of the resources can be updated. Below are two methods responsible for achieving this task:

  1. updateWithRelatedEntities(String, Store): receives id and new object Store which was constructed by deserializing PUT request entity, sets the version (used for optimistic locking) on new object and calls update in a transaction.

    public Store updateWithRelatedEntities(String id, Store newStore) {
        Store existingStore = this.get(id);
    
        newStore.setVersion(existingStore.getVersion());
    
        em.getTransaction().begin();
        newStore = super.update(id, newStore);
        em.getTransaction().commit();
    
        return newStore;
    }
    
  2. update(String, T): a generic method for making an update. Checks that ids match and performs merge operation.

    public T update(String id, T newObj) {
       if (newObj == null) {
        throw new EmptyPayloadException(type.getSimpleName());
       }
    
    
       Type superclass = getClass().getGenericSuperclass();
    
       if (superclass instanceof Class) {
           superclass = ((Class) superclass).getGenericSuperclass();
       }
    
       Class<T> type = (Class<T>) (((ParameterizedType) superclass).getActualTypeArguments()[0]);
    
       T obj = em.find(type, id);
    
       if (!newObj.getId().equals(obj.getId())) {
           throw new IdMismatchException(id, newObj.getId());
       }
    
       return em.merge(newObj);
    }
    

The problem is that this call: T obj = em.find(type, id); triggers an update of store object in the database which means that we get OptimisticLockException when triggering merge (because versions are now different).

Why is this happening? What would be the correct way to achieve this?

I kind of don't want to copy properties from newStore to existingStore and use existingStore for merge - which would, I think, solve the optimistic lock problem.

This code is not running on an application server and I am not using JTA.

EDIT: If I detach existingStore before calling update, T obj = em.find(type, id); doesn't trigger an update of store object so this solves the problem. The question still remains though - why does it trigger it when entity is not detached?

like image 712
Uros K Avatar asked May 05 '17 08:05

Uros K


1 Answers

I can't see your entity from code which you added but I believe that you missing some key point with optimistic locking -> @Version annotation on version field. If you have this field on your entity then container should be able to do merge procedure without problems. Please take a look to Optimistic Locking also good article don't break optimistic locking

like image 65
Saulius Next Avatar answered Sep 23 '22 17:09

Saulius Next