问题
I use Java 8, Hibernate 5.1.0.Final and 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();
}
Every test()
method execution will produce 10 (thread pool size) rows more in pg_stat_activity
. They are simple select * from
queries which have idle in transaction
state and never disappear. So I reach hibernate.c3p0.max_size
limit and my application stops working with database.
Database module:
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);
}
}
I have tried to @Inject Provider<ExampleDAO> exampleDAOProvider
into the task class code but it does not change anything. If I @Inject exampleDAO
, then I face concurrency issues (ConcurrentModificationException
) because it uses the same EntityManager
.
If I use @Inject Provider<ExampleDAO> exampleDAOProvider
or direct @Inject ExampleDAO exampleDAO
without multithreading, it works well and connections get released.
Why does it happen? How to get connections released in the multithreaded code?
回答1:
I have annotated almost every DAO method with @Transactional
and this seems to solve my problem. After processing, transactions are committed and connections released. Didn't mark as @Transactional
methods which query entities which should be persisted later in the same EntityManager
or Session
to avoid using merge()
. Please note that @Transactional
works only for public methods and synchronized
doesn't work with @Transactional
.
GenericDAOImpl use @Inject Provider<EntityManager>
instead of @Inject EntityManager
:
@Inject
protected Provider<EntityManager> entityManagerProvider;
private EntityManager getEntityManager() {
return entityManagerProvider.get();
}
Related discussion: Does Guice Persist provide transaction scoped or application managed EntityManager?
UDPATE 1
Another things to check:
- If you're using Hibernate 5.1.0.Final, then persistence.xml should contain:
<property name="hibernate.connection.provider_class" value="org.hibernate.c3p0.internal.C3P0ConnectionProvider" />
Instead of:
<property name="connection.provider_class" value="org.hibernate.connection.C3P0ConnectionProvider" />
UDPATE 2
If you're using
<property name="hibernate.enable_lazy_load_no_trans" value="true" />
it may cause connection leaks when lazy loading. Related discussions:
- org.hibernate.LazyInitializationException - could not initialize proxy - no Session
- Solve Hibernate Lazy-Init issue with hibernate.enable_lazy_load_no_trans
来源:https://stackoverflow.com/questions/43919411/guice-dao-provider-in-thread-pool-queries-become-idle-in-transation