SpringBoot的事务详解

一、事务的存在目的

1. 事务是为了解决数据安全问题而存在的。

最经典的例子就是银行转账问题,账户A给账户B转账100元,账户A扣除100元之后由于不可抗力因素导致程序中断,账户B没有收到100元的转账,账户A的100元凭空消失,肯定是不行的。A扣款和B收款操作要么同时成功,要么同时失败,这个时候就需要引入事务操作。

2、事务的四个特性

  • 原子性:一个事务是一个不可分割的工作单位。
  • 一致性:事务必须是使数据库从一个一致性状态变到另一个一致性状态,一致性与原子性是密切相关的。
  • 隔离性:一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
  • 持久性:一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。

3、事务管理方式

Spring支持编程式事务管理和声明式事务管理两种方式。

编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,Spring推荐使用TransactionTemplate。

声明式事务管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional 注解的方式),便可以将事务规则则应用到业务逻辑中。

4、@Transactional注解

它是声明式事务管理编程中使用的注解,放在接口实现类或接口实现方法上,并且只对public方法才起作用,一般在访问数据库的Service方法上添加。默认情况下,该注解只对 RuntimeException 及其子类异常执行事务回滚。 只读的接口不需要事务管理,防止影响系统性能。

@Transactional 实质是使用了JDBC的事务来进行事务控制的,实现原理:

4.1 事务开始时,通过AOP机制,生成一个代理connection对象,并将其放入 DataSource 实例的某个与 DataSourceTransactionManager 相关的某处容器中。在接下来的整个事务中,客户代码都应该使用该 connection 连接数据库,执行所有数据库命令。[不使用该 connection 连接数据库执行的数据库命令,在本事务回滚的时候得不到回滚](物理连接 connection 逻辑上新建一个会话session;DataSource 与 TransactionManager 配置相同的数据源)

4.2 事务结束时,回滚在第1步骤中得到的代理 connection 对象上执行的数据库命令,然后关闭该代理 connection 对象。(事务结束后,回滚操作不会对已执行完毕的SQL操作命令起作用)

public interface AccountService {
    @Transactional
    void transfer(String out,String in,Double money);
}

二、常见问题

@EnableTransactionManagement
问题:springboot 项目中是否需要在 启动类上添加 @EnableTransactionManagement 注解?
答案:不需要。因为 springboot 自动装配已经帮助我们处理了,springboot 项目默认支持了事务。
2. @Transactional 失效
问题:方法上添加了 @Transactional 注解,为什么没有进行回滚?
排查一:是否使用了 try catch 进行了异常捕获,并且捕获之后,没有通过 throw new RuntimeException(); 进行异常抛出。因为对于 spring aop 异常捕获原理,被拦截的方法需要显示的抛出异常,并不能进行任何处理,这样 aop 代理才能捕获到方法的异常,才能进行事务的回滚操作;默认清空下,aop 只捕获 RuntimeException 的异常,但是可以通过配置来捕获特定的异常并回滚。
排查二:是否使用了 try catch 进行了异常信息捕获,如果是可以在 catch 语句中添加:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 
排查三:排查是不是产生了自调用的问题。在 spring 的 aop 代理下,只有目标方法被外部调用,目标方法才由 spring 生成的代理对象来管理;若统一类中的其他没有用 @Transactional 注解进行修饰的方法内部调用了 用 @Transactional 注解进行修饰的方法,有 @Transactional 注解的方法的事务将会被忽略,不发生回滚。

备注:如果确实需要这样操作,只需要把 @Transactional 注解加在当前的类名上就可以了 或者使用 AspectJ 取代 spring aop 进行代理。
排查四:@Transactional 注解只被应用到 public 修饰的方法上;如果在 protected、private等修饰的方法上,@Transactional 注解不会报错,但是这个注解的将不会生效。
排查五:@Transactional 注解不会对当前修饰的方法的子方法生效。比如:我们在方法 A 中声明了 @Transactional 注解,但是 A 方法的内部调用的 方法 B 和 方法 C,其中方法 B 进行了 数据库的操作,但是该部分的异常被方法 B 进行了处理并且没有进行 抛出,这样的话事务是不会生效的。反之,如果 方法 B 声明了 @Transactional,但是方法 A 没有声明 @Transactional,A 方法内部调用 B 方法,事务也是不会生效的。如果想要事务生效,需要将子方法的事务控制交给调用的方法,在子方法中使用 @Transactional注解并通过 rollbackFor 指定定回滚的异常或者直接将异常 抛出。

备注:在使用事务的时候,最好把子方法的异常进行抛出,交给调用的方法进行处理。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值