Hibernate использует старый закрытый SessionImpl вместо заданного SessionImpl - PullRequest
0 голосов
/ 10 декабря 2018

У меня проблема с Hibernate Enverse (версия 5.2.0-Final).

Контекст: Я проверяю некоторые сущности с некоторыми ленивыми отношениями.У меня есть jsf-страница, которая загружает одну версию одной сущности со всеми связями этой версии.Это отлично работает.Так что теперь у меня есть страница, которая показывает ревизию сущности со всеми связями этой ревизии.На этой странице я могу открыть набор полей, который запускает AJAX.В этом запросе мы повторно присоединяем все отношения, вызывая entityManager.merge(entity), чтобы иметь возможность получить ленивые отношения в этом наборе полей.(EntityManager - RequestScoped)

Проблема: AJAX - это новый запрос.Сервер вызывает entityManager.merge(entity), что обеспечивает создание нового EntityManager (поэтому создается новый org.hibernate.internal.SessionImpl).На этот объект hibernate звонит SessionImpl.merge(...).Но в методе org.hibernate.internal.AbstractSharedSessionContract.createQuery(String) используется другой объект SessionImpl, который уже был закрыт в запросе ранее.Это обеспечивает java.lang.IllegalStateException: Session/EntityManager is closed.

В одном предложении : хотя был создан новый entityManager и было вызвано объединение для этого нового entityManger, Hibernate использует старый Session / EntityManager запроса перед.

Я отладил проблему и обнаружил следующее:

  • Debug1 : Показывает стековую трассировку SessionImpl.merge(...) с идентификатором объекта сеанса

  • Debug2 : Показывает последний метод с правильным объектом SessionImpl (см. Его идентификатор).Этот объект не используется в следующих методах.

  • Debug3 : шаг после Debug2 не знает заданный объект SessionImpl.У него есть собственный объект SessionImpl в collection.initializor.versionsReader.Этот сеанс был создан и закрыт в запросе ранее (при загрузке страницы).
  • Debug4 : теперь Hibernate хочет создать запрос с закрытым SessionImpl
  • Debug5 : это приводит к исключению, так как сессия закрыта.

Мои вопросы :

  • Этоошибка Hibernate?

  • Почему данный SessionImpl в методе org.hibernate.type.CollectionType.getElementIterater(...) не используется?

  • Кто-нибудь знает решение или обходной путь для этой проблемы?

Большое спасибо за любую идею.Я потратил несколько дней на эту ошибку.

1 Ответ

0 голосов
/ 11 декабря 2018

Почему аргумент Session в o.h.type.CollectionType.getElementIterator не используется?

Короткий ответ: он не обязателен, это просто проблема обратной совместимости с 8 лет назад.

Длинный ответ - система типов, используемая для фактического отклонения некоторого поведения на основе того, задал ли пользователь сеанс для работы в EntityMode.MAP или EntityMode.POJO и, следовательно, типы, необходимые для того, чтобы знать, в каком режимесеанс был в;следовательно, почему это было передано.

Но даже в 2011 году, когда это было изменено, аргумент сеанса влиял только на поведение тогда и только тогда, когда сеанс работал в EntityMode.MAP.Другими словами, все другие режимы всегда перенаправляются непосредственно в базовый метод коллекций * 1016.

Однако все это, кроме этого, никак не влияет на то, что вы испытываете в своем Debug3 .снимок экрана.

Это ошибка в Hibernate?

Нет, основываясь на том, что я прочитал, я полагаю, вы смешиваете проблемы.

В Hibernate (без Envers) вы в основном можете сделать это

// Request 1
request1EntityManager = getEntityManager();
sessionScopeEntity = request1EntityManager.find( MyEntity.class, myEntityId );

// Request 2
request2EntityManager = getEntityManager();
sessionScopeEntity = request2EntityManager.merge( sessionScopeEntity );
for ( SomeCollectionItem Item : sessionScopeEntity.getSomeCollection() ) {
  // do things here
}

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

// Request 1
request1EntityManager = getEntityManager();
sessionScopeEntity = request1EntityManager.find( MyEntity.class, myEntityId );
sessionScopeEntity.getSomeCollection().size() // initialize collection w/request1Session

// Request 2
request2EntityManager = getEntityManager();
for ( SomeCollectionItem Item : sessionScopeEntity.getSomeCollection() ) {
  // do things here
}

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

Основное различие между экземпляром сущности, возвращаемым Hibernate, и экземпляром проверяемого объекта, возвращаемым Envers, заключается в том, что проверяемый объектЭкземпляр сущности НЕ является управляемой персистентной сущностью.

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...