是否可以更改版本化实体实例,并在不使用flush的情况下获取待增加版本?因为从我读到的内容来看,即时冲洗并不是一种好的做法,因为它对性能甚至数据损坏都有不良影响?我不确定:D
这是一个简单的代码,输出也是注释:
/*
Hibernate: select receivingg0_.id as id9_14_, receivingg0_.creationDate as creation2_9_14_, ... too long
the version before modification : 16
the version after modification : 16
after merge the modification, the version is : 16
Hibernate: update ReceivingGood set creationDate=?, modificationDate=?, usercreate_id=?, usermodify_id=?, ... too long
after flushing the modification, the version is finally : 17
*/
public void modifyHeaderAndGetUpdatedVersion() {
String id = "3b373f6a-9cd1-4c9c-9d46-240de37f6b0f";
ReceivingGood receivingGood = em.find(ReceivingGood.class, id);
System.out.println("the version before modification : " + receivingGood.getVersion());
receivingGood.setTransactionNumber("NUM001xyz");
System.out.println("the version after modification : " + receivingGood.getVersion());
receivingGood = em.merge(receivingGood);
System.out.println("after merge the modification, the version is : " + receivingGood.getVersion());
em.flush();
System.out.println("after flushing the modification, the version is finally : " + receivingGood.getVersion());
}
在我的测试中,版本在刷新后增加了。从合并操作返回的实例没有增加的版本。
但是在我的情况下,我想以DTO的形式将实体返回到我的webui,并且实体应该在将其转换为DTO并将其返回到UI之前具有版本后刷新/提交渲染。用户界面可能有最新版本,并会在下次提交时通过此版本。
有没有办法可以在没有冲洗的情况下获得最新版本?
谢谢!
更新
根据我的经验,手动增加此功能可能会出现问题,如下面的示例所示。在这个例子中,我们有2次刷新。
第一个是将更改同步到数据库连接,以便来自同一连接的存储过程调用可以看到从entityManager进行的更改。
调用第二次刷新以获得最终版本。我们可以看到增加两次。因此,在没有刷新的情况下从手动增量获取版本在这种情况下无法工作,因为我们必须真正计算正在进行的刷新次数。
/*
Hibernate: select receivingg0_.id as id9_14_, receivingg0_.creationDate as creation2_9_14_, .. too long
the version before modification : 18
the version after modification : 18
after merge the modification, the version is : 18
now flushing the modification, so that the stored procedure call from the same connection can see the changes
Hibernate: update ReceivingGood set creationDate=?, modificationDate=?, usercreate_id=?, .. too long
after flushing the modification, the version is : 19
Hibernate: update ReceivingGood set creationDate=?, modificationDate=?, usercreate_id=?, .. too long
after the second flush, the version got increased again into : 20
*/
public void modifyHeaderAndGetUpdatedVersionWith2Flushes() {
String id = "3b373f6a-9cd1-4c9c-9d46-240de37f6b0f";
ReceivingGood receivingGood = em.find(ReceivingGood.class, id);
System.out.println("the version before modification : " + receivingGood.getVersion());
//auditEntity(receivingGood, getUser("3978fee3-9690-4377-84bd-9fb05928a6fc"));
receivingGood.setTransactionNumber("NUM001xyz");
System.out.println("the version after modification : " + receivingGood.getVersion());
receivingGood = em.merge(receivingGood);
System.out.println("after merge the modification, the version is : " + receivingGood.getVersion());
System.out.println("now flushing the modification, so that the stored procedure call from the same connection can see the changes");
em.flush();
System.out.println("after flushing the modification, the version is : " + receivingGood.getVersion());
receivingGood.setTransactionNumber("NUM001abc");
em.flush();
System.out.println("after the second flush, the version got increased again into : " + receivingGood.getVersion());
}
这是否意味着我真的必须依赖最后的同花顺才能获得修改过的实体的最新版本?
更新2
这是一个服务方法的简单示例,它将更新ReceivingGood实体,并应返回具有最新版本的DTO。
public ReceivingGoodDTO update(ReceivingGood entity) {
// merge it
entity = entityManager.merge(entity);
// the version is not incremented yet, so do the flush to increment the version
entityManager.flush(); // if i dont do this, the dto below will get the unincremented one
// use a mapper, maybe like dozer, to copy the properties from the entity to the dto object, including the newest version of that entity
ReceivingGoodDTO dto = mapper.map(entity, dto);
return dto;
}
以下是使用该方法的示例:
@Transactional
public ReceivingGoodDTO doSomethingInTheServiceAndReturnDTO() {
// do xxx ..
// do yyy ..
dto = update(entity);
return dto; // and the transaction commits here, but dto's version isnt increased because it's not a managed entity, just a plain POJO
}
答案 0 :(得分:5)
我将再次建议阅读有关Hibernate及其工作原理的更多信息,无论是通过其文档还是“Java Persistence with Hibernate”。
您在刷新操作中看到了这一点,因为这是数据库更新发生的地方。如果您只是省略flush并提交事务,您将看到相同的行为。这意味着,只要您的工作单元完成,Hibernate就会刷新对数据库的更改。正是在这一步中,Hibernate比较并更新了版本号。如果数据库版本是17并且您正在更新版本16,那么Hibernate将抛出一个关于更新陈旧对象的异常。
也就是说,您可以通过在实例中当前的值上增加1来“预测”下一个版本的内容。但这永远不会是真实的,因为这只会在更新数据库记录之前有效地增加。因此,除非您查询数据库,否则任何并发更改都不会对您的线程可见。
编辑:
您正在看两个增量,因为您正在进行两次刷新。 “版本”是一种“乐观”锁定的技术,这意味着,您希望在任何工作单元期间只有一个线程会更新记录(在更广泛的意义上,将其视为“用户操作”,他列出了记录,选择一个并更新它)。它的目的主要是避免Hibernate更新过时的对象,其中两个用户选择相同的记录进行编辑,进行一些并发更改并对其进行更新。其中一个编辑将不得不被拒绝,第一个编辑数据库获胜。当你更新记录两次时(通过调用flush两次),你会看到两个增量。但它真的不是关于刷新,而是关于“数据库中发布的更新”。如果您有两个交易,您会看到相同的行为。
那就是说,值得注意的是这种技术应该由Hibernate管理。你不应该自己增加它。不为这种领域提供制定者通常是一种好习惯。
换句话说:您应该将“版本”视为只读值,并且您无法控制它。因此,在屏幕上为用户显示其值是安全的,但操纵它(或“确定”下一个版本)是不安全的。最多,您可以做出“最佳猜测”,预测它将是current_version + 1。
答案 1 :(得分:0)
最新版本是当前版本+1。您可以在entityToDto()方法中自行增加版本。
但是在你的例子中,执行刷新不会导致这样的性能问题,IMO,因为Hibernate无论如何都会在提交时执行它。将有两次冲洗,但第二次冲洗将不再有任何冲洗。