Java .parallelStream() with spring annoted methods

我尝试在DAO中使用带有Spring注释,并遇到了这样的问题:parallelStream()@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)

如何使用带注释的方法?@TransactionalparallelStream()

更新为什么会发生这种情况 Spring 事务管理器和多线程 但是我希望 spring 4 与 java 8 支持可以为此提供一些解决方案。有什么想法吗?


答案 1

好吧,我有一个猜测,包括几个猜测:

  • 您将会话管理策略设为session-per-thread;
  • Object你在例子中写的实际上是一些使用延迟加载的实体;
  • processOne()方法使用懒惰加载的实体属性;
  • 由于第一点,线程,启动 的没有可用的会话(可能在 中,不记得会话在技术上是如何绑定到线程的);parallelStream()ThreadLocal

这完全导致了您遇到的问题。这种行为对我来说看起来很奇怪,所以我建议执行以下操作:

  • 删除所有延迟加载,然后重试;parallelStream()
  • 如果成功,则必须在执行 之前完全加载实体。parallelStream()

另一种方法是:在执行 之前从会话中分离所有列表元素。parallelStream()

尽管正如Marko在注释中所写的那样,它不是线程安全的,因此这意味着您必须通过删除延迟加载或从会话中分离所有实体来摆脱使用。SessionSession


答案 2

问题不在于并行流。在春季,使用AOP创建事务。
当您的 processCollection 方法执行 spring 时,请创建一个此代理对象,然后启动事务。调用同一类中的另一个方法,spring 不会在 New 事务中运行该方法,即使您指定了 @Transaction。要让它运行,请将该方法 process() 移动到新服务,然后执行您的问题。您的程序将正常工作。