本文分享自華為雲社區《哪些場景下Spring的事務會失效?》,作者:冰 河 。 在日常工作中,如果對Spring的事務管理功能使用不當,則會造成Spring事務不生效的問題。而針對Spring事務不生效的問題,也是在跳槽面試中被問的比較頻繁的一個問題。 今天,我們就一起梳理下有哪些場景會導致Spri ...
本文分享自華為雲社區《哪些場景下Spring的事務會失效?》,作者:冰 河 。
在日常工作中,如果對Spring的事務管理功能使用不當,則會造成Spring事務不生效的問題。而針對Spring事務不生效的問題,也是在跳槽面試中被問的比較頻繁的一個問題。
今天,我們就一起梳理下有哪些場景會導致Spring事務失效。
Spring事務不生效總覽
簡單來說,Spring事務會在幾種特定的場景下失效,如下圖所示。
資料庫不支持事務
Spring事務生效的前提是所連接的資料庫要支持事務,如果底層的資料庫都不支持事務,則Spring的事務肯定會失效。例如,如果使用的資料庫為MySQL,並且選用了MyISAM存儲引擎,則Spring的事務就會失效。
事務方法未被Spring管理
如果事務方法所在的類沒有載入到Spring IOC容器中,也就是說,事務方法所在的類沒有被Spring管理,則Spring事務會失效,示例如下。
public class ProductService { @Autowired private ProductDao productDao; @Transactional(propagation = Propagation.REQUIRES_NEW) public void updateProductStockCountById(Integer stockCount, Long id){ productDao.updateProductStockCountById(stockCount, id); } }
ProductService類上沒有標註@Service註解,Product的實例沒有載入到Spring IOC容器中,就會造成updateProductStockCountById()方法的事務在Spring中失效。
方法沒有被public修飾
如果事務所在的方法沒有被public修飾,此時Spring的事務會失效,例如,如下代碼所示。
@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上標註了@Service註解,同時updateProductStockCountById()方法上標註了@Transactional(propagation = Propagation.REQUIRES_NEW)註解。
但是,由於updateProductStockCountById()方法為內部的私有方法(使用private修飾),那麼此時updateProductStockCountById()方法的事務在Spring中會失效。
同一類中方法調用
如果同一個類中的兩個方法分別為A和B,方法A上沒有添加事務註解,方法B上添加了 @Transactional事務註解,方法A調用方法B,則方法B的事務會失效。例如,如下代碼所示。
@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); //減庫存 this.updateProductStockCountById(1, 1L); } @Transactional(propagation = Propagation.REQUIRES_NEW) public void updateProductStockCountById(Integer stockCount, Long id){ productDao.updateProductStockCountById(stockCount, id); } }
submitOrder()方法和updateProductStockCountById()方法都在OrderService類中,submitOrder()方法上沒有標註事務註解,updateProductStockCountById()方法上標註了事務註解,submitOrder()方法調用了updateProductStockCountById()方法,此時,updateProductStockCountById()方法的事務在Spring中會失效。
未配置事務管理器
如果在項目中沒有配置Spring的事務管理器,即使使用了Spring的事務管理功能,Spring的事務也不會生效。
例如,沒有在項目的配置類中配置如下代碼。
@Bean public PlatformTransactionManager transactionManager(DataSource dataSource) { return new DataSourceTransactionManager(dataSource); }
此時,Spring的事務就會失效。
方法的事務傳播類型不支持事務
如果內部方法的事務傳播類型為不支持事務的傳播類型,則內部方法的事務在Spring中會失效。
例如,如下代碼所示。
@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); //減庫存 this.updateProductStockCountById(1, 1L); } @Transactional(propagation = Propagation.NOT_SUPPORTED) public void updateProductStockCountById(Integer stockCount, Long id){ productDao.updateProductStockCountById(stockCount, id); } }
由於updateProductStockCountById()方法的事務傳播類型為NOT_SUPPORTED,不支持事務,則updateProductStockCountById()方法的事務會在Spring中失效。
不正確的捕獲異常
不正確的捕獲異常也會導致Spring的事務失效,示例如下。
@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); //減庫存 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("扣減庫存異常:", e.getMesaage()); } } }
updateProductStockCountById()方法中使用try-catch代碼塊捕獲了異常,即使updateProductStockCountById()方法內部會拋出異常,但也會被catch代碼塊捕獲到,此時updateProductStockCountById()方法的事務會提交而不會回滾,並且submitOrder()方法的事務會提交而不會回滾,這就造成了Spring事務的回滾失效問題。
錯誤的標註異常類型
如果在@Transactional註解中標註了錯誤的異常類型,則Spring事務的回滾會失效,示例如下。
@Transactional(propagation = Propagation.REQUIRED) public void updateProductStockCountById(Integer stockCount, Long id){ try{ productDao.updateProductStockCountById(stockCount, id); }catch(Exception e){ logger.error("扣減庫存異常:", e.getMesaage()); throw new Exception("扣減庫存異常"); } }
在updateProductStockCountById()方法中捕獲了異常,並且在異常中拋出了Exception類型的異常,此時,updateProductStockCountById()方法事務的回滾會失效。
為何會失效呢?這是因為Spring中對於預設回滾的事務異常類型為RuntimeException,上述代碼拋出的是Exception異常。
預設情況下,Spring事務中無法捕獲到Exception異常,所以此時updateProductStockCountById()方法事務的回滾會失效。
此時可以手動指定updateProductStockCountById()方法標註的事務異常類型,如下所示。
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
這裡,需要註意的是:Spring事務註解@Transactional中的rollbackFor屬性可以指定 Throwable 異常類及其子類。