Spring 事務(wù)失效的 8 種場(chǎng)景!

在日常工作中,如果對(duì)Spring的事務(wù)管理功能使用不當(dāng),則會(huì)造成Spring事務(wù)不生效的問(wèn)題。而針對(duì)Spring事務(wù)不生效的問(wèn)題,也是在跳槽面試中被問(wèn)的比較頻繁的一個(gè)問(wèn)題。
點(diǎn)擊上方卡片關(guān)注我
今天,我們就一起梳理下有哪些場(chǎng)景會(huì)導(dǎo)致Spring事務(wù)生效。
Spring事務(wù)不生效總覽
簡(jiǎn)單來(lái)說(shuō),Spring事務(wù)會(huì)在幾種特定的場(chǎng)景下失效,如下圖所示。

1.數(shù)據(jù)庫(kù)不支持事務(wù)
Spring事務(wù)生效的前提是所連接的數(shù)據(jù)庫(kù)要支持事務(wù),如果底層的數(shù)據(jù)庫(kù)都不支持事務(wù),則Spring的事務(wù)肯定會(huì)失效。例如,如果使用的數(shù)據(jù)庫(kù)為MySQL,并且選用了MyISAM存儲(chǔ)引擎,則Spring的事務(wù)就會(huì)失效。
2.事務(wù)方法未被Spring管理
如果事務(wù)方法所在的類(lèi)沒(méi)有加載到Spring IOC容器中,也就是說(shuō),事務(wù)方法所在的類(lèi)沒(méi)有被Spring管理,則Spring事務(wù)會(huì)失效,示例如下。
public?class?ProductService?{
?@Autowired
?private?ProductDao?productDao;
?@Transactional(propagation?=?Propagation.REQUIRES_NEW)
?public?void?updateProductStockCountById(Integer?stockCount,?Long?id){
??productDao.updateProductStockCountById(stockCount,?id);
?}
}
ProductService類(lèi)上沒(méi)有標(biāo)注@Service注解,Product的實(shí)例沒(méi)有加載到Spring IOC容器中,就會(huì)造成updateProductStockCountById()方法的事務(wù)在Spring中失效。
3.方法沒(méi)有被public修飾
如果事務(wù)所在的方法沒(méi)有被public修飾,此時(shí)Spring的事務(wù)會(huì)失效,例如,如下代碼所示。
@Service
public?class?ProductService?{
?@Autowired
?private?ProductDao?productDao;
?@Transactional(propagation?=?Propagation.REQUIRES_NEW)
?private?void?updateProductStockCountById(Integer?stockCount,?Long?id){
??productDao.updateProductStockCountById(stockCount,?id);
?}
}
雖然ProductService上標(biāo)注了@Service注解,同時(shí)updateProductStockCountById()方法上標(biāo)注了@Transactional(propagation = Propagation.REQUIRES_NEW)注解。
但是,由于updateProductStockCountById()方法為內(nèi)部的私有方法(使用private修飾),那么此時(shí)updateProductStockCountById()方法的事務(wù)在Spring中會(huì)失效。
4.同一類(lèi)中方法調(diào)用
如果同一個(gè)類(lèi)中的兩個(gè)方法分別為A和B,方法A上沒(méi)有添加事務(wù)注解,方法B上添加了 @Transactional事務(wù)注解,方法A調(diào)用方法B,則方法B的事務(wù)會(huì)失效。例如,如下代碼所示。
@Service
public?class?OrderService?{
?@Autowired
?private?OrderDao?orderDao;
?@Autowired
?private?ProductDao?productDao;
?public?void?submitOrder(){
??//生成訂單
??Order?order?=?new?Order();
??long?number?=?Math.abs(new?Random().nextInt(500));
??order.setId(number);
??order.setOrderNo("order_"?+?number);
??orderDao.saveOrder(order);
??//減庫(kù)存
??this.updateProductStockCountById(1,?1L);
?}
?@Transactional(propagation?=?Propagation.REQUIRES_NEW)
?public?void?updateProductStockCountById(Integer?stockCount,?Long?id){
??productDao.updateProductStockCountById(stockCount,?id);
?}
}
submitOrder()方法和updateProductStockCountById()方法都在OrderService類(lèi)中,submitOrder()方法上沒(méi)有標(biāo)注事務(wù)注解,updateProductStockCountById()方法上標(biāo)注了事務(wù)注解,submitOrder()方法調(diào)用了updateProductStockCountById()方法,此時(shí),updateProductStockCountById()方法的事務(wù)在Spring中會(huì)失效。
5.未配置事務(wù)管理器
如果在項(xiàng)目中沒(méi)有配置Spring的事務(wù)管理器,即使使用了Spring的事務(wù)管理功能,Spring的事務(wù)也不會(huì)生效。
例如,沒(méi)有在項(xiàng)目的配置類(lèi)中配置如下代碼。
@Bean
public?PlatformTransactionManager?transactionManager(DataSource?dataSource)?{
?return?new?DataSourceTransactionManager(dataSource);
}
此時(shí),Spring的事務(wù)就會(huì)失效。
6.方法的事務(wù)傳播類(lèi)型不支持事務(wù)
如果內(nèi)部方法的事務(wù)傳播類(lèi)型為不支持事務(wù)的傳播類(lèi)型,則內(nèi)部方法的事務(wù)在Spring中會(huì)失效。
例如,如下代碼所示。
@Service
public?class?OrderService?{
?@Autowired
?private?OrderDao?orderDao;
?@Autowired
?private?ProductDao?productDao;
?@Transactional(propagation?=?Propagation.REQUIRED)
?public?void?submitOrder(){
??//生成訂單
??Order?order?=?new?Order();
??long?number?=?Math.abs(new?Random().nextInt(500));
??order.setId(number);
??order.setOrderNo("order_"?+?number);
??orderDao.saveOrder(order);
??//減庫(kù)存
??this.updateProductStockCountById(1,?1L);
?}
?@Transactional(propagation?=?Propagation.NOT_SUPPORTED)
?public?void?updateProductStockCountById(Integer?stockCount,?Long?id){
??productDao.updateProductStockCountById(stockCount,?id);
?}
}
由于updateProductStockCountById()方法的事務(wù)傳播類(lèi)型為NOT_SUPPORTED,不支持事務(wù),則updateProductStockCountById()方法的事務(wù)會(huì)在Spring中失效。
7.不正確的捕獲異常
不正確的捕獲異常也會(huì)導(dǎo)致Spring的事務(wù)失效,示例如下。
@Service
public?class?OrderService?{
?@Autowired
?private?OrderDao?orderDao;
?@Autowired
?private?ProductDao?productDao;
?@Transactional(propagation?=?Propagation.REQUIRED)
?public?void?submitOrder(){
??//生成訂單
??Order?order?=?new?Order();
??long?number?=?Math.abs(new?Random().nextInt(500));
??order.setId(number);
??order.setOrderNo("order_"?+?number);
??orderDao.saveOrder(order);
??//減庫(kù)存
??this.updateProductStockCountById(1,?1L);
?}
?@Transactional(propagation?=?Propagation.REQUIRED)
?public?void?updateProductStockCountById(Integer?stockCount,?Long?id){
??try{
???productDao.updateProductStockCountById(stockCount,?id);
???int?i?=?1?/?0;
??}catch(Exception?e){
???logger.error("扣減庫(kù)存異常:",?e.getMesaage());
??}
?}
}
updateProductStockCountById()方法中使用try-catch代碼塊捕獲了異常,即使updateProductStockCountById()方法內(nèi)部會(huì)拋出異常,但也會(huì)被catch代碼塊捕獲到,此時(shí)updateProductStockCountById()方法的事務(wù)會(huì)提交而不會(huì)回滾,并且submitOrder()方法的事務(wù)會(huì)提交而不會(huì)回滾,這就造成了Spring事務(wù)的回滾失效問(wèn)題。
8.錯(cuò)誤的標(biāo)注異常類(lèi)型
如果在@Transactional注解中標(biāo)注了錯(cuò)誤的異常類(lèi)型,則Spring事務(wù)的回滾會(huì)失效,示例如下。
@Transactional(propagation?=?Propagation.REQUIRED)
public?void?updateProductStockCountById(Integer?stockCount,?Long?id){
?try{
??productDao.updateProductStockCountById(stockCount,?id);
?}catch(Exception?e){
??logger.error("扣減庫(kù)存異常:",?e.getMesaage());
??throw?new?Exception("扣減庫(kù)存異常");
?}
}
在updateProductStockCountById()方法中捕獲了異常,并且在異常中拋出了Exception類(lèi)型的異常,此時(shí),updateProductStockCountById()方法事務(wù)的回滾會(huì)失效。
為何會(huì)失效呢?這是因?yàn)镾pring中對(duì)于默認(rèn)回滾的事務(wù)異常類(lèi)型為RuntimeException,上述代碼拋出的是Exception異常。
默認(rèn)情況下,Spring事務(wù)中無(wú)法捕獲到Exception異常,所以此時(shí)updateProductStockCountById()方法事務(wù)的回滾會(huì)失效。
此時(shí)可以手動(dòng)指定updateProductStockCountById()方法標(biāo)注的事務(wù)異常類(lèi)型,如下所示。
@Transactional(propagation?=?Propagation.REQUIRED,rollbackFor?=?Exception.class)
這里,需要注意的是:Spring事務(wù)注解@Transactional中的rollbackFor屬性可以指定 Throwable 異常類(lèi)及其子類(lèi)。
好了,今天就到這兒吧,我們下期見(jiàn)~~
