Spring声明式事物的实现,有两种方式;第一种是配置xml,第二种是使用相关注解(这两种方式可详见。SpringBoot中默认配置了第二种方式,所以,SpringBoot直接使用注解即可。下面介绍SpringBoot通过注解开启事物的使用。
spring其实是有提供关于事务的接口给我们调用的,下面会说到,只不过为了避免代码过于复杂,用注解的方式把过程都给简化了,注解中的属性设置也是和接口中的方法是一样的,最终也是交由管理类来实现的。

参考文章:
https://blog.csdn.net/xcbeyond/article/details/81235995(参考事务管理器图片以及注解原码)
https://blog.csdn.net/justry_deng/article/details/80828180(参考注解的各个属性,如何使用。注意事项以及阿里巴巴编码例子)
https://blog.csdn.net/nextyu/article/details/78669997(有关于事务传播机制的例子,本文没有引用,主要引用序列图)
https://blog.csdn.net/qq_38182963/article/details/78891044(原理方面,有提到mybatis)
https://blog.csdn.net/sinat_34341162/article/details/84192023(原码分析,本文没有引用)

@Transactional声明式事务

原理:在应用系统调用声明了 @Transactional 的目标方法时,Spring Framework 默认使用 AOP 拦截,在代码运行时生成一个代理对象,根据 @Transactional 的属性配置信息,这个代理对象决定该声明 @Transactional 的目标方法是否由*** TransactionInterceptor 【核心类】来使用拦截,在 TransactionInterceptor 拦截时,会在目标方法开始执行之前创建并加入事务,并执行目标方法的逻辑, 最后根据执行情况是否出现异常,利用抽象事务管理器AbstractPlatformTransactionManager 操作数据源 DataSource 提交或回滚事务。而事务管理器其实底层说到底操作数据库都是使用了Connection来实现的。

如何使用:


Transactional 注解的常用属性表:
属性 类型 描述
value 和 transactionManager : String 可选的限定描述符,指定使用的事务管理器
propagation 事务的传播行为,默认值为 REQUIRED。 定义事务的生命周期,有REQUIRED、SUPPORTS、MANDATORY、REQUIRES_NEW、NOT_SUPPORTED、NEVER、NESTED,
isolation:事务的隔离度,默认值采用 DEFAULT
readOnly boolean 指定事务是否为读写或只读事务,默认值为 false;
timeout int (in seconds granularity) 事务的超时时间,默认值为-1,不超时。如果设置了超时时间(单位秒),那么如果超过该时间限制了但事务还没有完成,则自动回滚事务。
rollbackFor Class对象数组,必须继承自Throwable 导致事务回滚的异常类数组,用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔。
rollbackForClassName 类名数组,必须继承自Throwable 导致事务回滚的异常类名字数组
noRollbackFor Class对象数组,必须继承自Throwable 不会导致事务回滚的异常类数组,{xxx1.class, xxx2.class,……}
noRollbackForClassName 类名数组,必须继承自Throwable 不会导致事务回滚的异常类名字数组

注解@Transactional源码:
package org.springframework.transaction.annotation;
 
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
import org.springframework.core.annotation.AliasFor;
import org.springframework.transaction.TransactionDefinition;
 
 
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
 
	@AliasFor("transactionManager")
	String value() default "";
	
	@AliasFor("value")
	String transactionManager() default "";
	
	Propagation propagation() default Propagation.REQUIRED;
 
	Isolation isolation() default Isolation.DEFAULT;
 
	int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
 
	boolean readOnly() default false;
 
	Class<? extends Throwable>[] rollbackFor() default {};
 
	String[] rollbackForClassName() default {};
	
	Class<? extends Throwable>[] noRollbackFor() default {};
 
	String[] noRollbackForClassName() default {};
}


重点看一下事务的传播行为这个属性:这是spring定义的实现,与数据库底层无关
PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。
PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。

以及事务隔离级别属性isolation ,默认值为 Isolation.DEFAULT。这个与数据库底层的事务隔离级别是一样的,可以通过代码来设置当前事务的隔离级别。
可选的值有:
Isolation.DEFAULT:使用底层数据库默认的隔离级别。
Isolation.READ_UNCOMMITTED
Isolation.READ_COMMITTED
Isolation.REPEATABLE_READ
Isolation.SERIALIZABLE

注意事项:
(1)@Transactional是来自org.springframework.transaction.annotation包的。
(2)@Transactional不仅可以注解在方法上,也可以注解在类上。当注解在类上时,意味着此类的所有public方法都是开启事务的。如果类级别和方法级别同时使用了@Transactional注解,则使用在类级别的注解会重载方法级别的注解。

Transactional 注解只能应用到 public 可见度的方法上。(因为是基于AOP实现的) 如果应用在protected、private或者 package可见度的方法上,也不会报错,不过事务设置不会起作用。

默认情况下,Transactional 注解的事物所管理的方法中,如果方法抛出运行时异常或error,那么会进行事务回滚;如果方法抛出的是非运行时异常,那么不会回滚。
注:SQL异常属于检查异常(有的框架将SQL异常重写为了运行时异常),但是有时我们写SQL时,检查异常并不会提示;而默认情况下,事物对检查异常不会作出回滚处理。
注:在很多时候,我们除了catch一般的异常或自定义异常外,我们还习惯于catch住Exception异常;然后再抛出Exception异常。但是Exception异常属于非运行时异常(即:检查异常),因为默认是运行时异常时事物才进行回滚,那么 这种情况下,是不会回滚的。我们可以在@Transacional注解中,通过rollbackFor = {Exception.class} 来解决这个问题。即:设置当Exception异常或Exception的所有任意子 类异常时事物会进行回滚。
注:被catch处理了的异常,不会被事物作为判断依据,必须要抛出;如果异常被catch 了,但是又在catch中抛出了新的异常,那么事物会以这个新的异常作 为是否进行回滚的判断依据。




事务管理器

上面说到了@Transactional其实是通过AOP拦截,然后调用相应事务管理器来处理的,下面就介绍一下spring给我们提供的事务管理接口和类:(PlatformTransactionManager和各种实现中间还有一个抽象类AbstractPlatformTransactionManager,抽象了共用的内容
数据访问技术及实现
数据访问技术 实现类
JDBC          DataSourceTransactionManager
JPA             JpaTransactionManager
Hibernate     HibernateTransactionManager
JDO               JdoTransactionManager
分布式事务     JtaTransactionManager

注意:jpa和hibernate不是默认就有的,当你导入了jpa的依赖之后才会导入这些管理器,mybatis并没有集成的管理器,但是也可以使用事务注解来使用spring也能整合并且处理,如果自己想要创建事务管理器,应该可以通过jdbc那个来管理,本人没试验过,还有就是mybatis本身也有提供事务管理的方法,在Spring中,mybatis 有 SqlSessionTemplate 代理执行,有个方法isSqlSessionTransactional,该方法从Spring 的容器中取出持有 SqlSession 的 持有类,判断Spirng 持有的 SqlSession 和 Mybatis 持有的是否是同一个,如果是,则交给Spring,否则,Mybatis 自己处理。【具体内容没有去深究,mybatis学的还不深】



大致执行流程:

阿里巴巴推荐用法:

不使用自动回滚,而是手动使用事务管理器来回滚,写法如下:

而不推荐直接使用:

也可以不使用@Transactional,自己控制事务管理器手动提交手动回滚: