异常场景下如何关闭并重新打开Hibernate Session
哥们,针对你这个Spring+Hibernate结合open-session-in-conversation-filter的场景,我来给你捋捋怎么处理异常时的Session重建问题:
核心思路先明确
你当前的模式下,Session是和请求会话绑定的,但因为每次插入/更新都单独提交事务,一旦某步操作抛异常,当前Session可能处于脏状态(比如有未完成的持久化操作残留),这时候必须先清理掉旧Session,再重建新的才能继续后续操作。
方案一:手动干预Session生命周期(适合局部异常处理)
如果只是在特定操作的异常捕获里处理,可以直接借助Spring的SessionFactoryUtils和TransactionSynchronizationManager来手动管理:
try { // 你的插入/更新操作逻辑 someDao.insertEntity(entity); } catch (SQLException | HibernateException e) { // 第一步:清理异常状态的旧Session Session currentSession = SessionFactoryUtils.getSession(sessionFactory, false); if (currentSession != null && currentSession.isOpen()) { // 先解绑当前线程绑定的Session资源 if (TransactionSynchronizationManager.hasResource(sessionFactory)) { TransactionSynchronizationManager.unbindResource(sessionFactory); } // 关闭Session,释放数据库连接 SessionFactoryUtils.closeSession(currentSession); } // 第二步:创建并绑定新Session到当前线程 Session newSession = sessionFactory.openSession(); TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(newSession)); // 这里可以继续后续的数据库操作,或者抛出异常让上层处理 throw new BusinessException("操作失败,已重建Session", e); }
方案二:自定义事务拦截器(全局统一处理)
因为你用的是TransactionProxyFactoryBean做声明式事务,不如把Session的清理和重建逻辑放到事务拦截器的异常回调里,这样所有事务方法的异常都能统一处理:
- 先自定义一个继承自
TransactionInterceptor的拦截器:
public class SessionRecoveryTransactionInterceptor extends TransactionInterceptor { @Override protected Object invokeWithinTransaction(Method method, Class<?> targetClass, InvocationCallback invocation) throws Throwable { try { return super.invokeWithinTransaction(method, targetClass, invocation); } catch (Exception e) { // 触发异常时清理旧Session SessionFactory sessionFactory = getTransactionManager().getSessionFactory(); Session currentSession = SessionFactoryUtils.getSession(sessionFactory, false); if (currentSession != null && currentSession.isOpen()) { // 强制回滚未提交的事务(如果有的话) if (currentSession.getTransaction().isActive()) { currentSession.getTransaction().rollback(); } SessionFactoryUtils.closeSession(currentSession); } // 重建新Session并绑定 Session newSession = sessionFactory.openSession(); TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(newSession)); // 继续抛出异常,不影响上层的异常处理逻辑 throw e; } } }
- 然后在Spring配置里把
TransactionProxyFactoryBean的transactionInterceptor替换成这个自定义的:
<bean id="customTransactionInterceptor" class="com.yourpackage.SessionRecoveryTransactionInterceptor"> <property name="transactionManager" ref="transactionManager"/> <property name="transactionAttributes"> <props> <prop key="insert*">PROPAGATION_REQUIRED,rollback-for=java.lang.Exception</prop> <prop key="update*">PROPAGATION_REQUIRED,rollback-for=java.lang.Exception</prop> </props> </property> </bean> <bean id="yourServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="target" ref="yourService"/> <property name="transactionInterceptor" ref="customTransactionInterceptor"/> </bean>
关键注意事项
- 一定要确保旧Session关闭前,所有未完成的事务都已回滚,不然会导致数据库连接泄漏或者数据不一致
- 重建Session后,要保证后续的DAO操作能正确获取到新的Session(Spring的
HibernateDaoSupport或者SessionFactory.getCurrentSession()会自动从线程上下文拿,所以没问题) - 尽量避免频繁手动重建Session,优先通过合理的事务边界设计(比如把相关操作放到同一个事务里)来减少异常场景的发生
内容的提问来源于stack exchange,提问作者Mayur
相关产品推荐
相关产品推荐

