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

时间:2014-04-24 10:54:41

标签: java multithreading spring java-8 java-stream

我尝试在带有Spring parallelStream()注释的DAO中使用@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 transaction manager and multithreading 但我希望Spring 4支持java 8可以为此提供一些解决方案。有什么想法吗?

2 个答案:

答案 0 :(得分:6)

好吧,我猜了几个猜测:

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

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

  • 删除所有延迟加载并再次尝试parallelStream();
  • 如果成功,您必须在执行parallelStream()之前完全加载实体。

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

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

答案 1 :(得分:2)

问题不在于并行流。在春季交易是使用AOP创建的。
Spring执行您的processCollection方法时,将为此创建一个代理对象,并开始事务。 调用同一类中的任何其他方法,即使您指定了@Transaction,spring也不会在New transaction中运行该方法。 要使其运行,请将该方法process()移至新服务,然后执行您的问题。您的程序将正常运行。