Java.parallelStream() с spring аннотированными методами

Я пытаюсь использовать parallelStream() в DAO с аннотациями Spring @Transactional и получить такую ​​проблему:

@Transactional
public void processCollection(Collection<Object> objects) {
    objects.parallelStream()
            .forEach(this::processOne);  //throw exception
}

@Transactional
public void processOne(Object o) {
    ...
}

Правильно работает:

@Transactional
public void processCollection(Collection<Object> objects) {
    objects.stream()
            .forEach(this::processOne);  //work correctly
}

@Transactional
public void processOne(Object o) {
    ...
}

Исключение:

org.hibernate.HibernateException: No Session found for current thread
org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:106)
org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:978)

Как использовать @Transactional аннотированные методы parallelStream()?

Обновление Почему это происходит Spring менеджер транзакций и многопоточность Но я надеюсь, что Spring 4 с поддержкой java 8 может предоставить некоторое решение для этого. Любые идеи?

Ответ 1

Ну, у меня есть предположение, состоящее из нескольких догадок:

  • У вас политика управления сеансом как session-per-thread;
  • Object, который вы написали в примере, на самом деле является некоторой сущностью, которая использует ленивую загрузку;
  • processOne() метод использует свойства объекта, которые загружаются лениво;
  • Из-за первой точки потоки, запущенные для parallelStream(), не имеют сеанса (возможно, в ThreadLocal, не помните, как технически сеансы привязаны к потокам);

Это вызывает проблемы. Поведение выглядит довольно странно для меня, поэтому я предлагаю сделать следующее:

  • Удалите всю ленивую загрузку и повторите попытку parallelStream();
  • Если это удастся, вам нужно будет полностью загрузить объекты перед выполнением parallelStream().

Альтернативный способ: отделить все элементы списка от сеанса до выполнения parallelStream().

Хотя Marko писал в комментариях, Session не является потокобезопасным, поэтому вы должны избавиться от использования Session либо путем удаления ленивой загрузки, либо путем отсоединения всех сущностей от сеанса.