JPA实体仅在方法完成后更新

时间:2011-10-25 13:47:57

标签: jpa asynchronous merge entity

我有一个Asynchronous注释方法,它通过方法参数接收实体。在这个方法中,我尝试设置一个变量三次:

@Inject EntityDao entityDao;

@Asynchronous
public Future<String> doSomething (MyEntity p_myEntity) {

    MyEntity myEntity = entityDao.merge(p_myEntity); // change from detached to attached
    // em.contains(myEntity) returns true

    myEntity.setName("Joe 1"); // newer set in database
    // A System.out.println(myEntity.getName()) does say "Joe 1"

    try {
        Thread.sleep(20*1000);
    } catch () ...etc

    myEntity.setName("Joe 2"); // newer set in database
    // A System.out.println(myEntity.getName()) does say "Joe 2"

    try {
        Thread.sleep(20*1000);
    } catch () ...etc

    myEntity.setName("Joe 3"); // only one set in database

    return new AsyncResult<>("done");
}

编辑:感谢PedroKowalski,我对这个问题有了更好的理解,我将重新制定。

在执行上述方法的过程中,我有两种检查myEntity.setName()是否实际更改的方法:

  • 在sleep()期间,我检查数据库是否更改了名称值
  • 在网页上显示MyEntity对象列表(带有名称),此列表每2秒更新一次。

上述两种方法都表明数据库中的“Joe 1”和“Joe 2”值较新。只有在doSomething()方法完成后,才会将最后一个名称集(Joe 3)放入数据库中。

所以我的问题是:为什么值“Joe 1”和“Joe 2”没有放入数据库中,只有最后一个值放在数据库中?

2 个答案:

答案 0 :(得分:5)

如果使用JTA事务,则事务边界从方法的开始扩展到结束。

因此,在事务T2中无法看到在活动事务T1中所做的更改。如果你考虑一下,这是非常合理的。 假设T2可以对T1改变但未提交的数据进行操作。在T1回滚时,对T1中的实体所做的每个更改都必须无效。你已经结束了T2对无效数据进行操作的情况。

这就是为什么你不会从T1以外的任何交易中看到'Joe 1'(此值仅在T1中更改)。当方法结束时(T1提交),你只能看到'Joe 2'。

EntityManager#flush()将数据与基础数据库同步,但不提交。有关更多详细信息,请参阅此主题:http://www.java.net/node/665442#comment-678155

在这种情况下,我可以看到三种解决方案:

  1. 乐观锁定可以帮助您避免两个事务T1和T2更改相同数据(同一实体)的情况。如果您没有锁定,则只有最后提交的事务更改将反映在数据库中(因此前一个事务所做的更改将丢失)。使用锁定,您将在上次提交的事务中获得异常,因此不会丢失任何数据。

  2. 悲观锁定可能会在修改时锁定数据。在这种情况下,在T1完成之前,您的事务T2将不会对数据进行操作。

  3. 最后 - 最简单的情况是(如果可能的话)只是将你的方法分成较小的块。

  4. HTH。

答案 1 :(得分:0)

  

所以我的问题是:为什么没有输入值“Joe 1”和“Joe 2”   数据库,只有最后一个值放在数据库中?

  • 在em.flush()期间将数据写入数据库。提交事务时,其他用户/持久性上下文可以看到数据。

  • 您不能通过em.flush()手动刷新数据库,因此在提交事务时会自动调用em.flush()。

  • 事务在方法结束时提交,因此当刷新发生时,意味着“Joe3”被写入DB并且也被提交。