本文分享自华为云社区《哪些场景下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 异常类及其子类。
点击关注,第一时间了解华为云新鲜技术~
评论