Is it possible to turn off hibernate version increment for particular update?

断了今生、忘了曾经 提交于 2021-02-04 17:15:46

问题


Is it possible update entity in database without modifying version of entity using hibernate?

Using my web application users can create or update entities. And where is another asynchronous process which “processes” these entities after any user operation. If user opens entity for update before entity is “processed”, but tries to save it after it is “processed”, user will get “OptimisticLockException” and all his entered data will be lost. But I would like to overwrite data updated in asynchronous process with user provided data.

code snipet to demonstrate why I need such behaviour (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 entity:

@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 reality I have much more classes with similar problem - so I am looking for some elegant non intrusive solution.

It seems to me this is quite usual scenario. But I could not find any information how to achieve such functionality.


回答1:


Hibernate Optimistic Locking can be bypassed using hibernate Session (http://docs.jboss.org/hibernate/orm/5.0/javadocs/org/hibernate/Session.html) replicate(...) method.

Example of code which does not increment 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 uses (no need to duplicate Java objects <-> DB Tables mapping in native queries);
  • no need to manually execute flush (performance);
  • db and hibernate caches are in same state, no need to evict entities form cache (perfomance);
  • and you still have all ORM provided features like optimistic locking, and so on... ;



回答2:


The version will always be incremented (unless you use JPQL). The idea of a version is to track changes and in case of JPA to also enable optimistic locking so applications can work concurrently with the same entities.

But to get around this issue you could read the latest version from the database and apply all values from the entity version the user has worked on.

Something like this:

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 do realize that this discards any changes performed by the async call, thus rendering it obsolete so you could also not do it and wouldn't need to override any changes. :)




回答3:


You can bypass Hibernate Optimistic Locking mechanism by issuing a native query update for asynchronous processing:

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


来源:https://stackoverflow.com/questions/33972564/is-it-possible-to-turn-off-hibernate-version-increment-for-particular-update

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!