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 指定定回滚的异常或者直接将异常 抛出。
备注:在使用事务的时候,最好把子方法的异常进行抛出,交给调用的方法进行处理。