2011-03-11 28 views
7

是否可以更改版本化的实体实例,并在不使用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被渲染。然后用户界面可以有最新版本,并将通过这个版本的下一个提交。


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

谢谢!


UPDATE


以我的经验,人工增加这可能是问题的,因为可以从下面这个例子可以看出。在这个例子中,我们有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()); 
} 

这是否意味着我真的不得不依赖最后的flush来获取修改实体的最新版本?


更新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 
} 

回答

5

我会再次推荐阅读更多关于Hibernate的知识,以及它是如何工作的,无论是它的文档还是“Java Persistence with Hibernate”。

您在flush操作中看到了这一点,因为这是数据库更新发生的地方。如果你只是省略了刷新并提交交易,你会看到相同的行为。这意味着,只要您的工作单元完成,Hibernate就会将更改刷新到数据库。在这一步中,Hibernate会比较和更新版本号。如果数据库版本是17并且你正在更新版本16,Hibernate将抛出一个异常,关于更新一个陈旧的对象。也就是说,你可以通过在当前实例中增加1来预测下一个版本。但这永远不会是真实的,因为这只是在更新数据库记录之前才有效地增加。因此,除非您查询数据库,否则任何并发更改都不会对您的线程可见。

编辑:

你看到两个增量,因为你正在做的两次冲洗。 “版本”是一种“乐观”锁定的技术,意思是说,你希望只有一个线程会在任何工作单元中更新记录(从更广泛的意义上来说,认为是“用户操作”,他列出记录,选择一个并更新它)。它的目的主要是避免Hibernate更新陈旧的对象,其中两个用户选择相同的记录进行编辑,做一些并发的更改并更新它。其中一个编辑将不得不被拒绝,并且第一个到达数据库的数据会胜出。当你更新记录两次(通过调用两次刷新),你会看到两个增量。但它并不是关于刷新,而是关于“在数据库中发布的更新”。如果你有两个交易,你会看到相同的行为。

这就是说,值得注意的是这个技术应该由Hibernate来管理。你不应该自己增加它。不为这类领域提供制定者往往是一个好习惯。

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

+0

@partenon:我了解如何刷新和提交作品,以及版本如何增加他们。但是我仍然会阅读你推荐的这本书:-)无论如何,我已经更新了我原来的帖子,关于手动增量的其他一些问题。一如既往地感谢你。 – bertie 2011-03-11 09:29:31

+0

我刚刚更新了答案,以解决您的新问题:-) – jpkrohling 2011-03-11 09:40:41

+1

@partenon:所以,我终于可以得出结论,要创建一个DTO里面的活动交易,反映实体与最新版本,我真的必须做一个齐平()。谢谢 ! – bertie 2011-03-11 09:46:55

0

最新版本是当前一个+ 1,你可以增加自己的版本在entityToDto()方法。

但在你的例子中,执行flush操作不会导致这样的性能问题,IMO,因为Hibernate会在提交时执行它。会有两次冲洗,但第二次冲洗不会再有任何冲洗。

+0

Hello2,我已更新我的原始帖子,以了解有关手动增量的其他一些问题。 – bertie 2011-03-11 09:30:11