Pregunta

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.

¿Fue útil?

Solución

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.

Otros consejos

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.

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>
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top