1

我有一个 POJO(在此示例中为 Myclass),我在我的应用程序中保留/更新/删除它。

我使用侦听器类检测对该对象的更改,然后在侦听器类中将更改保存到另一个表中。

Here is my class (dummy example) :

EntityListeners({MyListener.class})
class MyClass {
  String name;
  String surname;

  /*...getters...setter..etc */

  public void save() {
    JPA.em().persist(this);
    return this;
  }

  public void update() {
    JPA.em().merge(this);
  }
}

class MyListener {
   @preUpdate
   public void preUpdate() {
    /*...some logic to save the changes irrelevant*/
    someAuditObj.createWithData(.......);
    someAuditObj.save();
   }
}

我正在使用 构建我的网络应用程序play framework v2.1.3,所有这些都运行良好,我真的很高兴它的工作方式。

今天我将播放框架更新为更新版本v2.2.1。并且由于某种原因,当MyClass更改实例和侦听器获取更改并尝试保存更改时,事务失败,我在日志中找到了这个:

Caused by: java.lang.RuntimeException: No EntityManager bound to this thread

所以我花了一段时间才弄清楚由于某种原因事务没有传播到侦听器,然后我尝试了一些方法来修复它(侦听器类):

@preUpdate
       public void preUpdate() {
        /*...some logic to save the changes irrelevant*/
       JPA.withTransaction(new F.Callback0() {
             @Override
             public void invoke() throws Throwable {
                      someAuditObj.createWithData(.......);
                      someAuditObj.save();
       });
       }

所以这修复了它,它像以前一样工作,没有问题。

我的问题是:

  1. 为什么它以前没有手动干预早期版本的游戏框架的事务就可以工作
  2. 有没有更好的方法来更优雅地实现同样的事情(我不确定这是正确的词)?

更新

这是我的控制器方法:

@Transactional
    public Result updateName(Long id, String name){
        MyClass c = MyClass.findById(id);
        c.setName(name);
        c.update();

        return ok();
    }

所以事务应该传播到所有方法,对吗?但为什么不去倾听呢?

我的估计是这样的:

如果一个方法有@Transactional 注释,那么内部发生的所有调用都应该在一个事务中?

4

3 回答 3

2

看来你遇到了和我一样的问题。看看我的问题:https ://github.com/playframework/playframework/issues/2042 相同的 JPA 代码适用于 2.1.0 但不适用于 2.2.1 所以我认为这是一个错误。

  1. 为什么它以前没有手动干预早期版本的游戏框架的事务就可以工作
  2. 有没有更好的方法来更优雅地实现同样的事情(我不确定这是正确的词)?

我们只需要等到这个问题得到解决,或者等待 play2 开发人员在这个问题中关于使用 JPA 事务的线程的一些解释。此刻问题是开放的。

于 2013-11-21T13:24:13.753 回答
1

在我们看来,问题在于 JPA.withTransaction()(并且 @Transactional 也使用它)块不能嵌套,因为 .withTransaction() 无条件地取消绑定 em,如果它是内部 .withTransaction(),则外部块将是离开了没有绑定的时间。

所以这个测试在 c.save() 失败(在我们的例子中保存实体)

@Test
public void nestedJPACalls() {

    JPATestEntity a = new JPATestEntity();
    JPATestEntity b = new JPATestEntity();
    JPATestEntity c = new JPATestEntity();

    JPA.withTransaction(() -> {

        a.save();

        JPA.withTransaction(() -> {

            b.save();

        });

        c.save();
    });
}

.withTransaction() 方法应该检查 em 是否已经绑定,如果是,既不绑定也不解除绑定。我已将其添加到https://github.com/playframework/playframework/issues/2042的讨论中

我们现在正在研究一个干净的解决方案。一个临时但丑陋的解决方案是仅当您收到“No EntityManager bound to this thread”异常时才尝试/捕获并在 JPA.withTransaction() 中运行您的代码。

于 2014-07-23T13:21:38.657 回答
1
// Create receipt asynch
            Akka.future(new Callable() {
                public Object call() {
                    // Issue Receipt
                    JPA.withTransaction(new F.Callback0() {

                        @Override
                        public void invoke() throws Throwable {
                            // TODO Auto-generated method stub
                            issueReceipt(pgTxn); // test
                        }
                    });
                    return null;
                }
            });
于 2014-09-12T06:08:59.257 回答