Вопрос

Мы используем Hibernate (с JPA) и Hibernate Envers для сохранения истории объектов.Веб-приложение запускает множество потоков, некоторые из них создаются вызовом метода RMI из других приложений, некоторые создаются самим приложением, а некоторые создаются для обработки http-запросов (они генерируют представления).

Мы также используем шаблон Open Session In View для управления сеансами, поэтому наш файл web.xml содержит:

<filter>
    <filter-name>openEntityManagerInViewFilter</filter-name>
    <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>openEntityManagerInViewFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Доступ к базе данных осуществляется с помощью DAO, все они имеют EntityManager, внедренные Spring.

@PersistenceContext
protected EntityManager em;

@PersistenceUnit
protected EntityManagerFactory emf;

Все работало вполне хорошо, прежде чем мы решили использовать Hibernate Envers.Когда какой-либо поток, не являющийся потоком, генерирующим представление, запускает код для получения старой версии объекта, выдается исключение.

@Override
public O loadByRevision(Long revision, Long id) {
    @SuppressWarnings("unchecked")
    O object = (O) AuditReaderFactory.get(em).createQuery().forEntitiesAtRevision(getBaseClass(), revision.intValue())
            .add(AuditEntity.id().eq(id)).getSingleResult();
    return object;
}

Исключение в потоке «Планировщик» org.hibernate.SessionException:Сессия закрыта!at org.hibernate.internal.abstractsessionImpl.errorifClosed (AbstractSessionImpl.java:129) в org.hibernate.internal.sessionImpl.createequery (SessionImpl.java:1776) at org.hibernate.envers.tools.query.querybuilder.toquery.toquery.toquery.toquery.toquery .java: 226) в org.hibernate.envers.query.impl.abstractauditquery.buildquery (AbstractaudiTquery.java:92) в org.hibernate.envers.query.impl.entitiesatrevision.list (entitiatrevisionquery.java:108) org. hibernate.envers.query.impl.abstractauditquery.getsingleresult (AbstractAuditQuery.java:110) (...)

Когда приведенный выше код запускается потоком, генерирующим представление, он работает нормально.Кроме того, код без конвертации в DAO отлично работает для каждого потока.Например, фрагмент ниже

@Override
public O load(Long id) {
    final O find = em.find(getBaseClass(), id);
    return find;
}

может без проблем запускаться потоками RMI.

Почему потоки без просмотра могут вызывать методы диспетчера сущностей без исключений, но не могут использовать AuditReaderFactory Envers с этим менеджером сущностей?Я подумал, что, возможно, вызов метода диспетчера сущностей создает временный сеанс, но при использовании Envers этого не происходит, правда ли это?

Как лучше всего решить эту проблему (чтобы AuditReaderFactory можно было использовать из каждого потока)?

Это было полезно?

Решение

Мы не выяснили, почему в потоках, не связанных с пользовательским интерфейсом, вызывается метод EntityManagerFactory сработало, но вызывается метод AuditReaderFactory не сделал.В любом случае, мы нашли способ это исправить.

Решением было аннотировать методы с помощью @Transactional.Если какой-либо метод в цепочке вызовов перед вызовом AuditReaderFactory был помечен как @Transactional, не было SessionException в потоках, не связанных с пользовательским интерфейсом.

Оказалось, что создание loadByRevision транзакций было недостаточно.Если объект, возвращаемый этим методом, содержит лениво загруженные постоянные пакеты, доступ к ним за пределами loadByRevision область метода вызвала LazyInitializationException (сеанса не было).

Окончательное решение заключалось в том, чтобы убедиться, что если какой-либо поток захочет загрузить некоторые данные из базы данных, вся загрузка (получение объекта и доступ к лениво загруженным коллекциям) будет выполняться внутри одного метода, аннотированного @Transactional.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top