我有一个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();
});
}
所以这解决了它,并且它像以前一样没有问题。
我的问题是:
更新
这是我的控制器方法:
@Transactional
public Result updateName(Long id, String name){
MyClass c = MyClass.findById(id);
c.setName(name);
c.update();
return ok();
}
那么事务应该传播到所有方法吗?但为什么不听听?
我估计是这样的:
如果方法有@Transactional注释,那么内部发生的所有调用都应该在事务中?
答案 0 :(得分:2)
似乎你和我一样有同样的问题。看看我的问题:https://github.com/playframework/playframework/issues/2042 相同的JPA代码适用于2.1.0但不适用于2.2.1 所以我认为这是一个错误。
- 为什么在没有手动干预交易之前就可以工作了 早期版本的播放框架
- 有没有更好的方法 更优雅地完成同样的事情(我不确定那是什么 对它说正确的话)?
醇>
我们只需要等到这个问题得到解决,或者等待一些解释,在这个问题上使用来自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;
}
});