12

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?

Uros K
  • 3,274
  • 4
  • 31
  • 45

1 Answers1

1

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

Saulius Next
  • 1,340
  • 10
  • 16
  • I am using `@Version` field, problem is that I am updating with a new object (constructed by deserializing), so I have to set `@Version` field manually. – Uros K May 11 '17 at 10:45
  • Why don't you just deserialize the object and take it's id, do an `em.find` and then you can update the proper entity. What you are currently doing is kinda hacky.. – Arnold Galovics May 12 '17 at 14:39
  • That would be a solution, but I don't like it, because I know which fields I don't want to copy and not the other way around. So if I add a field to entity in the future, I will have to remember that I have to add that field to another part of the codebase too. – Uros K May 13 '17 at 13:35