Spring 6/Hibernate 6升级后SessionFactory无CurrentSessionContext配置问题
Hibernate 6.6 + Spring 6.2 会话管理升级问题解析
一、旧版本(Spring 5.3 + Hibernate 5.6)工作原理
你之前的实现能正常工作,核心是Spring对SessionFactory的代理机制:
- 通过
LocalContainerEntityManagerFactoryBean创建的SessionFactory并非Hibernate原生实例,而是Spring生成的代理对象。 - 当进入
@Transactional方法时,Spring事务管理器会通过HibernateJpaDialect自动创建Session并绑定到Spring的会话上下文。 - 调用
sessionFactory.getCurrentSession()时,Spring代理会拦截该方法,直接返回当前事务上下文里已打开的Session,无需依赖Hibernate原生的会话上下文配置。
二、升级后失效的原因
Spring 6.2对JPA容器的代理逻辑做了调整:
- 直接注入的
SessionFactory现在是Hibernate原生的SessionFactoryImpl,不再被Spring代理。 - 此时调用
getCurrentSession()只会使用Hibernate自身的会话上下文(比如你配置的thread),但Spring的事务管理是绑定到JPA EntityManager上下文的,两者没有关联,所以无法获取事务内已打开的Session。 - 而
@PersistenceContext注入的Session本质是Spring管理的EntityManager的Hibernate Session包装,天然和事务上下文绑定,因此能正常工作。
三、恢复原有工作方式的可行方案
方案1:配置Hibernate使用Spring会话上下文
在persistence.xml的持久化单元中添加以下配置,让Hibernate直接使用Spring的会话上下文实现,打通和Spring事务的关联:
<persistence-unit name="JPATest" > <!-- 原有配置 --> <properties> <property name="hibernate.current_session_context_class" value="org.springframework.orm.hibernate6.SpringSessionContext"/> </properties> </persistence-unit>
注意:Hibernate 6对应的Spring会话上下文类路径是
org.springframework.orm.hibernate6.SpringSessionContext,Hibernate 5是org.springframework.orm.hibernate5.SpringSessionContext,请匹配你的版本。
方案2:让Spring代理原生SessionFactory
通过Spring的ProxyFactoryBean为原生SessionFactory创建代理,保留Spring对getCurrentSession()的拦截逻辑:
<!-- 原有的LocalContainerEntityManagerFactoryBean,改个ID --> <bean id="originalSessionFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="persistenceUnitName" value="JPATest" /> <property name="persistenceXmlLocation" value="classpath:persistence.xml" /> <property name="dataSource" ref="dataSource" /> <property name="jpaVendorAdapter" ref="vendorAdapter" /> <property name="jpaDialect"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/> </property> </bean> <!-- 创建Spring代理的SessionFactory --> <bean id="sessionFactory" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="originalSessionFactory"/> <property name="interceptorNames"> <list> <value>org.springframework.orm.hibernate6.HibernateInterceptor</value> </list> </property> </bean>
之后保持原有@Resource(name = "sessionFactory")的注入方式即可,代理后的SessionFactory会在事务内返回正确的Session。
方案3:自定义SessionFactory包装类
如果无法修改注入点或配置,可以编写一个包装类,内部通过Spring的SessionFactoryUtils获取事务绑定的Session:
public class SpringManagedSessionFactory implements SessionFactory { private final SessionFactory delegate; public SpringManagedSessionFactory(SessionFactory delegate) { this.delegate = delegate; } @Override public Session getCurrentSession() throws HibernateException { // 利用Spring工具类获取事务内的Session return SessionFactoryUtils.getCurrentSession(delegate); } // 其他SessionFactory方法直接委托给原生实例 @Override public Session openSession() throws HibernateException { return delegate.openSession(); } // 实现SessionFactory接口的所有其他方法... }
然后在配置中替换原有的SessionFactory bean:
<bean id="sessionFactory" class="your.package.SpringManagedSessionFactory"> <constructor-arg ref="originalSessionFactory"/> </bean> <bean id="originalSessionFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <!-- 原有的配置 --> </bean>
内容的提问来源于stack exchange,提问作者Steven Miller
相关产品推荐
相关产品推荐

