Spring(四)——事务传播性(介绍、例子解释、等级介绍、代码实现)
一、事务传播性
1、事务传播性介绍
Spring相应的定义了一个枚举类来方便使用事务传播行为:
2、事务传播性例子解释
还是用经典的转账例子:
以前转账都是在 dao 层写好各自的数据库逻辑,然后 service 层调用 dao 层的两个方法,完成一方减钱另一方加钱的逻辑,事务的话就是把这两个逻辑包裹起来。
现在就不用这个方法;在减钱的逻辑里面调用加钱的方法,这么一来只需要调用减钱的逻辑,就自然而然的使用到加钱逻辑,一套方法走下来就是转账的逻辑。
那么此时就是使用到事务的传播性了,外面那层有事务,回滚了,里面是否回滚;同理,里面那层回滚了,外面那层是否也会回滚?
3、事务传播等级介绍
事务传播是有分等级的。不同等级有不同的效果。主要了解前三种即可,后四种用得少:
a、REQUIRED
大白话翻译:首先平时开启事务,默认就是这个等级:REQUIRED。
其次,这个等级修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰:也就是如果外部方法没有开启事务,内部方法会自己开启事务(因为默认就是 REQUIRED 这个等级)。
最后,内外都是这个等级的话,只要其中一个回滚(外层回滚或者内层回滚),那么整个事务都回滚。内外其中有一个触发异常,那么内外都回滚。
b、REQUIRES_NEW
大白话翻译:这个等级修饰的内部方法,会新开启自己的事务,且开启的事务相互独立,互不干扰。因为默认不是这个等级的,那么意味着如果外层方法开启了事务,这时就同时有两个事务存在。
那么此时就有两种情况,一种是内外都回滚,一种是内层回滚外层不回滚。
先说第一种情况:内层方法触发了异常,理所应当的会进行回滚,如果内层的异常没有处理,那么异常会抛出到外层,那么此时外层也会接收到异常,所以导致外层触发了异常也进行回滚。这就是内外层都回滚。
第二种情况:就是内层触发了异常,但是把异常处理了,那么外层就接收不到异常了,那么这时只有内层回滚,而外层就不会回滚了。
那么异常怎么处理呢?其实 try…catch就是处理异常了:
c、NESTED
大白话翻译:外部没有开启事务的情况下,这个等级的作用跟默认等级是一样的。且开启的事务相互独立,互不干扰。
如果外部开启了事务的状态下,NESTED 修饰的内部方法,是属于外部事务的子事务。外部事务回滚的话,内部事务也会回滚;内部事务回滚,外部事务反而不会回滚。
d、MANDATORY
e、SUPPORTS
f、SUPPORTED
g、NEVER
h、注意
如果操作的字段没有索引,这时候会触发表锁,就是你执行某个数据库操作的时候,这时会把整张表锁住,那么此时其他操作就不能对这个表进行操作了。只有操作的字段是索引的情况下,才会触发行锁;这时才能对该张表的其他行操作。
比如主键 ID 就有索引。比如 username 这个就有可能没有索引,需要自己设置。所以 innodb 并不是没有表锁,得看情况。
4、xml 配置事务传播
导入依赖:
<properties>
<slf4j.version>1.7.32</slf4j.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.14</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.14</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.7</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${slf4j.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.googlecode.usc</groupId>
<artifactId>jdbcdslog</artifactId>
<version>1.0.6.2</version>
</dependency>
</dependencies>
xml 配置中加入事务传播等级:
5、Java / xml 配置事务隔离
下图中红色框框部分就是 xml 配置事务隔离级别:
如果是注解配置则是这样:
6、事务超时属性
7、事务只读属性
xml 配置:
8、事务回滚规则
并不是所有的异常都会触发回滚,只有部分异常才会触发。
一般像写完代码就报错要你捕获或者抛出去的这种就是检查型异常,比如 IOexception。
一般来说默认的也就够用了。
9、@Transactional 注解使用细节
a、作用范围
b、常用参数
c、作用原理
d、自调用问题
大白话讲就是如果在 service 层里面调用外面的东西才会生效,如果 service 层定义了 A 方法,然后又调用了 A 方法,那么这时候就不会生效了,没走 AOP 那套。要避免这种事情的发生。