带有spring注释方法的Java .parallelStream()
我尝试使用带有Spring @Transactional
注释的DAO中的parallelStream()
并得到如此问题:
@Transactional public void processCollection(Collection objects) { objects.parallelStream() .forEach(this::processOne); //throw exception } @Transactional public void processOne(Object o) { ... }
工作正确:
@Transactional public void processCollection(Collection 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)
如何通过parallelStream()
使用@Transactional
注释方法?
更新为什么会发生这种情况Spring事务管理器和multithreading但是我希望Spring 4支持java 8可以为此提供一些解决方案。 有任何想法吗?
好吧,我有一个猜测包括几个猜测:
- 您将会话管理策略作为
session-per-thread
; - 您在示例中编写的
Object
实际上是一些使用延迟加载的实体; -
processOne()
方法使用懒惰加载的实体属性; - 因为第一点,为
parallelStream()
启动的线程没有可用的会话(可能在ThreadLocal
,不记得技术上会话如何绑定到线程);
这完全导致了你的问题。 这种行为对我来说很奇怪,所以我建议你做以下事情:
- 删除所有延迟加载并再次尝试
parallelStream()
; - 如果成功,则必须在执行
parallelStream()
之前完全加载实体。
另一种方法:在执行parallelStream()
之前从会话中分离所有列表元素。
虽然Marko在评论中写道,但Session
不是线程安全的,因此这意味着您必须通过删除延迟加载或从会话中分离所有实体来消除Session
使用。
问题不在于并行流。spring事务是使用AOP创建的。
当你的processCollection方法被执行时,spring创建一个这样的代理对象并启动事务。 在同一个类中调用任何其他方法,即使您指定了@Transaction,spring也不会在New事务中运行该方法。 为了让它运行,将该方法process()移动到新服务然后执行你的问题。你的程序将正常工作。