Весенняя многопоточность с гибернацией - PullRequest
1 голос
/ 19 февраля 2020

Некоторые короткие версии информации: Spring Boot 2.1 с Hibernate 5 и Java 8.

Мы пытаемся выполнить многопоточный этап обработки, на котором мы используем сервисы Spring для работы с объектами Hibernate. В основном это выглядит следующим образом:

   ExecutorService executorService = Executors.newFixedThreadPool(4);


        List<Callable<String>> executions = new ArrayList<>();
        for (String partition : partitions) {
            Callable<String> partitionExecution = () -> {
                step.execute(partition);
                return partition;
            };
            executions.add(partitionExecution);
        }

        executorService.invokeAll(executions);

Проблема в том, что сеанс hibernat почему-то недоступен в созданных потоках. Мы получаем следующее исключение:

org.hibernate.LazyInitializationException: 
failed to lazily initialize a collection of role: ..., could not initialize proxy - no Session

Если я удаляю в многопоточную часть (т.е. удаляю службу исполнителя), все работает нормально.

Мы уже попробовали следующее:

  • Использовать управляемый пружиной ThreadPoolTaskExecutor
  • Поместить @Transactional в начало метода / класса (который связан с другим классом и вызывается там, поэтому в основном должно работать)

Любые советы / предложения приветствуются:)

Ответы [ 2 ]

0 голосов
/ 21 февраля 2020

Я придумал рабочую версию. По сути, я позволил Spring порождать потоки самостоятельно, используя аннотацию Asyn c. Таким образом, создаваемые потоки получают присоединенный требуемый сеанс гибернации.

Я создал сервис Spring, который делегирует через асин c метод.

@Component
public AsyncDelegate {

    @Async
    @Transactional
    public Future delegate(Step step, String partition){
        step.execute(partition);
        return new AsyncResult(partition);
    }
}

И адаптировал исходный код следующим образом: это:

    @Autowired
    AsyncDelegate asyncDelegate;

    List<Future> executions = new ArrayList<>();
    for (String partition : partitions) {
        executions.add(asyncDelegate.delegate(step, partition));
    }
0 голосов
/ 20 февраля 2020

Короче говоря, я бы настоятельно рекомендовал не передавать управляемые объекты Hibernate в несколько потоков, поскольку это может вызвать:

  1. Ленивые проблемы инициализации (как вы встречали)
  2. Пропущенные обновления
  3. Исключения блокировки

Для получения более подробной информации в этом блоге объясняется, почему: https://xebia.com/blog/hibernate-and-multi-threading/

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

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