Java事务管理-传播特性:@Transactional(REQUIRES_NEW)方法技术咨询
从你给出的代码片段来看,虽然批量处理的逻辑没写完,但咱们可以结合@Transactional(propagation = Propagation.REQUIRES_NEW)的特性,拆解这个方法里的关键点和潜在问题:
先搞懂REQUIRES_NEW的核心逻辑
咱们先把这个注解的本质说清楚:Propagation.REQUIRES_NEW 是强制开启独立新事务的传播特性——哪怕当前线程已经有一个正在运行的事务,它也会把旧事务临时挂起,自己启动一个完全独立的新事务。等这个方法执行完毕(不管是提交还是回滚),再把旧事务恢复继续执行。
这种特性适合那些需要和主事务完全隔离的操作,比如日志记录、状态更新必须确保成功,不受主事务回滚影响的场景。
你的代码里可能存在的问题点
1. 手动获取数据库连接的风险
你代码里直接调用dataSource.getConnection()获取连接,这里要踩坑:Spring的事务管理是基于连接绑定的——它会把当前事务的连接绑定到线程上下文里。你手动获取的连接大概率不在这个REQUIRES_NEW事务的上下文里,这会导致两个问题:
- 后面的
em.merge(invoice)操作和PDF生成用的不是同一个连接,事务的一致性无法保证; - 如果忘记手动关闭连接,很容易造成连接池泄漏,拖垮数据库。
如果必须手动用连接,一定要用try-with-resources自动关闭:
try (Connection connection = dataSource.getConnection()) { jasperConvert.jInvoicePdf(invoice, connection); } catch (SQLException e) { // 这里可以加异常处理,确保事务能正确回滚 }
更推荐的方式是让jasperConvert直接使用Spring管理的连接,比如注入DataSource或者让Spring帮你处理连接的生命周期。
2. 批量操作的事务边界问题
从if(i % batchS...的片段能看出来你想做批量更新,但整个jasperToInvoice1方法是一个单独的REQUIRES_NEW事务。如果批量处理的发票数量很大,会有两个隐患:
- 单个事务处理大量数据,会占用数据库的事务日志资源,甚至触发数据库的事务大小限制;
- 中间某一步出错(比如某张发票PDF生成失败),整个事务会回滚,所有已经生成PDF并更新
isPdf的发票都会被还原,这可能不是你想要的结果。
如果需要分批提交,建议把单张(或小批量)发票的更新逻辑拆成单独的事务方法,比如:
@Transactional(propagation = Propagation.REQUIRES_NEW) public void processSingleInvoice(TInvoiceSummary invoice, Connection connection) { jasperConvert.jInvoicePdf(invoice, connection); invoice.setIsPdf(true); em.merge(invoice); }
这样每处理一张发票就提交一次独立事务,避免全量回滚。
3. 异常回滚的配置缺失
你的方法声明抛出ClassNotFoundException, SQLException, JRException这些检查型异常,但Spring的@Transactional默认只对RuntimeException和Error触发事务回滚。如果这些检查型异常发生时,你希望当前REQUIRES_NEW事务回滚,必须显式配置rollbackFor属性:
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = {ClassNotFoundException.class, SQLException.class, JRException.class})
不然的话,就算PDF生成失败抛出了JRException,事务还是会提交,导致发票的isPdf被错误地设为true,出现数据不一致。
4. EntityManager的事务同步问题
你用em.merge(invoice)更新发票状态,要确保这个EntityManager是Spring注入的实例,而不是手动创建的。手动创建的EntityManager不会和当前的REQUIRES_NEW事务绑定,导致更新操作不会被事务管理,就算方法执行完,更新也可能不会提交到数据库。
优化总结
- 尽量避免手动获取数据库连接,依赖Spring的事务连接管理;
- 批量操作时拆分事务边界,避免大事务带来的风险;
- 显式配置
rollbackFor,确保异常能触发事务回滚; - 确保EntityManager等持久化对象由Spring管理,和事务上下文同步。
内容的提问来源于stack exchange,提问作者Arifin Rochmawan

