Java Play 2.3 F.Promise没有绑定到此线程的实体管理器

时间:2015-05-05 13:19:01

标签: java mysql concurrency playframework

我试图同时访问mysql数据库以获取使用F.Promise的对象列表,但我得到:

  

没有实体经理绑定到此线程

虽然在我调用存储过程的方法中,我用JPA.withTransactionAsync包装它但仍然得到相同的错误。

 import play.libs.F.*;
 import play.mvc.*;
 import java.util.concurrent.Callable;

 import static play.libs.F.Promise.promise;

 public class Application extends Controller {
 public static Promise<Result> index() {
    return promise(new Function0<Integer>() {
        public Integer apply() {
            return getUserId();
        }
    }).map(new Function<Integer,Result>() {
        public Result apply(Integer i) {
        return ok("Got " + i);
        }
    });

并在getUserId()内部

public static int getUserId()
{
    return JPA.withTransactionAsync(.........);
}

1 个答案:

答案 0 :(得分:2)

这是一个已知问题 - 您可以看到它的讨论here

引用James Roper ......

跨线程使用事务会引入死锁。这是问题所在:

  • 请求A获取连接
  • 请求A执行一些异步操作,并生成其线程
  • 请求B获取线程,尝试获取连接,但因为连接池为空而阻塞
  • 请求A的异步操作完成,因此它进入队列执行,并最终将连接返回到池,但它无法执行,因为线程被请求B保持,等待连接,它永远不会得到,因为它等待A返回它,它不能......

所以我们陷入僵局。上面的场景描述了一个简单的场景,其中只有一个连接和一个线程,但我们有许多用户在更现实的情况下看到生产死锁,其中许多线程在连接池上被阻塞,并且许多连接保持线程被阻塞等待连接池阻止线程产生。

解决方案是使用异步连接池,不幸的是,JPA不支持。解决方法是使用专用线程池来异步获取连接,这样,当连接池耗尽时,专用线程将阻塞,但这不会影响任何内容(除了可能嵌套尝试获取连接)因为它仅用于从池中获取连接。

所以,你有3个选择:

  1. 您可以使用this library正确处理实体管理器的关闭,但仍然可以解决上述死锁问题。

  2. 使用JPA.withTransaction代替JPA.withTransactionAsync

  3. 使用没有这些问题的Ebean。

  4. 编辑:为了完整起见,我将添加选项4(我的首选解决方案),即不使用ORM框架并将其替换为jOOQ。