GWT RequestFactory: как использовать один EntityManager для каждого запроса

Чтобы заставить RequestFactory сохранять прикрепленные объекты, мне нужно убедиться, что я используйте один и тот же EntityManager для каждого запроса.

Для этого я собрал свой собственный класс Factory на основе реализации ThreadLocal, но я не знаю, как правильно освободить ресурсы (например, как узнать, что запрос завершен, и вызвать close()).

Есть ли простой способ гарантировать, что один EntityManager используется во всем заданном ServletRequest, не прибегая к полноценному J2EE/CDI? Я выберу этот путь, если придется, но я надеялся, что все будет просто, тем более что я хотел бы продолжать использовать облегченный сервер разработки, который поставляется с GWT.


person George Armhold    schedule 14.02.2011    source источник


Ответы (3)


Вот что я в конечном итоге придумал, основываясь на отзывах GWT Google Group и БобВ.

Создайте локальный держатель потока для EntityManager; ссылайтесь на это в своих объектах, когда им нужно получить 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);
    }
}

Затем создайте фильтр, который установит начальный EntityManager для запроса:

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();
        }

    }
}

Затем примените фильтр к шаблону URL /gwtRequest:

<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>

Обратите внимание, что здесь есть недостаток — EntityManager создается для каждого запроса, проходящего через этот сервлет, независимо от того, используется ли он вашим базовым кодом или нет. Вероятно, его можно было бы сделать более надежным и каким-то образом лениво создавать EntityManager (и транзакцию) только по запросу.

Но пока этот код работает хорошо с RequestFactory. Предложения по улучшению приветствуются.

Примечание: этот опыт научил меня тому, что, вероятно, стоит перейти на полноценный CDI, а не пытаться реализовать его части, подобные этому. У меня просто не было времени на такой ход во время этого проекта.

person George Armhold    schedule 15.02.2011
comment
Я сомневаюсь, что у вас будет много запросов через этот фильтр, которым не нужен EntityManager. Это то, что я сделал, и это работает хорошо для меня тоже! В appengine бит транзакции не будет работать, но простое открытие и закрытие менеджера постоянства в JDO тоже работает хорошо. - person Riley Lark; 21.02.2011

Пример приложения DynaTableRf делает нечто подобное, добавляя фильтр сервлета для настройки контекста сохраняемости в его web.xml. В качестве альтернативы вы можете создать подкласс RequestFactoryServlet и переопределите его метод doPost(), чтобы удалить EntityManager в блоке finally при возврате super.doPost().

person BobV    schedule 14.02.2011
comment
Спасибо (еще раз), BobV. Я пытаюсь использовать подход с фильтром сервлета. Получил работу с одним EntityManager-per-request, и это, безусловно, решило мою проблему RequestFactory. Но теперь я сливаю ресурсы. Это всегда что-то. :-) Я вернусь и подведу итоги, как только исправлю ошибки. - person George Armhold; 15.02.2011

Если вы используете Spring, вам нужно всего лишь добавить фильтр сервлета OpenEntityManagerInView в файл 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>
person StefanR    schedule 16.08.2012