Is it possible to disable the increment of the sleep version for a specific update?

Is it possible that the update object in the database without changing the version of the object using sleep mode?

Using my web application, users can create or update objects. And where is another asynchronous process that "processes" these objects after any user operation. If the user opens the object for updating before the entity is "processed", but tries to save it after "processing", the user will receive an "OptimisticLockException", and all of his entered data will be lost. But I would like to overwrite the data updated in the asynchronous process with the data provided by the user.

code snipet to show why I need this behavior (JPA + Hibernate):

//user creates entity by filling form in web application Entity entity = new Entity (); entity.setValue("some value"); entity.setProcessed (false); em.persist(entity); em.flush(); em.clear(); //but after short period of time user changes his mind and again opens entity for update entity = em.find(Entity.class, entity.getId()); em.clear(); //em.clear just for test purposes //another application asynchronously updates entities List entities = em.createQuery( "select e from Entity e where e.processed = false") .getResultList(); for (Object o: entities){ Entity entityDb = (Entity)o; someTimeConsumingProcessingOfEntityFields(entityDb); //update lots of diferent entity fields entityDb.setProcessed(true); em.persist(entityDb); } em.flush(); //version of all processed entities are incremented. //Is it possible to prevent version increment? em.clear(); //user modifies entity in web application and again press "save" button em.merge(entity); //em.merge just for test purposes entity.setValue("some other value"); entity.setProcessed (false); em.persist(entityDb); em.flush(); //OptimisticLockException will occur. //Is it possible to prevent this exception from happening? //I would like to overwrite data updated in asynchronous process //with user provided data. 

And my essence:

 @Entity @Table(name = "enities") public class Entity implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Version private int versionNum; @Column private String value @Column private boolean processed; //โ€ฆ and so on (lots other properties) } 

In fact, I have a lot more classes with a similar problem - so I'm looking for a few elegant non - intrusive solutions.

It seems to me that this is a very common scenario. But I could not find any information on how to achieve such functionality.

+5
source share
3 answers

Optimistic sleep mode locking can be bypassed with a sleep mode session ( http://docs.jboss.org/hibernate/orm/5.0/javadocs/org/hibernate/Session.html ). Use the replication method (...).

Example code that does not increase version:

 //Detaching to prevent hibernate to spot dirty fields. //Otherwise if entity exists in hibernate session replication will be skipped //and on flush entity will be updated with version increment. em.detach(entityDb); someTimeConsumingProcessingOfEntityFields(entityDb); //Telling hibernate to save without any version modifications. //Update hapends only if no newer version exists. //Executes additional query DB to get current version of entity. hibernateSession.replicate(entity, ReplicationMode.LATEST_VERSION); 

I think this solution is better than native SQL update because:

  • ORM mappings (anotations or * .hbm.xml) are used (no need to duplicate Java objects โ†” Display DB tables in your own queries);
  • no need to manually flash (performance);
  • db and hibernate caches are in the same state, there is no need to evict entities from the cache (performance);
  • and you have all the ORM functions, such as optimistic locking, etc.,
0
source

The version will always increase (if you are not using JPQL). The idea of โ€‹โ€‹the version is to track changes, and in the case of JPA, also enable optimistic locking so that applications can work simultaneously with the same objects.

But to get around this problem, you can read the latest version from the database and apply all the values โ€‹โ€‹from the version of the entity that the user was working on.

Something like that:

 void updateWithUserInput(Entity userVersion) { Entity dbVersion = entityManager.find(Entity.class, userVersion.getId); // apply changes... dbVersion.setX(userVersion.getX()); // ...and so on // Also: We're dealing with an attached entity so a merge call should not be necessary } 

I assume that you understand that this discards any changes made by the asynchronous call, thereby making it obsolete so that you also cannot do this and do not need to redefine any changes. :)

0
source

You can bypass the Hibernate optimizer locking mechanism by issuing your own request update for asynchronous processing:

 em .createNativeQuery("update entities set processed = :1 where id =:2") .setParameter(1, true) .setParameter(2, entityDb.getId()) .executeUpdate(); 
0
source

Source: https://habr.com/ru/post/1237006/


All Articles