Spring4.3+Hibernate4多线程下ADBAdapter遇SessionClosed异常的解决方法
这个问题的核心原因很明确:Hibernate Session是线程不安全的,且默认和当前线程绑定。当父线程的事务结束(或者请求处理完成),Spring会自动关闭绑定的Session;如果你的ADBAdapter在父线程中获取了这个Session并缓存起来,在子线程中复用这个已经被关闭的Session,就会抛出SessionClosed异常。
下面是几个针对性的解决方案,按推荐程度排序:
1. 在每次操作时动态获取Session(最稳妥的基础方案)
修改你的ADBAdapter,不要在构造函数中缓存Session实例,而是在每个业务方法里从SessionFactory获取新的Session,并确保用完后正确关闭(可以用try-with-resources自动关闭):
public class ADBAdapter { private final SessionFactory sessionFactory; // 构造函数注入SessionFactory,保持不变 public ADBAdapter(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } public void doYourBusinessLogic() { // 每次操作都获取新的Session try (Session session = sessionFactory.openSession()) { // 执行你的Hibernate操作,比如: Query query = session.createQuery("from YourEntity"); List<YourEntity> results = query.list(); // ...其他逻辑 } catch (HibernateException e) { // 处理异常,比如日志记录或抛出业务异常 e.printStackTrace(); } } }
这种方式完全避免了Session的线程共享问题,每个线程的操作都使用独立的Session,用完即关,不会和父线程的Session生命周期绑定。
2. 利用Spring事务管理为子线程创建独立Session(更贴合Spring生态的方案)
如果你的项目已经在使用Spring的事务管理,可以通过@Transactional注解让Spring为子线程自动创建并管理Session:
第一步:检查Hibernate配置
确保hibernate.cfg.xml中配置了Spring的Session上下文:
<property name="hibernate.current_session_context_class">org.springframework.orm.hibernate4.SpringSessionContext</property>
第二步:修改ADBAdapter的方法,添加事务注解
public class ADBAdapter { private final SessionFactory sessionFactory; public ADBAdapter(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } // 添加@Transactional,Spring会为该方法创建独立的Session和事务 @Transactional(propagation = Propagation.REQUIRES_NEW) public void doYourBusinessLogic() { // 使用getCurrentSession()获取Spring管理的Session Session session = sessionFactory.getCurrentSession(); Query query = session.createQuery("from YourEntity"); List<YourEntity> results = query.list(); // ...其他逻辑 } }
这里Propagation.REQUIRES_NEW确保子线程的操作会开启一个全新的事务,不会依赖父线程的事务上下文,从而获取到独立的、有效的Session。
第三步:确保子线程的方法被Spring代理
注意:如果你的ADBAdapter是单例Bean,直接在子线程中调用方法时,@Transactional注解需要通过Spring代理生效。如果是直接new的实例,注解不会生效,所以要确保子线程中使用的ADBAdapter是从Spring容器中获取的Bean。
3. 避免缓存Session实例(排查代码的关键)
检查你的ADBAdapter代码,是否存在类似这样的错误写法:
// 错误示例:缓存父线程的Session,导致子线程复用已关闭的Session public class ADBAdapter { private Session session; public ADBAdapter(SessionFactory sessionFactory) { // 在父线程构造时获取Session,子线程复用就会出问题 this.session = sessionFactory.getCurrentSession(); } }
这种写法是绝对要避免的,Session不能被缓存或跨线程共享,必须在需要的时候动态获取。
关键注意点
- Hibernate Session绝对不能跨线程共享,它的设计就是线程不安全的,每个线程应该使用自己的Session实例。
- 如果使用Spring的事务管理,一定要确保事务注解的方法是通过Spring代理调用的,否则注解不会生效。
- 不要依赖父线程的Session或事务上下文到子线程中,父线程的事务结束后,对应的Session会被Spring自动关闭。
内容的提问来源于stack exchange,提问作者Julian Rost

