I'm using: Hibernate 3.6.8 with mySQL, Tomcat 6, JDBC connection pooling
I've run into a problem where Hibernate is opening a large number of connections, which eventually causes my web app to not be able to get a new connection. At first I thought the problem was that I was running without a connection pool, so I hooked up Tomcat JDBC pooling.
This made the problem worse. Instead of failing after a handful of queries, it failed immediately. This turned out to be because Hibernate was trying to open a larger number of connections than my pool size (30). I checked, and everywhere I'm creating an EntityManager, I'm calling close(), within a finally clause if there's a try, so I wasn't sure why they were staying open.
Looking at the source for close(), I discovered that it doesn't actually close if there's a transaction open,
so I tried calling getTransaction().commit() on the entity manager, even though its just a select statment. This had no effect.
Then I discovered this configuration flag, and turned it on:
<property name="hibernate.ejb.discard_pc_on_close" value="true" />
This made a difference, I got an exception when I tried to render the object for output: failed to lazily initialize a collection of role : XXX.Comment.attachments, no session or session was closed
The issue seems to be that I make a number of queries to create a collection of Comments, and all of these queries keep a connection open. The Comment class contains this:
@OneToMany
@JoinTable(name = "Object", joinColumns = { @JoinColumn(name = "parentId") }, inverseJoinColumns = { @JoinColumn(name = "id") })
public List<Attachment> getAttachments()
{
if (attachments == null)
{
attachments = new ArrayList<Attachment>();
}
return attachments;
}
I'll mention that there's some inheritance going on here.
Attachment is a subclass of KObject (table name Object in the db), and KObject contains the parentId used to map back to Comment (which is also a subclass of KObject).
For clarification, here's my call to the entity manager:
public <T> T findById(Class<T> objClass, String id)
{
EntityManager em = theEMF.createEntityManager();
em.getTransaction().begin();
try {
T obj = em.find(objClass, id);
return obj;
} catch(NoResultException e) {
return null;
} finally {
em.getTransaction().commit();
em.clear();
em.close();
}
}
theEMF is a static member variable that's initialized on first use.
I thought the solution would be making it @OneToMany(fetch=FetchType.EAGER), but this had no effect. What am I doing wrong?