是否可以在不使用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)
}
实际上我有更多类似问题的课程 - 所以我正在寻找一些优雅的非侵入式解决方案。
在我看来,这是很常见的情况。但我找不到任何有关如何实现此类功能的信息。
答案 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更新更好,因为: