【问题标题】:No EntityManager bound to this thread JPA没有 EntityManager 绑定到这个线程 JPA
【发布时间】:2013-11-12 14:27:18
【问题描述】:

我有一个 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注解,那么内部发生的所有调用都应该在一个事务中?

【问题讨论】:

  • 你能发布你的控制器代码来调用你的更新方法吗?异步操作不一定在同一个线程上运行。这将解释您看到的错误消息。我相信您现在设置它的方式是实现交易保证的最佳方式。
  • 我猜问题是preUpdate 在不同的线程中运行,这听起来像是一个错误。您是否使用不同的回调(例如 prePersist)得到类似的错误?我意识到 prePersist 不适合您的用例。

标签: java hibernate jpa playframework playframework-2.0


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

    在我们看来,问题在于 JPA.withTransaction()(并且 @Transactional 也使用它)块不能嵌套,因为 .withTransaction() 无条件地解除绑定,如果它是一个内部 .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 bound to this thread”异常时才尝试/捕获并在 JPA.withTransaction() 中运行您的代码。

    【讨论】:

      【解决方案3】:
      // 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;
                      }
                  });
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-11-05
        • 1970-01-01
        • 1970-01-01
        • 2015-08-11
        • 2014-12-15
        • 2011-05-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多