线程池中的 Guice DAO 提供程序 - 查询变为 'idle in transation'

Guice DAO Provider in thread pool - queries become 'idle in transation'

我使用 Java 8、Hibernate 5.1.0.Final 和 Guice 4.1.0.

@Inject
private Provider<ExampleDAO> exampleDAOProvider;

public void test(){

    ExecutorService threadPool = Executors.newFixedThreadPool(10);

    for (int i = 0; i < 100; i++)
        threadPool.execute(new Runnable() {

            @Override
            public void run() {
                logger.info(exampleDAOProvider.find(1l));

            }
        });

    threadPool.shutdown();

}

每个 test() 方法执行将在 pg_stat_activity 中产生 10(线程池大小)行。它们是具有 idle in transaction 状态且永不消失的简单 select * from 查询。所以我达到了 hibernate.c3p0.max_size 限制并且我的应用程序停止使用数据库。

数据库模块:

public class ExampleModule extends PrivateModule {

    @Override
    public void configure() {

        install(new JpaPersistModule("example-persistence-unit").properties(jpaProperties()));

        bind(ExampleDAO.class).to(ExampleDAOImpl.class);

        expose(ExampleDAO.class);

        Key<PersistFilter> key = Key.get(PersistFilter.class, ExamplePersistenceUnit.class);
        bind(key).to(PersistFilter.class);
        expose(key);
    }
}

我已经尝试 @Inject Provider<ExampleDAO> exampleDAOProvider 进入任务 class 代码,但它没有改变任何东西。如果我 @Inject exampleDAO,那么我会面临并发问题 (ConcurrentModificationException),因为它使用相同的 EntityManager.

如果我在没有多线程的情况下使用 @Inject Provider<ExampleDAO> exampleDAOProvider 或直接 @Inject ExampleDAO exampleDAO,它运行良好并且连接被释放。

为什么会这样?如何在多线程代码中释放连接?

我几乎用 @Transactional 注释了每个 DAO 方法,这似乎解决了我的问题。处理后,提交事务并释放连接。没有标记为 @Transactional 查询实体的方法,这些实体稍后应该在相同的 EntityManagerSession 中保留以避免使用 merge()。请注意,@Transactional 仅适用于 public 方法,而 synchronized 不适用于 @Transactional

GenericDAOImpl 使用 @Inject Provider<EntityManager> 而不是 @Inject EntityManager:

@Inject
protected Provider<EntityManager> entityManagerProvider;

private EntityManager getEntityManager() {
    return entityManagerProvider.get();
}

相关讨论:Does Guice Persist provide transaction scoped or application managed EntityManager?


UDPATE 1

要检查的其他事项:

  • 如果您使用的是 Hibernate 5.1。0.Final,则 persistence.xml 应包含:

<property name="hibernate.connection.provider_class" value="org.hibernate.c3p0.internal.C3P0ConnectionProvider" />

而不是:

<property name="connection.provider_class" value="org.hibernate.connection.C3P0ConnectionProvider" />

UDPATE 2

如果您正在使用

<property name="hibernate.enable_lazy_load_no_trans" value="true" />

延迟加载时可能会导致连接泄漏。相关讨论:

  • org.hibernate.LazyInitializationException - could not initialize proxy - no Session
  • Solve Hibernate Lazy-Init issue with hibernate.enable_lazy_load_no_trans