没有EntityManager绑定到此线程JPA

时间:2013-11-12 14:27:18

标签: java hibernate jpa playframework playframework-2.0

我有一个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构建我的网络应用程序,所有这一切都很有效,我对它的工作方式非常满意。

今天我将play框架更新为更新的版本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. 为什么在没有使用早期版本的play framework的交易进行人工干预之前就已经开始工作了
  2. 是否有更好的方法可以更优雅地实现同样的目标(我不确定这是否是正确的词)?
  3. 更新

    这是我的控制器方法:

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

    那么事务应该传播到所有方法吗?但为什么不听听?

    我估计是这样的:

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

3 个答案:

答案 0 :(得分:2)

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

  
      
  1. 为什么在没有手动干预交易之前就可以工作了   早期版本的播放框架
  2.   
  3. 有没有更好的方法   更优雅地完成同样的事情(我不确定那是什么   对它说正确的话)?
  4.   

我们只需要等到这个问题得到解决,或者等待一些解释,在这个问题上使用来自play2开发者的JPA事务的线程。此时此问题已经公开。

答案 1 :(得分:1)

在我们看来,问题是JPA.withTransaction()(和@Transactional也使用了这个)块无法嵌套,因为.withTransaction()无条件地取消绑定em,如果它是一个内部.withTransaction(),则外部块将没有绑定的em。

所以这个测试在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绑定到这个线程时,才尝试/捕获并运行JPA.withTransaction()中的代码。例外。

答案 2 :(得分: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;
                }
            });