Domanda

Using Hibernate, c3p0, Guice and Guice-persist, we have strange issues with the fact that the connections to the data are not released to the pool after transactions.

It seems that it's related to the way JpaPersistService is done, caching the EntityManager in a threadLocal variable:

private final ThreadLocal<EntityManager> entityManager = new ThreadLocal<EntityManager>();

My questions are:

  • Should we remove guice-persist and replace this by something creating a new entityManager then closing it each time we need (as suggested here) ?
  • Or caching the entityManager is the way to go, and we should fix our issues with something else ?

(Our issues: 1)mysql connection killed by mysqld after the classic 8h timeout, so the entityManager is no longer usable. Can be solved by enabling the c3p0 keep-alive, but as the application will sometime will not be used for a long time, we would like to avoid keeping useless active connection. 2) It seems that each EntityManager do some caching, and entites are not coherent between entityManager. Didn't looked at how to solve that)

È stato utile?

Soluzione

Finally found a solution.

It seems that it's a particular bug in guice-persist.

If an EntityManager is injected outside an UnitOfWork or outisde a transaction, then an never-closing EntityManager is created, leading to strange issues.

The documentation of guice-persist doesn't point this.

The solution is to always inject Provider<EntityManager> instead of directly EntityManager if you're not inside a transaction or a UnitOfWork

Altri suggerimenti

I believe you have the same problem as I experienced: Guice JPA - "This connection has been closed." error

It's all nicely decribed in Issue 730: Automatically started UnitOfWork is never ended

When using the JpaPersistService, if you attempt to access an EntityManager outside of an active UnitOfWork, Guice will automatically start one for you. However, as Guice does not (and cannot) know when to end this UnitOfWork, it never does.

Result? The offending thread will be stuck with the same EntityManager throughout the life of the application. This is a bad state for the application to run in, and ours inevitably exhaust the available memory after a while and crash.

The real killer here is that it's not at all obvious when you've made this mistake. The only real tip-off is that you're getting inconsistent data from your database between different threads (due to the EMs first-level cache) or that the applications memory consumption keeps on growing. In my case it was the active connection in the pool that got me to suspect it, and then, when I turned on detailed logging I noticed that app was not borrowing the connection from pool at all, instead it was reusing the connection already held by unclosed EntityManager.

Actually there are actually quite a few duplicates of this issue reported: http://code.google.com/p/google-guice/issues/list?can=1&q=UnitOfWork

So injecting the EntityManager wouldn't do much good I think. The problem occurs when you actually use any EntityManager methods. You just need to ensure every access is inside a UnitOfWork. How to do it depends on your application.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top