You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring5→6、Hibernate5→6迁移后单元测试无法创建事务管理器

问题背景

将运行在Tomcat 9.5上的Web应用迁移至Tomcat 10,已完成javax.*到jakarta.*的包重命名,基于Java 17编译,使用Spring 6.0.22 + Hibernate Core 6.3.5.Final,持久化单元测试无法正常运行。

当前Spring上下文配置(XML):

<beans>    
<bean id="persistenceDataSource" class="com.infogix.component.kernel.export.db.BasicDataSource"
          destroy-method="close"
          p:driverClassName="${db.driver}" p:url="${db.url}"
          p:username="${db.user}" p:password="${db.password}"/>

    <bean id="hibernateProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
        <property name="properties">
            <props>
                <prop key="hibernate.event.merge.entity_copy_observer">allow</prop>
                <prop key="hibernate.show_sql">${db.hibernate.show_sql}</prop>
                <prop key="hibernate.id.optimizer.pooled.preferred">org.hibernate.id.enhanced.PooledLoOptimizer</prop>
                <prop key="hibernate.id.new_generator_mappings">false</prop>
                <prop key="hibernate.id.generator.stored_last_used">false</prop>
                <prop key="hibernate.current_session_context_class">jta</prop>
            </props>
        </property>
    </bean>

    <bean id="entityManagerFactory"
          class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="persistenceDataSource"/>
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
            </bean>
        </property>
        <property name="jpaProperties" ref="hibernateProperties"/>
        <property name="loadTimeWeaver">
            <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
        </property>
        <property name="packagesToScan">
            <list>
                <value>com.infogix.component.**.export.fdt.**</value>
                <value>com.infogix.component.**.rsc.impl.**</value>
                <value>com.infogix.component.datastore.ent.impl.**</value>
            </list>
        </property>
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>

    <tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/>

    <bean id="persistenceExceptionTranslationPostProcessor" class=
            "org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
</beans>

单元测试配置:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"/persistence-test-context.xml"})

错误信息:
org.hibernate.HibernateException: No TransactionManagerLookup specified

核心情况:

  • 单元测试为独立运行,不依赖Tomcat或其他容器
  • 仅使用单个Postgres 10数据库,不使用JTA跨库事务
  • 原Spring5+Hibernate5环境中,注入SessionFactory操作数据库
  • 大量使用Spring的@Transactional注解

疑问:

  1. 单元测试未在容器内运行,是否应该使用LocalContainerEntityManagerFactoryBean?这里的'Container'指完整Jakarta EE容器还是Tomcat这类Servlet容器?
  2. 若应使用该Bean,如何正确配置以创建事务管理器?
  3. 应该注入SessionFactory还是EntityManager?

解决方案与疑问解答

1. 是否应该使用LocalContainerEntityManagerFactoryBean?

完全可以用。这里的"Container"指的是Spring容器,而非Jakarta EE完整容器或Tomcat这类Servlet容器。LocalContainerEntityManagerFactoryBean是Spring中最灵活的JPA工厂Bean,支持包扫描、自定义数据源、JPA属性配置等,不管是容器内还是独立环境(比如单元测试)都适用,尤其适合你这种依赖注解扫描实体类的场景。

2. 正确配置事务管理器的步骤

你的核心问题是Hibernate配置中指定了hibernate.current_session_context_class=jta,但又没有提供JTA事务管理器,导致报错。因为你不使用JTA,需要修改Hibernate配置,并调整Spring事务相关配置:

步骤1:修改Hibernate属性

删除hibernate.current_session_context_class=jta这一行(Spring会自动接管会话上下文,无需手动指定JTA相关配置)。修改后的hibernateProperties配置:

<bean id="hibernateProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
    <property name="properties">
        <props>
            <prop key="hibernate.event.merge.entity_copy_observer">allow</prop>
            <prop key="hibernate.show_sql">${db.hibernate.show_sql}</prop>
            <prop key="hibernate.id.optimizer.pooled.preferred">org.hibernate.id.enhanced.PooledLoOptimizer</prop>
            <prop key="hibernate.id.new_generator_mappings">false</prop>
            <prop key="hibernate.id.generator.stored_last_used">false</prop>
            <!-- 移除jta相关配置 -->
        </props>
    </property>
</bean>

步骤2:确认JpaTransactionManager配置

你的JpaTransactionManager配置本身是正确的,它会自动适配本地事务(非JTA),只要EntityManagerFactory基于本地数据源配置,就能正常工作。无需引入Narayana或WildFly的JTA依赖,这些是多余的。

步骤3:调整LoadTimeWeaver(可选)

如果单元测试中不需要类加载时织入(比如没有使用JPA实体类增强),可以移除loadTimeWeaver配置,避免可能的类加载问题:

<!-- 移除这一段 -->
<property name="loadTimeWeaver">
    <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
</property>

如果确实需要织入,确保测试启动时添加了JavaAgent参数(比如-javaagent:spring-instrument.jar)。

3. 注入SessionFactory还是EntityManager?

两种方式都支持,但推荐优先使用EntityManager,这是JPA的标准API,更符合Spring 6+和Hibernate 6的最佳实践。如果旧代码大量依赖SessionFactory,也可以通过EntityManager获取Hibernate Session,或者直接从EntityManagerFactory中提取SessionFactory:

方式1:注入EntityManager(推荐)

@PersistenceContext
private EntityManager entityManager;

// 需要时获取Hibernate Session
Session session = entityManager.unwrap(Session.class);

方式2:注入SessionFactory

如果要保留原有代码习惯,可以在Spring配置中暴露SessionFactory Bean:

<bean id="sessionFactory" factory-bean="entityManagerFactory" factory-method="unwrap">
    <constructor-arg value="org.hibernate.SessionFactory"/>
</bean>

然后在代码中注入:

@Autowired
private SessionFactory sessionFactory;

注意事项

不管用哪种方式,Spring的@Transactional注解都能正常生效,因为JpaTransactionManager同时支持EntityManager和SessionFactory的事务管理。

最终验证

修改配置后,单元测试应该能正常启动,@Transactional注解会自动管理本地数据库事务,无需JTA相关依赖。

内容的提问来源于stack exchange,提问作者Swavek L

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 16:24:49