是否可以关闭特定更新的休眠版本增量?

时间:2015-11-28 14:42:22

标签: hibernate jpa

是否可以在不使用hibernate修改实体版本的情况下在数据库中更新实体?

使用我的Web应用程序,用户可以创建或更新实体。另外一个异步过程在任何用户操作之后“处理”这些实体。如果用户在“处理”实体之前打开实体进行更新,但在“处理”实体后尝试保存,则用户将获得“OptimisticLockException”,并且所有输入的数据都将丢失。但我想用用户提供的数据覆盖异步过程中更新的数据。

代码snipet来演示我为什么需要这样的行为(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.

我的实体:

@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)
}

实际上我有更多类似问题的课程 - 所以我正在寻找一些优雅的非侵入式解决方案。

在我看来,这是很常见的情况。但我找不到任何有关如何实现此类功能的信息。

3 个答案:

答案 0 :(得分:0)

版本将始终递增(除非您使用JPQL)。版本的想法是跟踪更改,如果JPA也启用乐观锁定,那么应用程序可以与相同的实体同时工作。

但要解决此问题,您可以从数据库中读取最新版本,并应用用户所使用的实体版本中的所有值。

这样的事情:

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
}

我认为您确实意识到这会丢弃异步调用所执行的任何更改,从而使其过时,因此您也可能无法执行此操作,也无需覆盖任何更改。 :)

答案 1 :(得分:0)

您可以通过为异步处理发出本机查询更新来绕过Hibernate Optimistic Locking机制:

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

答案 2 :(得分:0)

可以使用hibernate Session http://docs.jboss.org/hibernate/orm/5.0/javadocs/org/hibernate/Session.html replicate(...)方法绕过Hibernate Optimistic Locking。

不增加版本的代码示例:

//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);

我认为这个解决方案比原生SQL更新更好,因为:

  • 使用ORM映射(anotations或* .hbm.xml)(无需在本机查询中复制Java对象< - > DB Tables映射);
  • 无需手动执行flush(性能);
  • db和hibernate缓存处于相同状态,无需从实体缓存中删除(性能);
  • 并且您仍然拥有所有ORM提供的功能,例如乐观锁定等等......;