使用HibernateTransactionManager时@Transactional(REQUIRES_NEW)无法创建新事务
问题背景
正在重构遗留代码,将Spring配置从XML迁移至Java类配置。已通过TransactionConfiguration.java初始化事务管理器,应用可正常读写数据库,但在只读事务中调用标注@Transactional(propagation = Propagation.REQUIRES_NEW)的方法时,无法创建新事务。此前使用XML配置TransactionProxyFactoryBean时该功能正常。
相关配置代码
Java类配置(TransactionConfiguration.java)
@Configuration @EnableTransactionManagement public class TransactionConfiguration { private String[] packagesToScan = {...redacted...}; @Bean public DataSource dataSource(@Value("${db.driverClassName}") String driverClassName, @Value("${db.url}") String jdbcUrl, @Value("${db.usr.login}") String username, @Value("${db.usr.password}") String pwd, @Value("${db.maxPoolSize}") int maxPoolSize) { HikariDataSource dataSource = new HikariDataSource(); dataSource.setDriverClassName(driverClassName); dataSource.setJdbcUrl(jdbcUrl); dataSource.setUsername(username); dataSource.setPassword(pwd); dataSource.setMaximumPoolSize(maxPoolSize); return dataSource; } @Bean public LocalSessionFactoryBean sessionFactory(DataSource dataSource, @Value("${db.schema}") String dbSchema, @Value("${db.dialect}") String dbDialect, @Value("${db.show.sql}") Boolean dbShowSql, @Value("${db.hbm2ddl.auto}") String hbm2dll) { LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean(); sessionFactory.setDataSource(dataSource); sessionFactory.setPackagesToScan(packagesToScan); Properties hibernateProperties = new Properties(); hibernateProperties.put("hibernate.default_schema", dbSchema); hibernateProperties.put("hibernate.dialect", dbDialect); hibernateProperties.put("hibernate.show_sql", dbShowSql); hibernateProperties.put("hibernate.hbm2ddl.auto", hbm2dll); sessionFactory.setHibernateProperties(hibernateProperties); return sessionFactory; } @Bean public PlatformTransactionManager transactionManager(LocalSessionFactoryBean sessionFactory) { HibernateTransactionManager transactionManager = new HibernateTransactionManager(); transactionManager.setSessionFactory(sessionFactory.getObject()); return transactionManager; } }
服务调用场景
@Service public class Service1{ @Autowired private Service2 service2; @Transactional(readOnly = true) public void method1() { [... read only treatment...] service2.method2(); [... read only treatment...] } } @Service public class Service2{ @Autowired private Service3 service3; public void method2() { [... read only treatment...] method3(); [... read only treatment...] } private void method3() { [... read only treatment...] service3.method4(); [... read only treatment...] } } @Service public class Service3{ @Value("${my.test.prop}") private String myTestProp; @Transactional(propagation = Propagation.REQUIRES_NEW) public void method4() { System.out.println(myTestProp); [... write treatment...] } }
原XML配置(正常工作)
<bean class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager" ref="transactionManager" /> <property name="transactionAttributes"> <props> <prop key="method1">PROPAGATION_REQUIRED,readOnly</prop> <prop key="method2">PROPAGATION_REQUIRES_NEW</prop> </props> </property> </bean>
已尝试的排查步骤
- 确认方法分属不同类,保证Spring AOP代理生效
- 开启Logback日志,未发现新事务创建的日志记录
- 尝试切换至
JPATransactionManager,问题依旧 - 尝试使用
Propagation.NESTED传播行为,未解决问题 - 环境版本:Spring 5.3.6,Hibernate 5.4.33
问题分析与解决方案
1. 事务管理器配置错误
当前transactionManager Bean的参数是LocalSessionFactoryBean,直接调用getObject()获取SessionFactory会绕过Spring的Bean生命周期管理,可能导致SessionFactory未被正确初始化或代理。正确做法是将参数改为SessionFactory,Spring会自动注入LocalSessionFactoryBean生成的SessionFactory实例:
@Bean public PlatformTransactionManager transactionManager(SessionFactory sessionFactory) { HibernateTransactionManager transactionManager = new HibernateTransactionManager(); transactionManager.setSessionFactory(sessionFactory); return transactionManager; }
2. 包扫描范围遗漏
检查TransactionConfiguration中的packagesToScan是否包含Service3所在的包。如果Service3不在扫描范围内,Spring无法识别其@Transactional注解,不会生成事务代理。
3. 事务代理模式问题
@EnableTransactionManagement默认使用JDK动态代理(proxyTargetClass=false),如果Service3没有实现任何接口,需要设置proxyTargetClass=true强制使用CGLIB代理:
@Configuration @EnableTransactionManagement(proxyTargetClass = true) public class TransactionConfiguration { // ... 其他配置 }
4. 日志级别不足
将Spring事务相关日志级别设为DEBUG,查看是否有事务拦截器的执行日志,确认method4是否被事务切面处理:
<!-- Logback配置示例 --> <logger name="org.springframework.transaction" level="DEBUG"/> <logger name="org.springframework.aop.framework" level="DEBUG"/>
内容的提问来源于stack exchange,提问作者TonioLeParigo

