Spring事务与事务传播机制

为什么需要事务?

事务是将一组操作封装成一个执行单元,该组操作要么全部执行成功,要么全部执行失败,不能只执行成功一部分

如转账:A账户扣100,B账户增100,该组操作得全部执行成功,不能执行一条,否则要么A平白无故少100,要么B平白无故多100

所以需要事务来保障该组操作全部执行成功或全部执行失败

回顾MySQL中事务的使用

-- 开启事务
begin;

-- 执行一组操作

-- 提交事务
commit;

--回滚事务
rollback;

Spring中事务的实现

Spring中操作事务有两种方式:

  • 声明式事务(使用注解自动开启事务与提交事务)
  • 编程式事务(手动写代码实现事务)

编程式事务

@RestController
public class UserController {

    //JDBC事务管理器
    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;

    //事务属性
    @Autowired
    private TransactionDefinition transactionDefinition;

    @RequestMapping("/delete")
    public int delete(Integer id){
        if(id==null && id<=0) return 0;
        TransactionStatus transactionStatus = null;
        try{
            //开启事务
            transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
            int n = userService.delete(id);
            //提交事务
            dataSourceTransactionManager.commit(transactionStatus);
            return n;
        }catch (Exception e){ //出现异常,进行事务回滚操作
            //回滚事务
            dataSourceTransactionManager.rollback(transactionStatus);
        }
        return 0;
    }
}

声明式事务

只需在方法上加@Transactional注解,在进行方式时开启事务,当不发生异常时,待方法执行完提交事务,如果有异常发生会自动进行事务回滚操作

说明: @Transactional注解如果加在测试方法上,方法执行完会进行回滚操作,目的为了不污染数据库

@RestController
public class UserController2 {

    @RequestMapping("/delete")
    @Transactional
    public int delete(Integer id){
        if(id==null || id<=0){
            return 0;
        }
        int n = 0;
        n = userService.delete(id);
        return n;
    }
}

注意事项:

  • @Transactional可以修饰方法和类,修饰类时,该注解只对类中的public方法生效,修饰方法时,只对public方法生效
  • @Transactional在异常被捕获时,不会对事务进行回滚操作

如果异常被捕获,我们有两种解决方式:

  1. 在catch代码块中,再将异常抛出
	@RequestMapping("/delete")
	public int delete(Integer id){
	   if(id==null || id<0){
	       return 0;
	   }
	   try{
	       int n = 0;
	       n = userService.delete(id);
	       return n;
	   }catch (Exception e){
	       throw e; //再将异常抛出
	   }
	   return 0;
	}
  1. 手动进行回滚事务的操作
    @RequestMapping("/delete")
    public int delete(Integer id){
        if(id==null || id<0){
            return 0;
        }
        try{
            int n = 0;
            n = userService.delete(id);
            return n;
        }catch (Exception e){
        	//手动进行事务回滚操作
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return 0;
    }

声明式事务失效场景

  1. @Transactional修饰的方法为非public修饰的方法
  2. @Transactional设置了超时时间timeout,当方法的执行时间超过了该超时时间,事务会自动进行回滚
  3. 代码中有try-catch代码块,当异常被捕获时,不会自动进行回滚操作
  4. 数据库如果本身不支持事务,@Transactional只是给数据库通知开启,提交,回滚事务
  5. 在方法中(没使用@Transactional)调用了别的使用@Transactional的方法,事务也会失效

Spring中事务的隔离级别

Spring中事务的隔离级别有以下五种:

  • Isolation.DEFAULT:以连接数据库的隔离级别为准
  • Isolation.READ_UNCOMMITTED:读未提交
  • Isolation.READ_COMMITTED:读已提交(解决了脏读
  • Isolation.REPEATABLE_READ:可重复读(解决了脏读不可重复读
  • Isolation.SERIALIZABLE:串行化(解决了脏读不可重复读幻读

Spring中设置事务的隔离级别:

@Transactional(isolation = Isolation.SERIALIZABLE) //在@Transactional中添加isolation 属性

Spring事务的传播机制

Spring事务的传播机制描述的是,多个开启事务的方法,在相互调用时,事务是如何在这些方法之间传递的

在搞清楚事务传播机制之前,先搞懂两个概念:

  • 非事务方式运行:就是方法中每条操作执行完就会自动提交,不会再将该方法的多个操作打包成一个原子的操作执行,不保证其原子性
  • 事务挂起:比如A方法调用B方法,会将A方法开启的Transaction挂起,方法B中任何操作数据库的操作都不在该Transaction的管理之下,也就是两个事务是相互独立,互不干扰

方法A中调用方法B,下述描述中的当前指的的方法A

Spring事务的传播机制有以下七种:

  1. Propagation.REQUIRED默认的事务传播机制,如果当前(A)存在事务,则(B)加入该(A)事务,如果当前(A)不存在事务,则(B)创建新事务运行
  2. Propagation.SUPPORTS:如果当前存在事务,则加入该事务,如果当前不存在事务,则以非事务方式运行
  3. Propagation.MANDATORY:如果当前存在存在事务,则加入该事务,如果当前不存在事务,则抛异常
  4. Propagation.REQUIRES_NEW:如果当前(A)存在事务,将当前事务挂起,(B)会创建新的事务执行,两个事务相互独立,互不干扰,如果房前不存在事务,(B)也会创建新事务执行
  5. Propagation.NOT_SUPPORTED:(B)以非事务方式运行,如果当前(A)存在事务,将(A)事务挂起
  6. Propagation.NEVER:以非事务方式运行,如果当前存在事务,则抛异常
  7. Propagation.NESTED:如果当前(A)存在事务,则(B)创建一个事务当作当前(A)事务的嵌套事务来运行,如果当前不存在事务,则(B)创建新事物运行(等价于REQUIRED)

Spring中设置事务的传播机制:

@Transactional(propagation = Propagation.REQUIRED)

以上传播机制分为三类:

  • 支持当前事务:REQUIRED,SUPPORTS,MANDATORY
  • 不支持当前事务:REQUIRES_NEW,NOT_SUPPORTED,NEVER
  • 嵌套事务:NESTED

面试题: 加入事务,嵌套事务,事务挂起的区别?

  • 加入事务:如果该事务中某个操作执行失败,那整个事务就会进行回滚操作
  • 事务挂起:两个事务相互不影响,某个事务中的操作只会影响到自己所在事务,不会对别的事务产生影响
  • 嵌套事务:A调用B,B是以嵌套事务的方式执行,此时如果B中某个操作执行失败,那此时只有嵌套的事务进行回滚,不会影响外部的A事务,如果A事务中的某个操作执行失败,那A事务连同嵌套事务都会进行回滚
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

X_H学Java

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值