问题
In order to get RequestFactory
to persist attached entities, I need to ensure that I use the same EntityManager
for each request.
I cobbled together my own Factory class for this based on a ThreadLocal
implementation, but I'm unsure how to properly release resources (e.g. how to know that the request has finished and call close()
).
Is there a simple way to ensure that a single EntityManager is used throughout a given ServletRequest without resorting to full-on J2EE/CDI? I'll take that route if I must, but I was hoping to keep things simple, especially since I'd like to continue using the lightweight development server that comes with GWT.
回答1:
Here is what I ultimately came up with, based on feedback from the GWT Google Group, and BobV.
Create a thread-local holder for the EntityManager; reference this in your entities when they need to get an EntityManager:
public class ThreadLocalEntityManager
{
private static ThreadLocal<EntityManager> holder = new ThreadLocal<EntityManager>();
private ThreadLocalEntityManager()
{
}
public static EntityManager get()
{
return holder.get();
}
public static void set(EntityManager em)
{
holder.set(em);
}
}
Then create a filter that will set the initial EntityManager for the request:
public class PersistenceFilter implements Filter
{
protected static final Logger log = Logger.getLogger(PersistenceFilter.class.getName());
private EntityManagerFactory factory;
@Override
public void init(FilterConfig filterConfig) throws ServletException
{
factory = Persistence.createEntityManagerFactory("my_persistence");
}
@Override
public void destroy()
{
factory.close();
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException
{
EntityManager em = factory.createEntityManager();
ThreadLocalEntityManager.set(em);
EntityTransaction tx = em.getTransaction();
tx.begin();
try
{
chain.doFilter(req, res);
tx.commit();
}
catch (Exception e)
{
tx.rollback();
}
finally
{
log.info("closing EntityManager: " + EMF.entityManager());
em.close();
}
}
}
Then apply the filter to the /gwtRequest URL pattern:
<filter>
<filter-name>PersistenceFilter</filter-name>
<filter-class>com.example.PersistenceFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>PersistenceFilter</filter-name>
<url-pattern>/gwtRequest</url-pattern>
</filter-mapping>
Note that there is a flaw here- an EntityManager is created for each request that goes through this servlet, whether it's used by your underlying code or not. It could probably stand to be made more robust and somehow lazily create the EntityManager (and the transaction) only when requested.
But so far this code seems to work well with RequestFactory
. Suggestions for improvements highly welcomed.
Note: this experience has taught me that it's probably worth moving to full-on CDI rather than trying to implement pieces of it such as this. I just didn't have the time available for such a move during this project.
回答2:
The DynaTableRf sample app does something similar by adding a servlet filter to set up a persistence context in its web.xml file. Alternatively, you could subclass the RequestFactoryServlet and override its doPost()
method to tear down the EntityManager
in a finally
block when super.doPost()
returns.
回答3:
If you're using Spring you only need to add the an OpenEntityManagerInView servlet filter to your web.xml.
<filter>
<filter-name>entityManagerFilter</filter-name>
<filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>entityManagerFilter</filter-name>
<url-pattern>/gwtRequest</url-pattern>
</filter-mapping>
来源:https://stackoverflow.com/questions/4988397/gwt-requestfactory-how-to-use-single-entitymanager-per-request