Как использовать несколько аннотированных методов @Transactional? - PullRequest
0 голосов
/ 13 апреля 2020

У меня есть проект Spring-boot, где у меня есть служебный компонент с 2 @Transactional аннотированными методами.

Эти методы выполняют действия JPA (спящего режима) только для чтения, чтобы извлекать данные из файловой базы данных H SQL, используя как репозитории JPA, так и загрузчики с отложенной загрузкой в ​​сущностях.

У меня также есть CliBan, который обрабатывает команды (используя PicoCLI). Из одной из этих команд я пытаюсь вызвать оба @Transactional аннотированных методов, но я получаю следующую ошибку при выполнении второго метода:

org.hibernate.LazyInitializationException - could not initialize proxy - no Session
        at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:602)
        at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:217)
        at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:581)
        at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:148)
        at org.hibernate.collection.internal.PersistentSet.iterator(PersistentSet.java:188)
        at java.util.Spliterators$IteratorSpliterator.estimateSize(Spliterators.java:1821)
        at java.util.Spliterator.getExactSizeIfKnown(Spliterator.java:408)
        at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
        at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
        at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
        at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
        at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:566)
        at <mypackage>.SomeImpl.getThings(SomeImpl.java:<linenr>)
...

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

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

Итак, мои вопросы:

  • Это связано с тем, как hibernate запускает сеанс, как транзакции закрывают сеансы или что-либо, связанное с базой данных H SQL?
  • Является ли добавление включающей транзакции правильным способом решения проблемы или это просто устранение симптома?
  • Как лучше всего использовать несколько @Transactional аннотированных методов из одного метода?

РЕДАКТИРОВАТЬ: Я хочу пояснить, что я не выставляю сущности вне транзакционных методов, так что на первый взгляд мне кажется, что 2 транзакционных метода должны работать независимо друг от друга.

EDIT2: для большей ясности: транзакционные методы должны быть доступны в API. и пользователь API должен иметь возможность вызывать несколько из этих транзакционных методов, без необходимости использовать транзакционные аннотации и без получения LazyInitializationException

Api:

public interface SomeApi {
    List<String> getSomeList();
    List<Something> getThings(String somethingGroupName);
}

Реализация:

public class SomeImpl implements SomeApi {

    @Transactional
    public List<String> getSomeList() {
        return ...; //Do jpa stuff to get the list
    }

    @Transactional
    public List<Something> getThings(String somethingGroupName) {
        return ...; //Do other jpa stuff to get the result from the group name
    }
}

Использование третьей стороной (которая может не знать, что такое транзакционность):

public someMethod(String somethingGroupName) {
    ...

    SomeApi someApi = ...; // Get an implementation of the api in some way

    List<String> someList = someApi.someList();
    if (someList.contains(somethingGroupName) {
        System.out.println(someApi.getThings(somethingGroupName));
    }

    ...
}

Ответы [ 2 ]

0 голосов
/ 15 апреля 2020

Я обнаружил, что hibernate из коробки не открывает сеанс заново и, следовательно, не включает отложенную загрузку после завершения первой транзакции, независимо от того, находятся ли последующие операторы jpa в транзакции или нет. Однако в hibernate есть свойство, позволяющее включить эту функцию:

spring:
  jpa:
    properties:
      hibernate.enable_lazy_load_no_trans: true

Это гарантирует, что если сеанс отсутствует, то будет создан временный сеанс. Я полагаю, что это также предотвратит завершение сеанса после транзакции, но я точно не знаю этого.

Частичный зачет идет на следующие ответы из других вопросов StackOverflow:

ВНИМАНИЕ: В спящем режиме 4.1.8 есть ошибка, которая может привести к потере данных! Убедитесь, что вы используете 4.2.12, 4.3.5 или более новые версии hibernate. См .: https://hibernate.atlassian.net/browse/HHH-7971.

0 голосов
/ 13 апреля 2020

Похоже, что вы получаете доступ к некоторым неинициализированным данным из ваших сущностей после завершения транзакций. В этом случае поставщик сохраняемости может выдать исключение lazyinitialization.

Если вам нужно получить некоторую информацию, не загруженную с помощью сущностей, вы можете использовать одну из двух стратегий:

  • аннотируйте вызывающий метод также аннотацией @Transactional, как вы это сделали: он не запускает новую транзакцию для каждого вызова, но делает активную открытую транзакцию до тех пор, пока не завершится ваш вызывающий метод, избегая исключения; или
  • заставляет вызываемые методы загружать необходимые поля с использованием идиомы JPIN JOIN FETCH.

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

...