【问题标题】:JPA entity updated only after method is done仅在方法完成后才更新 JPA 实体
【发布时间】:2011-12-14 23:19:55
【问题描述】:

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

@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”放入数据库中,而只将最后一个值放入数据库中?

【问题讨论】:

  • 这是同一事务的另一种方法吗?您是否尝试在合并发生后显式刷新 EM?
  • 不,不是同一笔交易。您的意思是在 EntityDao 中的 merge() 之后每次都刷新?
  • “我正在调用 myEntity.getName() 方法”是什么意思。这是您传递给“doSomething”方法的同一个实例,还是使用 EntityManager#findById(-) 查找的 MyEntity 的实例?
  • 不,在控制器类的其他地方,我通过调用 entityDao.findAll() 检索所有 myEntities,并将它们显示在网页上。这是每 5 秒完成一次。说清楚:异步方法和控制器没有任何关系
  • 您可以设置您想要的所有值并在方法结束时合并。您还可以在方法的开头合并并在它之后设置您想要的所有值(在从 merge() 实例返回的对象实例上)。不过没关系,您至少需要调用一次合并操作,因为您在此方法中对分离的实体进行操作。

标签: jpa asynchronous merge entity


【解决方案1】:

如果您使用 JTA 事务,那么事务边界会从方法的开始延伸到结束。

因此,在活动事务 T1 中所做的更改无法在事务 T2 中看到。仔细想想,还是挺有道理的。 假设 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. 悲观锁定可以在修改时锁定数据。在这种情况下,您的事务 T2 在 T1 完成之前不会对数据进行操作。

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

HTH。

【讨论】:

  • 我没有使用 JTA 事务,是吗?唯一与 JTA 相关的代码在我的 persistence.xml 中:。我不是自己开始/提交任何交易。 OP 中描述的方法没有被事务包围。
  • 您显示的相关部分是 JTA 使用声明。因为您使用 JTA,所以您没有任何事务的开始/提交代码。如果您使用 RESOURCE_LOCAL 事务类型,则需要通过显式使用 start/commit 来设置事务的边界。如前所述,默认情况下,JTA 事务从方法开始并在提交时结束。
  • 谢谢 :) 如果有人想添加信息,请继续。同时,我会将此作为答案。
  • 您是否有一个文档链接解释了 JTA 事务从方法开始并在提交时结束?
【解决方案2】:

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

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-24
    • 2014-02-10
    • 1970-01-01
    相关资源
    最近更新 更多