内部@Transactional回滚触发外部事务全回滚,为何未实现部分持久化?
问题原因分析
你的核心问题出在事务的传播行为上:
- 外层的
autoSynchPubMedJob方法被@Transactional标记,意味着整个循环执行过程处于一个全局大事务中 - 内部的
updatePublication方法默认使用REQUIRED传播行为(Spring事务默认规则),会直接加入外层的全局事务,而非开启独立事务 - 当某条记录触发异常时,虽然你在catch块里捕获了异常,但Spring已经把外层的全局事务标记为需要回滚的状态
- 循环结束后,全局事务提交时,因为已被标记回滚,所有之前成功执行的操作都会被一起回滚,最终导致没有数据持久化
解决方案
有两种靠谱的解决方式,可根据业务场景选择:
方案1:移除外层方法的@Transactional
直接去掉MailServiceImpl类上或autoSynchPubMedJob方法上的@Transactional注解,让内部的updatePublication方法各自管理独立事务:
@Service("mailService") @EnableScheduling // 移除类上的@Transactional public class MailServiceImpl implements MailService { @Override @Scheduled(cron = "${mail.cron.pubmed.autosynch.job}") // 方法上也无需添加@Transactional public void autoSynchPubMedJob() { //... 获取结果集 for (Result r: resultset) { try { pubService.updatePublication(r); } catch (Exception e) { log.error("Error on record: ", e); } } } }
这样每个updatePublication的事务都是独立的,某条记录出错回滚不会影响其他记录的提交。
方案2:修改内部方法的事务传播行为为REQUIRES_NEW
如果外层方法必须保留事务(比如存在其他需要事务的操作),可以把updatePublication方法的事务传播行为改成REQUIRES_NEW,强制开启新的独立事务:
@Override @Transactional(readOnly = false, rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW) public void updatePublication(Publication publication) throws Exception { dao.update1(..); dao.update2(..); if (publication.getId() == 123) { throw new Exception("Test Exception"); } }
这种方式下,每次调用updatePublication都会开启一个新事务,与外层事务完全隔离,出错时只会回滚当前这条记录的事务,其他记录的事务正常提交。
注意事项
- 不要在同一个类内部调用带有
REQUIRES_NEW的方法,Spring事务代理基于AOP实现,内部调用不会触发代理逻辑,事务配置会失效。你的代码是调用pubService的跨Service方法,因此不存在这个问题。 - 方案2会频繁开启新事务,带来一定性能开销,需根据数据量评估是否可接受。
内容的提问来源于stack exchange,提问作者gene b.
相关产品推荐
相关产品推荐

