We have an application that connects to postgre database using Hibernate. We use C3P0 as connection pool.
persistence.xml:
<persistence-unit name="tv-europe-core" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistence</provider>
---classes---
<properties> <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
<property name="hibernate.connection.password" value="---password---" />
<property name="hibernate.connection.url" value="---database---" />
<property name="hibernate.connection.username" value="---username---" /><property name="hibernate.connection.driver_class" value="org.postgresql.Driver" />
<property name="hibernate.connection.release_mode" value="after_statement" />
<property name="hibernate.connection.autocommit" value="false" /><property name="hibernate.c3p0.minPoolSize" value="5"/>
<property name="hibernate.c3p0.maxPoolSize" value="60"/>
<property name="hibernate.c3p0.maxIdleTime" value="10"/>
<property name="hibernate.c3p0.idleConnectionTestPeriod" value="5"/>
<property name="hibernate.c3p0.testConnectionOnCheckin" value="true"/>
</properties>
</persistence-unit>
Saving object:
public Entity saveOrUpdate(Entity entity, User u) { EntityTransaction tx = EntityManagerHelper.getEntityManager().getTransaction(); try { if(!tx.isActive()) tx.begin(); Entity result = null; if (getID(entity) == null) { EntityManagerHelper.getEntityManager().persist(entity); } else { result = EntityManagerHelper.getEntityManager().merge(entity); } tx.commit(); return result; } catch (RuntimeException re) { re.printStackTrace(); tx.rollback(); throw re; } }
Loading objects:
@SuppressWarnings("unchecked")
public List<Entity> findByProperty(String propertyName, final Object value,
final int... rowStartIdxAndCount) {
try {
final String queryString = "select model from " + clazz.getName()
+ " model where model." + propertyName + "= :propertyValue";
Query query = EntityManagerHelper.getEntityManager().createQuery(
queryString);
query.setParameter("propertyValue", value);
if (rowStartIdxAndCount != null && rowStartIdxAndCount.length > 0) {
int rowStartIdx = Math.max(0, rowStartIdxAndCount[0]);
if (rowStartIdx > 0) {
query.setFirstResult(rowStartIdx);
}
if (rowStartIdxAndCount.length > 1) {
int rowCount = Math.max(0, rowStartIdxAndCount[1]);
if (rowCount > 0) {
query.setMaxResults(rowCount);
}
}
}
final List<Entity> result = query.getResultList();
return result;
} catch (RuntimeException re) {
re.printStackTrace();
throw re;
}
}
Creating EntityManagerFactory and getting EntityManager:
private static EntityManagerFactory emf;
private static final ThreadLocal<EntityManager> threadLocal = new ThreadLocal<EntityManager>();
public static EntityManager getEntityManager() throws HibernateException {
EntityManager session = (EntityManager) threadLocal.get();
if (session == null || !session.isOpen()) {
session = (emf != null) ? emf.createEntityManager()
: null;
threadLocal.set(session);
}
return session;
}
The problem is, that time after time a database connection stays in "idle in transaction" state and after that this connection is never returned. After several days the application stops responding because the number of connections exceeds the pool max size.
When hibernate hibernate.connection.autocommit is enabled, those connections do not become "idle in transaction", but they are still somehow blocked and the resulting problem is the same.
Are we doing something wrong (some missing configuration etc..)?
I have noticed that when I use eager loading only, there is no problem. But I have to use lazy loading because of performance. Should the EntityManagerFactory be explicitly closed? I hope not, because we need the app to work for a very long time and I suspect I cannot just reset it when somebody is working with persistent objects.
In the log I see the following, but I dont know if it somehow relates to our problem:
java.lang.Exception: DEBUG -- CLOSE BY CLIENT STACK TRACE
at com.mchange.v2.c3p0.impl.NewPooledConnection.close(NewPooledConnection.java:491)
at com.mchange.v2.c3p0.impl.NewPooledConnection.close(NewPooledConnection.java:191)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.dest royResource(C3P0PooledConnectionPool.java:470)
at com.mchange.v2.resourcepool.BasicResourcePool$1DestroyResourceTask.run(BasicResourcePool.ja va:964)
at com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunn er.java:547)
Thanks for any help! :)