一、問題情況:
1.項目中使用的多數據源,各個方法上加了@DataSource注解,對於批量操作,需要做使用事務保持批操作的一致性。
2.運行測試時,發現insert報錯,數據源並未切換至second。
3.核心代碼如下:
@Service("chargeAccountDetailService") public class ChargeAccountDetailServiceImpl implements ChargeAccountDetailService { @Autowired ChargeAccountDetailMapper detailMapper; @DataSource(name = "second") @Transactional(rollbackFor = Exception.class) @Override public int insertAccountDetailBatch(Long accountId, List<ChargeAccountDetail> list, String tenant) { return detailMapper.insertAccountDetailBatch(accountId, list, tenant); } }
@Override @Transactional(rollbackFor = Exception.class) public void run(Long[] jobIds) { for(Long jobId : jobIds){ ScheduleUtils.run(scheduler, this.selectById(jobId)); } }
二、相關疑問:
1.其他的未加事務的數據源切換都是正常的,為什么加上@Transactional(rollbackFor = Exception.class)注解會導致切換數據源失敗?
@Transaction的默認propagation=Propagation.REQUIRED,即事務的傳播屬性是,存在事務加入當前事務,不存在就創建事務,
因為在開啟事務的同時,會從數據庫連接池獲取數據庫連接。但是事務@Transaction的默認傳播屬性,並不是新建一個事務,也就沒有重新獲取數據源連接。
而在事務內的所有數據庫操作,都是在事務連接建立之后進行的,所以會產生數據源沒有切換的問題。
2.如何保證數據源切換正常,且事務正常?
要切換數據源,則必須是一個新的事務,所以需要修改事務的傳播數據,使其創建一個新的事務,獲取新的數據庫連接。
可以在
@Trasactional注解上修改propagation = Propagation.REQUIRES_NEW
三、解決問題:
1.使用@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)即可,代碼如下:
@Service("chargeAccountDetailService") public class ChargeAccountDetailServiceImpl implements ChargeAccountDetailService { @Autowired ChargeAccountDetailMapper detailMapper; @DataSource(name = "second") @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class) @Override public int insertAccountDetailBatch(Long accountId, List<ChargeAccountDetail> list, String tenant) { return detailMapper.insertAccountDetailBatch(accountId, list, tenant); } }
@Transactional(propagation = Propagation.REQUIRES_NEW)
表示新建事務,如果當前存在事務,把當前事務掛起。
四、擴展:
1.對於切換數據源的方法,需要使用事務,請盡可能使用@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)。
2.多數據源切換,方法調用先后順需,內部方法切換數據源等見參考連接內容。