合并获取不刷新的待更新版本?

Posted

技术标签:

【中文标题】合并获取不刷新的待更新版本?【英文标题】:Merge and get the to-be-updated version without flush? 【发布时间】:2011-03-11 08:41:43 【问题描述】:

是否可以更改版本化实体实例,并在不使用 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 之前,该实体应该具有 version-after-flush/commit呈现。然后 UI 可能会有最新的版本,并将通过这个版本进行下一次提交。


有没有什么方法可以在不刷新的情况下获得最新版本?

谢谢!


更新


根据我的经验,从下面的示例中可以看出,手动增加它可能会出现问题。在此示例中,我们有 2 次刷新。

第一个是将更改同步到 db 连接,以便来自同一连接的存储过程调用可以看到 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

【问题讨论】:

【参考方案1】:

我将再次推荐阅读有关 Hibernate 及其工作原理的更多信息,无论是其文档还是“Java Persistence with Hibernate”。

您在刷新操作中看到了这一点,因为这是数据库更新发生的地方。如果您只是省略刷新并提交事务,您将看到相同的行为。这意味着,每当您的工作单元完成时,Hibernate 会将更改刷新到数据库中。正是在这一步中,Hibernate 比较并更新了版本号。如果数据库版本是 17 并且您正在更新版本 16,Hibernate 将抛出一个异常,关于更新一个过时的对象。

也就是说,您可以通过将您当前在实例中的值增加 1 来“预测”下一个版本。但这永远不会是真实的,因为这只会在更新数据库记录之前有效地增加。因此,除非您查询数据库,否则您的线程将看不到任何并发更改。

编辑:

您会看到两个增量,因为您正在执行两次刷新。 “版本”是一种“乐观”锁定技术,意思是,您希望在任何工作单元期间只有一个线程会更新记录(在更广泛的意义上,将其视为“用户操作”,他在其中列出记录,选择一个并更新它)。它的目的主要是为了避免 Hibernate 更新一个陈旧的对象,其中两个用户选择相同的记录进行编辑,进行一些并发更改并更新它。其中一个编辑必须被拒绝,第一个进入数据库的编辑获胜。当您更新记录两次(通过调用 flush 两次)时,您会看到两个增量。但这实际上不是关于刷新,而是关于“在数据库中发布的更新”。如果您有两个事务,您会看到相同的行为。

也就是说,值得注意的是,这项技术应该由 Hibernate 管理。你不应该自己增加它。不为此类字段提供设置器通常是一个好习惯。

换句话说:您应该将“版本”视为只读值,并且您无法控制它。因此,在屏幕上为用户显示其值是安全的,但操纵它(或“确定”下一个版本)并不安全。最多,您可以做出“最佳猜测”,预测它将是 current_version+1。

【讨论】:

@partenon:我了解刷新和提交是如何工作的,以及版本是如何被它们增加的。但是我仍然会阅读您推荐的书 :-) 无论如何,我已经更新了我的原始帖子,以解决有关手动增量的一些额外问题。一如既往的感谢。 @partenon:所以,我终于可以得出结论,要在活动事务中创建一个反映具有最新版本的实体的 DTO,我确实必须执行 flush()。谢谢! 否 :-) 一旦事务完成或发生刷新,Hibernate 将更新对象。因此,如果您只是提交事务,您的 DTO 将被更新。 @partenon:啊,对不起,我没有得到你。所以,我更新了一个简单的服务或 dao 方法示例,它更新实体,并返回一个带有更新版本的 DTO。我真的不明白 DTO 在提交阶段是如何更新的? Hibernate 在更新数据库中的记录之前更新它。 Hibernate 发出一个选择,以查看对象是否过时(即:版本号是否与正在更新的相同),然后增加版本号并发出更新语句。【参考方案2】:

最新版本是当前版本 + 1。您可以在 entityToDto() 方法中自己增加版本。

但是在您的示例中,IMO 执行刷新不会导致这样的性能问题,因为 Hibernate 无论如何都会在提交时执行此操作。将有两次冲洗,但第二次将不再有任何冲洗。

【讨论】:

Hello2,我已经更新了我的原始帖子,以解决有关手动增量的一些额外问题。

以上是关于合并获取不刷新的待更新版本?的主要内容,如果未能解决你的问题,请参考以下文章

git上master的更新分支,但缺少新版本

不只是支持Windows, PyTorch 0.4新版本变动详解与升级指南

检查新版本(更新检查)

如何使用 git CLI 获取最新版本

Android studio 4.0 新更新版本说 Kotlin 与这个新版本不兼容

Packagist 没有从 GitHub 获取我的新版本