当前位置: 首页 > 科技观察

必问专访-Spring的事务在哪些场景下会失败?

时间:2023-03-21 19:01:15 科技观察

在日常工作中,如果Spring的事务管理功能使用不当,会导致Spring事务无法生效。Spring事务不生效的问题也是跳槽面试中经常被问到的问题。今天就来梳理一下哪些场景会导致Spring事务生效。注:部分内容引用自冰河和猫爷出版的《深入理解分布式事务:原理与实战》一书。文章收录在GitHub和Gitee:GitHub:https://github.com/sunshinelyz/technology-bingheGitee:https://gitee.com/binghe001/technology-bingheSpringtransactiondoesnottakeeffect在某些场景下会失败,如如下图所示。数据库不支持事务。Spring事务生效的前提是连接的数据库必须支持事务。如果底层数据库不支持事务,Spring事务肯??定会失败。比如使用的数据库是MySQL,选择了MyISAM存储引擎,那么Spring事务就会失效。事务方法不是由Spring管理的。如果事务方法所在的类没有加载到SpringIOC容器中,即事务方法所在的类不是由Spring管理的,那么Spring事务就会失效。示例如下。publicclassProductService{@AutowiredprivateProductDaoproductDao;@Transactional(propagation=Propagation.REQUIRES_NEW)publicvoidupdateProductStockCountById(IntegerstockCount,Longid){productDao.updateProductStockCountById(stockCount,idService);}}@ProductService注解容器实例上没有标注@ProductService类,它会导致updateProductStockCountById()方法的事务在Spring中失败。该方法未被public修改。如果事务所在的方法没有被public修改,此时Spring事务就会失效,例如如下代码所示。@ServicepublicclassProductService{@AutowiredprivateProductDaoproductDao;@Transactional(propagation=Propagation.REQUIRES_NEW)privatevoidupdateProductStockCountById(IntegerstockCount,Longid){productDao.updateProductStockCountById(stockCount,id);}}虽然ProductService上标了@Service注解,同步更新了ProductdStock@Transactional(propagation=Propagation.REQUIRES_NEW)注解。但是由于updateProductStockCountById()方法是一个内部私有方法(用private修饰),所以此时updateProductStockCountById()方法的事务在Spring中会失效。同一个类中的方法调用如果同一个类中的两个方法是A和B,方法A没有添加事务注解,方法B添加@Transactional事务注解,方法A调用方法B,那么方法B的事务会作废。例如,如下代码所示。@ServicepublicclassOrderService{@AutowiredprivateOrderDaoorderDao;@AutowiredprivateProductDaoproductDao;publicvoidsubmitOrder(){//生成订单Orderorder=newOrder();longnumber=Math.abs(newRandom().nextInt(500));order.setId(number);order.setOrderNo("order_"+数字);orderDao.saveOrder(order);//减少库存this.updateProductStockCountById(1,1L);}@Transactional(propagation=Propagation.REQUIRES_NEW)publicvoidupdateProductCountById(IntegerstockCount,Longid){productDao.updateIdtProductStockById(Countid);}}submitOrder()方法和updateProductStockCountById()方法都在OrderService类中,submitOrder()方法没有标注交易注解,updateProductStockCountById()方法标注了交易注解,submitOrder()方法调用了updateProductStockCountById()方法,这时,updateProductStockCountById()方法的事务在Spring中就会失效。未配置事务管理器如果项目中没有配置Spring的事务管理器,即使使用了Spring的事务管理功能,Spring的事务也不会生效。比如下面的代码,在项目的配置类中是没有配置的。@BeanpublicPlatformTransactionManagertransactionManager(DataSourcedataSource){returnnewDataSourceTransactionManager(dataSource);}此时Spring的事务就会失效。该方法的事务传播类型不支持事务。如果内部方法的事务传播类型是不支持事务的传播类型,那么内部方法的事务在Spring中就会失效。例如,如下代码所示。@ServicepublicclassOrderService{@AutowiredprivateOrderDaoorderDao;@AutowiredprivateProductDaoproductDao;@Transactional(propagation=Propagation.REQUIRED)publicvoidsubmitOrder(){//生成订单Orderorder=newOrder();longnumber=Math.abs(newRandom().nextInt(500));order.setId(number);order.setOrderNo("order_"+number);orderDao.saveOrder(order);//减少库存this.updateProductStockCountById(1,1L);}@Transactional(propagation=Propagation.NOT_SUPPORTED)publicvoidupdateProductStockCountById(IntegerstockCount,Longid){productDao.updateProductStockCountById(stockCount,id);}}由于updateProductStockCountById()方法的事务传播类型为NOT_SUPPORTED,不支持事务,所以在Spring中updateProductStockCountById()方法的事务会失效。异常捕获不当异常捕获不正确也会导致Spring的事务失败,示例如下。@ServicepublicclassOrderService{@AutowiredprivateOrderDaoorderDao;@AutowiredprivateProductDaoproductDao;@Transactional(propagation=Propagation.REQUIRED)publicvoidsubmitOrder(){//生成订单Orderorder=newOrder();longnumber=Math.abs(newRandom().nextInt(500));order.setId(number);order.setOrderNo("order_"+number);orderDao.saveOrder(order);//减少库存this.updateProductStockCountById(1,1L);}@Transactional(propagation=Propagation.REQUIRED)publicvoidupdateProductStockCountById(IntegerstockCount,Longid){try{productDao.updateProductStockCountById(stockCount,id);inti=1/0;}catch(Exceptione){logger.error("扣除库存异常:",e.getMesaage());}}}updateProductStockCountById()方法使用try-catch代码块来捕获异常。即使updateProductStockCountById()方法内部抛出异常,它也会被catch代码块捕获。这时候updateProductStockCountById()方法的事务会被提交,不会被回滚。而submitOrder()方法的事务会在没有回滚的情况下提交,这就造成了Spring事务的回滚失败问题。错误注解的异常类型如果在@Transactional注解中注解了错误的异常类型,将会导致Spring事务回滚失败,如下图。@Transactional(propagation=Propagation.REQUIRED)publicvoidupdateProductStockCountById(IntegerstockCount,Longid){try{productDao.updateProductStockCountById(stockCount,id);}catch(Exceptione){logger.error("扣除库存异常:",e.getMesaage());thrownewException("Deductinginventoryexception");}}updateProductStockCountById()方法中捕获到异常,异常中抛出Exception类型的异常。此时回滚updateProductStockCountById()方法事务将失效。为什么会失败?这是因为Spring中默认回滚的事务异常类型是RuntimeException,上面的代码抛出了Exception。默认情况下,Spring事务中无法捕获到Exception,所以此时回滚updateProductStockCountById()方法事务会失效。此时可以手动指定updateProductStockCountById()方法标记的交易异常类型,如下图。@Transactional(propagation=Propagation.REQUIRED,rollbackFor=Exception.class)这里需要注意的是:Spring事务注解@Transactional中的rollbackFor属性可以指定Throwable异常类及其子类。本文转载自微信公众号“银禾科技”,可通过以下二维码关注。转载本文请联系冰川科技公众号。

最新推荐
猜你喜欢