带有spring注释方法的Java .parallelStream()

Vla*_*adS 13 java spring multithreading java-8 java-stream

我尝试使用parallelStream()带有Spring @Transactional注释的DAO 并得到这样的问题:

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

@Transactional
public void processOne(Object o) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

工作正确:

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

@Transactional
public void processOne(Object o) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

例外:

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)
Run Code Online (Sandbox Code Playgroud)

我如何使用带@Transactional注释的方法parallelStream()

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

Ale*_*lev 6

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

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

这完全导致了你的问题.这个行为对我来说很奇怪,所以我建议你做以下事情:

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

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

虽然Marko在评论中写道,但Session它不是线程安全的,所以这意味着你必须Session通过删除延迟加载或从会话中分离所有实体来消除使用.

  • Hibernate的会话不是线程安全的.唯一的选择是脱离. (5认同)

小智 6

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