Почему контекст сохранения состояния гибернации недоступен вне транзакции?

Пример Влада по исправлению MultipleBagsException будет нашей отправной точкой: Как исправить MultipleBagsException — Vlad Mihalcea

В нем он делает 2 последовательных запроса HQL, чтобы загрузить 2 лениво загруженных отношения (мешки).

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

utx.begin();
List<Post> posts = entityManager.createQuery("""
    select distinct p
    from Post p
    left join fetch p.comments
    where p.id between :minId and :maxId""", Post.class)
.setParameter("minId", 1L)
.setParameter("maxId", 50L)
.setHint(QueryHints.PASS_DISTINCT_THROUGH, false)
.getResultList();
 
posts = entityManager.createQuery("""
    select distinct p
    from Post p
    left join fetch p.tags t
    where p in :posts""", Post.class)
.setParameter("posts", posts)
.setHint(QueryHints.PASS_DISTINCT_THROUGH, false)
.getResultList();
utx.rollback();

Для справки: это проект JavaEE (не Spring), развернутый в Wildfly 20. Модуль сохранения определен как таковой в файле persistence.xml.

<persistence-unit name="some-p-unit-name">
    <jta-data-source>java:/jdbc/someDB</jta-data-source>
</persistence-unit>  

И EntityManager определяется как таковой:

    @PersistenceContext(type = PersistenceContextType.EXTENDED, unitName = "some-p-unit-name")
private EntityManager em;

@Produces
@RequestScoped
@SomeResource // this is an annotation to differentiate it from another entity manager that can also be injectable
public EntityManager getEm() {
    return em;
}

Так почему же нам нужно запускать транзакцию, чтобы PersistenceContext был включен, даже если мы настраиваем его на использование контекста EXTENDED?


person fpezzini    schedule 27.11.2020    source источник
comment
Почему вы включили все, кроме фактического класса, который использует диспетчер сущностей? Может быть, не важно знать, находитесь ли вы внутри EJB?   -  person Smutje    schedule 27.11.2020
comment
Он вызывается асинхронным методом в EJB без сохранения состояния.   -  person fpezzini    schedule 27.11.2020
comment
Я уверен, что есть сотни примеров асинхронных методов EJB, использующих диспетчер сущностей без ручных транзакций, так что это может не быть фундаментальной проблемой JEE...   -  person Smutje    schedule 27.11.2020
comment
Спасибо за ваш вклад. Как вы думаете, может ли аннотация @TransactionAttribute(value=TransactionAttributeType.NEVER), добавленная в класс EJB, иметь какое-то отношение к этому? Я настроил его, чтобы избежать конфликта с транзакцией, которая запускается модулем, который мы вызываем для хранения информации в базе данных.   -  person fpezzini    schedule 27.11.2020
comment
Итак, после отключения транзакций для EJB вы удивляетесь, что тот же EJB не запускает транзакцию автоматически?   -  person Smutje    schedule 27.11.2020
comment
Я не уверен, что следую. Вы утверждаете, что для существования PersistenceContext требуется транзакция? Поскольку из того, что я видел в документации, это может быть либо транзакционный, либо расширенный (stackoverflow.com/a/2548617)   -  person fpezzini    schedule 27.11.2020
comment
Из ссылки, которую вы разместили: «Расширенный контекст сохранения, управляемый контейнером, может быть инициирован только в рамках сеансового компонента с отслеживанием состояния».   -  person Smutje    schedule 28.11.2020
comment
Спасибо, это было очень полезно! Я подумаю над тем, чтобы превратить этот EJB в EBJ с отслеживанием состояния.   -  person fpezzini    schedule 28.11.2020


Ответы (1)


Спасибо @Smutje за то, что привели меня к правильному ответу. Решение в нашем случае состояло в том, чтобы аннотировать класс, определяющий EntityManager, с помощью @Stateful, согласно документации, PersistenceContext доступен только для EJB с отслеживанием состояния в сценариях, управляемых контейнером.

Пример кода ниже. Обратите внимание на аннотацию Stateful, а также тип контекста сохранения = EXTENDED.

@Stateful
public class Resources {
    @PersistenceContext(type = PersistenceContextType.EXTENDED, unitName = "some-p-unit-name")
    private EntityManager em;

    @Produces
    @RequestScoped
    public EntityManager getEm() {
        return em;
    }
}
person fpezzini    schedule 03.12.2020