[docs update]spring事务内容完善
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 73 KiB |
Before Width: | Height: | Size: 81 KiB |
@ -5,15 +5,13 @@ tag:
|
||||
- Spring
|
||||
---
|
||||
|
||||
大家好,我是 Guide 哥,前段时间答应读者的 **Spring 事务**分析总结终于来了。这部分内容比较重要,不论是对于工作还是面试,但是网上比较好的参考资料比较少。
|
||||
前段时间答应读者的 **Spring 事务** 分析总结终于来了。这部分内容比较重要,不论是对于工作还是面试,但是网上比较好的参考资料比较少。
|
||||
|
||||
如果本文有任何不对或者需要完善的地方,请帮忙指出!Guide 哥感激不尽!
|
||||
|
||||
## 1. 什么是事务?
|
||||
## 什么是事务?
|
||||
|
||||
**事务是逻辑上的一组操作,要么都执行,要么都不执行。**
|
||||
|
||||
_Guide 哥:大家应该都能背上面这句话了,下面我结合我们日常的真实开发来谈一谈。_
|
||||
相信大家应该都能背上面这句话了,下面我结合我们日常的真实开发来谈一谈。
|
||||
|
||||
我们系统的每个业务方法可能包括了多个原子性的数据库操作,比如下面的 `savePerson()` 方法中就有两个原子性的数据库操作。这些原子性的数据库操作是有依赖的,它们要么都执行,要不就都不执行。
|
||||
|
||||
@ -24,13 +22,12 @@ _Guide 哥:大家应该都能背上面这句话了,下面我结合我们日
|
||||
}
|
||||
```
|
||||
|
||||
另外,需要格外注意的是:**事务能否生效数据库引擎是否支持事务是关键。比如常用的 MySQL 数据库默认使用支持事务的`innodb`引擎。但是,如果把数据库引擎变为 `myisam`,那么程序也就不再支持事务了!**
|
||||
另外,需要格外注意的是:**事务能否生效数据库引擎是否支持事务是关键。比如常用的 MySQL 数据库默认使用支持事务的 `innodb`引擎。但是,如果把数据库引擎变为 `myisam`,那么程序也就不再支持事务了!**
|
||||
|
||||
事务最经典也经常被拿出来说例子就是转账了。假如小明要给小红转账 1000 元,这个转账会涉及到两个关键操作就是:
|
||||
|
||||
1. 将小明的余额减少 1000 元
|
||||
|
||||
2. 将小红的余额增加 1000 元。
|
||||
> 1. 将小明的余额减少 1000 元。
|
||||
> 2. 将小红的余额增加 1000 元。
|
||||
|
||||
万一在这两个操作之间突然出现错误比如银行系统崩溃或者网络故障,导致小明余额减少而小红的余额没有增加,这样就不对了。事务就是保证这两个关键操作要么都成功,要么都要失败。
|
||||
|
||||
@ -58,9 +55,7 @@ public class OrdersService {
|
||||
|
||||
另外,数据库事务的 ACID 四大特性是事务的基础,下面简单来了解一下。
|
||||
|
||||
## 2. 事务的特性(ACID)了解么?
|
||||
|
||||

|
||||
## 事务的特性(ACID)了解么?
|
||||
|
||||
- **原子性(Atomicity):** 一个事务(transaction)中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。即,事务不可分割、不可约简。
|
||||
- **一致性(Consistency):** 在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设约束、触发器、级联回滚等。
|
||||
@ -69,17 +64,17 @@ public class OrdersService {
|
||||
|
||||
参考 :[https://zh.wikipedia.org/wiki/ACID](https://zh.wikipedia.org/wiki/ACID) 。
|
||||
|
||||
## 3. 详谈 Spring 对事务的支持
|
||||
## 详谈 Spring 对事务的支持
|
||||
|
||||
**再提醒一次:你的程序是否支持事务首先取决于数据库 ,比如使用 MySQL 的话,如果你选择的是 innodb 引擎,那么恭喜你,是可以支持事务的。但是,如果你的 MySQL 数据库使用的是 myisam 引擎的话,那不好意思,从根上就是不支持事务的。**
|
||||
> ⚠️ 再提醒一次:你的程序是否支持事务首先取决于数据库 ,比如使用 MySQL 的话,如果你选择的是 innodb 引擎,那么恭喜你,是可以支持事务的。但是,如果你的 MySQL 数据库使用的是 myisam 引擎的话,那不好意思,从根上就是不支持事务的。
|
||||
|
||||
这里再多提一下一个非常重要的知识点: **MySQL 怎么保证原子性的?**
|
||||
|
||||
我们知道如果想要保证事务的原子性,就需要在异常发生时,对已经执行的操作进行**回滚**,在 MySQL 中,恢复机制是通过 **回滚日志(undo log)** 实现的,所有事务进行的修改都会先先记录到这个回滚日志中,然后再执行相关的操作。如果执行过程中遇到异常的话,我们直接利用 **回滚日志** 中的信息将数据回滚到修改之前的样子即可!并且,回滚日志会先于数据持久化到磁盘上。这样就保证了即使遇到数据库突然宕机等情况,当用户再次启动数据库的时候,数据库还能够通过查询回滚日志来回滚将之前未完成的事务。
|
||||
|
||||
### 3.1. Spring 支持两种方式的事务管理
|
||||
### Spring 支持两种方式的事务管理
|
||||
|
||||
#### 1).编程式事务管理
|
||||
#### 编程式事务管理
|
||||
|
||||
通过 `TransactionTemplate`或者`TransactionManager`手动管理事务,实际应用中很少使用,但是对于你理解 Spring 事务管理原理有帮助。
|
||||
|
||||
@ -125,7 +120,7 @@ public void testTransaction() {
|
||||
}
|
||||
```
|
||||
|
||||
#### 2)声明式事务管理
|
||||
#### 声明式事务管理
|
||||
|
||||
推荐使用(代码侵入性最小),实际是通过 AOP 实现(基于`@Transactional` 的全注解方式使用最多)。
|
||||
|
||||
@ -142,7 +137,7 @@ public void aMethod {
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2. Spring 事务管理接口介绍
|
||||
### Spring 事务管理接口介绍
|
||||
|
||||
Spring 框架中,事务管理相关最重要的 3 个接口如下:
|
||||
|
||||
@ -154,7 +149,7 @@ Spring 框架中,事务管理相关最重要的 3 个接口如下:
|
||||
|
||||
**`PlatformTransactionManager`** 会根据 **`TransactionDefinition`** 的定义比如事务超时时间、隔离级别、传播行为等来进行事务管理 ,而 **`TransactionStatus`** 接口则提供了一些方法来获取事务相应的状态比如是否新事务、是否可以回滚等等。
|
||||
|
||||
#### 3.2.1. PlatformTransactionManager:事务管理接口
|
||||
#### PlatformTransactionManager:事务管理接口
|
||||
|
||||
**Spring 并不直接管理事务,而是提供了多种事务管理器** 。Spring 事务管理器的接口是: **`PlatformTransactionManager`** 。
|
||||
|
||||
@ -162,7 +157,7 @@ Spring 框架中,事务管理相关最重要的 3 个接口如下:
|
||||
|
||||
**`PlatformTransactionManager` 接口的具体实现如下:**
|
||||
|
||||

|
||||

|
||||
|
||||
`PlatformTransactionManager`接口中定义了三个方法:
|
||||
|
||||
@ -184,21 +179,36 @@ public interface PlatformTransactionManager {
|
||||
|
||||
**这里多插一嘴。为什么要定义或者说抽象出来`PlatformTransactionManager`这个接口呢?**
|
||||
|
||||
主要是因为要将事务管理行为抽象出来,然后不同的平台去实现它,这样我们可以保证提供给外部的行为不变,方便我们扩展。我前段时间分享过:**“为什么我们要用接口?”**
|
||||
主要是因为要将事务管理行为抽象出来,然后不同的平台去实现它,这样我们可以保证提供给外部的行为不变,方便我们扩展。
|
||||
|
||||

|
||||
我前段时间在我的[知识星球](https://www.yuque.com/snailclimb/rpkqw1/pvak2w)分享过:**“为什么我们要用接口?”** 。
|
||||
|
||||
#### 3.2.2. TransactionDefinition:事务属性
|
||||
> 《设计模式》(GOF 那本)这本书在很多年前都提到过说要基于接口而非实现编程,你真的知道为什么要基于接口编程么?
|
||||
>
|
||||
> 纵观开源框架和项目的源码,接口是它们不可或缺的重要组成部分。要理解为什么要用接口,首先要搞懂接口提供了什么功能。我们可以把接口理解为提供了一系列功能列表的约定,接口本身不提供功能,它只定义行为。但是谁要用,就要先实现我,遵守我的约定,然后再自己去实现我定义的要实现的功能。
|
||||
>
|
||||
> 举个例子,我上个项目有发送短信的需求,为此,我们定了一个接口,接口只有两个方法:
|
||||
>
|
||||
> 1.发送短信
|
||||
> 2.处理发送结果的方法。
|
||||
>
|
||||
> 刚开始我们用的是阿里云短信服务,然后我们实现这个接口完成了一个阿里云短信的服务。后来,我们突然又换到了别的短信服务平台,我们这个时候只需要再实现这个接口即可。这样保证了我们提供给外部的行为不变。几乎不需要改变什么代码,我们就轻松完成了需求的转变,提高了代码的灵活性和可扩展性。
|
||||
>
|
||||
> 什么时候用接口?当你要实现的功能模块设计抽象行为的时候,比如发送短信的服务,图床的存储服务等等。
|
||||
|
||||
#### TransactionDefinition:事务属性
|
||||
|
||||
事务管理器接口 **`PlatformTransactionManager`** 通过 **`getTransaction(TransactionDefinition definition)`** 方法来得到一个事务,这个方法里面的参数是 **`TransactionDefinition`** 类 ,这个类就定义了一些基本的事务属性。
|
||||
|
||||
那么什么是 **事务属性** 呢?
|
||||
|
||||
事务属性可以理解成事务的一些基本配置,描述了事务策略如何应用到方法上。
|
||||
**什么是事务属性呢?** 事务属性可以理解成事务的一些基本配置,描述了事务策略如何应用到方法上。
|
||||
|
||||
事务属性包含了 5 个方面:
|
||||
|
||||

|
||||
- 隔离级别
|
||||
- 传播行为
|
||||
- 回滚规则
|
||||
- 是否只读
|
||||
- 事务超时
|
||||
|
||||
`TransactionDefinition` 接口中定义了 5 个方法以及一些表示事务属性的常量比如隔离级别、传播行为等等。
|
||||
|
||||
@ -235,7 +245,7 @@ public interface TransactionDefinition {
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2.3. TransactionStatus:事务状态
|
||||
#### TransactionStatus:事务状态
|
||||
|
||||
`TransactionStatus`接口用来记录事务的状态 该接口定义了一组方法,用来获取或判断事务的相应状态信息。
|
||||
|
||||
@ -253,19 +263,17 @@ public interface TransactionStatus{
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3. 事务属性详解
|
||||
### 事务属性详解
|
||||
|
||||
_实际业务开发中,大家一般都是使用 `@Transactional` 注解来开启事务,很多人并不清楚这个参数里面的参数是什么意思,有什么用。为了更好的在项目中使用事务管理,强烈推荐好好阅读一下下面的内容。_
|
||||
际业务开发中,大家一般都是使用 `@Transactional` 注解来开启事务,很多人并不清楚这个参数里面的参数是什么意思,有什么用。为了更好的在项目中使用事务管理,强烈推荐好好阅读一下下面的内容。
|
||||
|
||||
#### 3.3.1. 事务传播行为
|
||||
#### 事务传播行为
|
||||
|
||||
**事务传播行为是为了解决业务层方法之间互相调用的事务问题**。
|
||||
|
||||
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。
|
||||
|
||||
**举个例子!**
|
||||
|
||||
我们在 A 类的`aMethod()`方法中调用了 B 类的 `bMethod()` 方法。这个时候就涉及到业务层方法之间互相调用的事务问题。如果我们的 `bMethod()`如果发生异常需要回滚,如何配置事务传播行为才能让 `aMethod()`也跟着回滚呢?这个时候就需要事务传播行为的知识了,如果你不知道的话一定要好好看一下。
|
||||
举个例子:我们在 A 类的`aMethod()`方法中调用了 B 类的 `bMethod()` 方法。这个时候就涉及到业务层方法之间互相调用的事务问题。如果我们的 `bMethod()`如果发生异常需要回滚,如何配置事务传播行为才能让 `aMethod()`也跟着回滚呢?这个时候就需要事务传播行为的知识了,如果你不知道的话一定要好好看一下。
|
||||
|
||||
```java
|
||||
Class A {
|
||||
@ -309,30 +317,29 @@ import org.springframework.transaction.TransactionDefinition;
|
||||
|
||||
public enum Propagation {
|
||||
|
||||
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
|
||||
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
|
||||
|
||||
SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),
|
||||
SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),
|
||||
|
||||
MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),
|
||||
MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),
|
||||
|
||||
REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
|
||||
REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
|
||||
|
||||
NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),
|
||||
NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),
|
||||
|
||||
NEVER(TransactionDefinition.PROPAGATION_NEVER),
|
||||
NEVER(TransactionDefinition.PROPAGATION_NEVER),
|
||||
|
||||
NESTED(TransactionDefinition.PROPAGATION_NESTED);
|
||||
NESTED(TransactionDefinition.PROPAGATION_NESTED);
|
||||
|
||||
private final int value;
|
||||
|
||||
private final int value;
|
||||
Propagation(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
Propagation(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int value() {
|
||||
return this.value;
|
||||
}
|
||||
public int value() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -344,8 +351,8 @@ public enum Propagation {
|
||||
|
||||
使用的最多的一个事务传播行为,我们平时经常使用的`@Transactional`注解默认使用就是这个事务传播行为。如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。也就是说:
|
||||
|
||||
1. 如果外部方法没有开启事务的话,`Propagation.REQUIRED`修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。
|
||||
2. 如果外部方法开启事务并且被`Propagation.REQUIRED`的话,所有`Propagation.REQUIRED`修饰的内部方法和外部方法均属于同一事务 ,只要一个方法回滚,整个事务均回滚。
|
||||
- 如果外部方法没有开启事务的话,`Propagation.REQUIRED`修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。
|
||||
- 如果外部方法开启事务并且被`Propagation.REQUIRED`的话,所有`Propagation.REQUIRED`修饰的内部方法和外部方法均属于同一事务 ,只要一个方法回滚,整个事务均回滚。
|
||||
|
||||
举个例子:如果我们上面的`aMethod()`和`bMethod()`使用的都是`PROPAGATION_REQUIRED`传播行为的话,两者使用的就是同一个事务,只要其中一个方法回滚,整个事务均回滚。
|
||||
|
||||
@ -395,11 +402,10 @@ Class B {
|
||||
|
||||
如果当前存在事务,就在嵌套事务内执行;如果当前没有事务,就执行与`TransactionDefinition.PROPAGATION_REQUIRED`类似的操作。也就是说:
|
||||
|
||||
1. 在外部方法开启事务的情况下,在内部开启一个新的事务,作为嵌套事务存在。
|
||||
2. 如果外部方法无事务,则单独开启一个事务与 PROPAGATION_REQUIRED 类似
|
||||
这里还是简单举个例子:
|
||||
- 在外部方法开启事务的情况下,在内部开启一个新的事务,作为嵌套事务存在。
|
||||
- 如果外部方法无事务,则单独开启一个事务,与 `PROPAGATION_REQUIRED` 类似。
|
||||
|
||||
如果 `bMethod()` 回滚的话,`aMethod()`也会回滚。
|
||||
这里还是简单举个例子:如果 `bMethod()` 回滚的话,`aMethod()`也会回滚。
|
||||
|
||||
```java
|
||||
Class A {
|
||||
@ -433,7 +439,7 @@ Class B {
|
||||
|
||||
更多关于事务传播行为的内容请看这篇文章:[《太难了~面试官让我结合案例讲讲自己对 Spring 事务传播行为的理解。》](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247486668&idx=2&sn=0381e8c836442f46bdc5367170234abb&chksm=cea24307f9d5ca11c96943b3ccfa1fc70dc97dd87d9c540388581f8fe6d805ff548dff5f6b5b&token=1776990505&lang=zh_CN#rd)
|
||||
|
||||
#### 3.3.2 事务隔离级别
|
||||
#### 事务隔离级别
|
||||
|
||||
`TransactionDefinition` 接口中定义了五个表示隔离级别的常量:
|
||||
|
||||
@ -454,25 +460,25 @@ public interface TransactionDefinition {
|
||||
```java
|
||||
public enum Isolation {
|
||||
|
||||
DEFAULT(TransactionDefinition.ISOLATION_DEFAULT),
|
||||
DEFAULT(TransactionDefinition.ISOLATION_DEFAULT),
|
||||
|
||||
READ_UNCOMMITTED(TransactionDefinition.ISOLATION_READ_UNCOMMITTED),
|
||||
READ_UNCOMMITTED(TransactionDefinition.ISOLATION_READ_UNCOMMITTED),
|
||||
|
||||
READ_COMMITTED(TransactionDefinition.ISOLATION_READ_COMMITTED),
|
||||
READ_COMMITTED(TransactionDefinition.ISOLATION_READ_COMMITTED),
|
||||
|
||||
REPEATABLE_READ(TransactionDefinition.ISOLATION_REPEATABLE_READ),
|
||||
REPEATABLE_READ(TransactionDefinition.ISOLATION_REPEATABLE_READ),
|
||||
|
||||
SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE);
|
||||
SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE);
|
||||
|
||||
private final int value;
|
||||
private final int value;
|
||||
|
||||
Isolation(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
Isolation(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int value() {
|
||||
return this.value;
|
||||
}
|
||||
public int value() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
@ -498,23 +504,23 @@ mysql> SELECT @@tx_isolation;
|
||||
+-----------------+
|
||||
```
|
||||
|
||||
~~这里需要注意的是:与 SQL 标准不同的地方在于 InnoDB 存储引擎在 **REPEATABLE-READ(可重读)** 事务隔离级别下使用的是Next-Key Lock 锁算法,因此可以避免幻读的产生,这与其他数据库系统(如 SQL Server)是不同的。所以说InnoDB 存储引擎的默认支持的隔离级别是 **REPEATABLE-READ(可重读)** 已经可以完全保证事务的隔离性要求,即达到了 SQL标准的 **SERIALIZABLE(可串行化)** 隔离级别。~~
|
||||
~~这里需要注意的是:与 SQL 标准不同的地方在于 InnoDB 存储引擎在 **REPEATABLE-READ(可重读)** 事务隔离级别下使用的是 Next-Key Lock 锁算法,因此可以避免幻读的产生,这与其他数据库系统(如 SQL Server)是不同的。所以说 InnoDB 存储引擎的默认支持的隔离级别是 **REPEATABLE-READ(可重读)** 已经可以完全保证事务的隔离性要求,即达到了 SQL 标准的 **SERIALIZABLE(可串行化)** 隔离级别。~~
|
||||
|
||||
🐛问题更正:**MySQL InnoDB的REPEATABLE-READ(可重读)并不保证避免幻读,需要应用使用加锁读来保证。而这个加锁读使用到的机制就是 Next-Key Locks。**
|
||||
🐛 问题更正:**MySQL InnoDB 的 REPEATABLE-READ(可重读)并不保证避免幻读,需要应用使用加锁读来保证。而这个加锁读使用到的机制就是 Next-Key Locks。**
|
||||
|
||||
因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是 **READ-COMMITTED(读取提交内容)** ,但是你要知道的是InnoDB 存储引擎默认使用 **REPEAaTABLE-READ(可重读)** 并不会有任何性能损失。
|
||||
因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是 **READ-COMMITTED(读取提交内容)** ,但是你要知道的是 InnoDB 存储引擎默认使用 **REPEAaTABLE-READ(可重读)** 并不会有任何性能损失。
|
||||
|
||||
InnoDB 存储引擎在 **分布式事务** 的情况下一般会用到 **SERIALIZABLE(可串行化)** 隔离级别。
|
||||
|
||||
🌈拓展一下(以下内容摘自《MySQL技术内幕:InnoDB存储引擎(第2版)》7.7章):
|
||||
🌈 拓展一下(以下内容摘自《MySQL 技术内幕:InnoDB 存储引擎(第 2 版)》7.7 章):
|
||||
|
||||
> InnoDB存储引擎提供了对XA事务的支持,并通过XA事务来支持分布式事务的实现。分布式事务指的是允许多个独立的事务资源(transactional resources)参与到一个全局的事务中。事务资源通常是关系型数据库系统,但也可以是其他类型的资源。全局事务要求在其中的所有参与的事务要么都提交,要么都回滚,这对于事务原有的ACID要求又有了提高。另外,在使用分布式事务时,InnoDB存储引擎的事务隔离级别必须设置为SERIALIZABLE。
|
||||
> InnoDB 存储引擎提供了对 XA 事务的支持,并通过 XA 事务来支持分布式事务的实现。分布式事务指的是允许多个独立的事务资源(transactional resources)参与到一个全局的事务中。事务资源通常是关系型数据库系统,但也可以是其他类型的资源。全局事务要求在其中的所有参与的事务要么都提交,要么都回滚,这对于事务原有的 ACID 要求又有了提高。另外,在使用分布式事务时,InnoDB 存储引擎的事务隔离级别必须设置为 SERIALIZABLE。
|
||||
|
||||
#### 3.3.3. 事务超时属性
|
||||
#### 事务超时属性
|
||||
|
||||
所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在 `TransactionDefinition` 中以 int 的值来表示超时时间,其单位是秒,默认值为-1。
|
||||
|
||||
#### 3.3.4. 事务只读属性
|
||||
#### 事务只读属性
|
||||
|
||||
```java
|
||||
package org.springframework.transaction;
|
||||
@ -543,14 +549,14 @@ public interface TransactionDefinition {
|
||||
|
||||
分享一下关于事务只读属性,其他人的解答:
|
||||
|
||||
1. 如果你一次执行单条查询语句,则没有必要启用事务支持,数据库默认支持 SQL 执行期间的读一致性;
|
||||
2. 如果你一次执行多条查询语句,例如统计查询,报表查询,在这种场景下,多条查询 SQL 必须保证整体的读一致性,否则,在前条 SQL 查询之后,后条 SQL 查询之前,数据被其他用户改变,则该次整体的统计查询将会出现读数据不一致的状态,此时,应该启用事务支持
|
||||
- 如果你一次执行单条查询语句,则没有必要启用事务支持,数据库默认支持 SQL 执行期间的读一致性;
|
||||
- 如果你一次执行多条查询语句,例如统计查询,报表查询,在这种场景下,多条查询 SQL 必须保证整体的读一致性,否则,在前条 SQL 查询之后,后条 SQL 查询之前,数据被其他用户改变,则该次整体的统计查询将会出现读数据不一致的状态,此时,应该启用事务支持
|
||||
|
||||
#### 3.3.5. 事务回滚规则
|
||||
#### 事务回滚规则
|
||||
|
||||
这些规则定义了哪些异常会导致事务回滚而哪些不会。默认情况下,事务只有遇到运行期异常(RuntimeException 的子类)时才会回滚,Error 也会导致事务回滚,但是,在遇到检查型(Checked)异常时不会回滚。
|
||||
这些规则定义了哪些异常会导致事务回滚而哪些不会。默认情况下,事务只有遇到运行期异常(`RuntimeException` 的子类)时才会回滚,`Error` 也会导致事务回滚,但是,在遇到检查型(Checked)异常时不会回滚。
|
||||
|
||||

|
||||

|
||||
|
||||
如果你想要回滚你定义的特定的异常类型的话,可以这样:
|
||||
|
||||
@ -558,15 +564,15 @@ public interface TransactionDefinition {
|
||||
@Transactional(rollbackFor= MyException.class)
|
||||
```
|
||||
|
||||
### 3.4. @Transactional 注解使用详解
|
||||
### @Transactional 注解使用详解
|
||||
|
||||
#### 1) `@Transactional` 的作用范围
|
||||
#### `@Transactional` 的作用范围
|
||||
|
||||
1. **方法** :推荐将注解使用于方法上,不过需要注意的是:**该注解只能应用到 public 方法上,否则不生效。**
|
||||
2. **类** :如果这个注解使用在类上的话,表明该注解对该类中所有的 public 方法都生效。
|
||||
3. **接口** :不推荐在接口上使用。
|
||||
|
||||
#### 2) `@Transactional` 的常用配置参数
|
||||
#### `@Transactional` 的常用配置参数
|
||||
|
||||
`@Transactional`注解源码如下,里面包含了基本事务属性的配置:
|
||||
|
||||
@ -604,15 +610,15 @@ public @interface Transactional {
|
||||
|
||||
**`@Transactional` 的常用配置参数总结(只列出了 5 个我平时比较常用的):**
|
||||
|
||||
| 属性名 | 说明 |
|
||||
| :---------- | :------------------------------------------------------------------------------------------- |
|
||||
| propagation | 事务的传播行为,默认值为 REQUIRED,可选的值在上面介绍过 |
|
||||
| isolation | 事务的隔离级别,默认值采用 DEFAULT,可选的值在上面介绍过 |
|
||||
| 属性名 | 说明 |
|
||||
| :---------- | :----------------------------------------------------------- |
|
||||
| propagation | 事务的传播行为,默认值为 REQUIRED,可选的值在上面介绍过 |
|
||||
| isolation | 事务的隔离级别,默认值采用 DEFAULT,可选的值在上面介绍过 |
|
||||
| timeout | 事务的超时时间,默认值为-1(不会超时)。如果超过该时间限制但事务还没有完成,则自动回滚事务。 |
|
||||
| readOnly | 指定事务是否为只读事务,默认值为 false。 |
|
||||
| rollbackFor | 用于指定能够触发事务回滚的异常类型,并且可以指定多个异常类型。 |
|
||||
| readOnly | 指定事务是否为只读事务,默认值为 false。 |
|
||||
| rollbackFor | 用于指定能够触发事务回滚的异常类型,并且可以指定多个异常类型。 |
|
||||
|
||||
#### 3)`@Transactional` 事务注解原理
|
||||
#### `@Transactional` 事务注解原理
|
||||
|
||||
面试中在问 AOP 的时候可能会被问到的一个问题。简单说下吧!
|
||||
|
||||
@ -648,7 +654,7 @@ public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
|
||||
|
||||
> `TransactionInterceptor` 类中的 `invoke()`方法内部实际调用的是 `TransactionAspectSupport` 类的 `invokeWithinTransaction()`方法。由于新版本的 Spring 对这部分重写很大,而且用到了很多响应式编程的知识,这里就不列源码了。
|
||||
|
||||
#### 4) Spring AOP 自调用问题
|
||||
#### Spring AOP 自调用问题
|
||||
|
||||
若同一类中的其他没有 `@Transactional` 注解的方法内部调用有 `@Transactional` 注解的方法,有`@Transactional` 注解的方法的事务会失效。
|
||||
|
||||
@ -673,28 +679,22 @@ private void method1() {
|
||||
|
||||
解决办法就是避免同一类中自调用或者使用 AspectJ 取代 Spring AOP 代理。
|
||||
|
||||
#### 5) `@Transactional` 的使用注意事项总结
|
||||
#### `@Transactional` 的使用注意事项总结
|
||||
|
||||
1. `@Transactional` 注解只有作用到 public 方法上事务才生效,不推荐在接口上使用;
|
||||
2. 避免同一个类中调用 `@Transactional` 注解的方法,这样会导致事务失效;
|
||||
3. 正确的设置 `@Transactional` 的 `rollbackFor` 和 `propagation` 属性,否则事务可能会回滚失败;
|
||||
3. 被 `@Transactional` 注解的方法所在的类必须被 Spring 管理,否则不生效;
|
||||
3. 底层使用的数据库必须支持事务机制,否则不生效;
|
||||
4. ......
|
||||
- `@Transactional` 注解只有作用到 public 方法上事务才生效,不推荐在接口上使用;
|
||||
- 避免同一个类中调用 `@Transactional` 注解的方法,这样会导致事务失效;
|
||||
- 正确的设置 `@Transactional` 的 `rollbackFor` 和 `propagation` 属性,否则事务可能会回滚失败;
|
||||
- 被 `@Transactional` 注解的方法所在的类必须被 Spring 管理,否则不生效;
|
||||
- 底层使用的数据库必须支持事务机制,否则不生效;
|
||||
- ......
|
||||
|
||||
## 4. Reference
|
||||
## 参考
|
||||
|
||||
1. [总结]Spring 事务管理中@Transactional 的参数:[http://www.mobabel.net/spring 事务管理中 transactional 的参数/](http://www.mobabel.net/spring事务管理中transactional的参数/)
|
||||
|
||||
2. Spring 官方文档:[https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html](https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html)
|
||||
|
||||
3. 《Spring5 高级编程》
|
||||
|
||||
4. 透彻的掌握 Spring 中@transactional 的使用: [https://www.ibm.com/developerworks/cn/java/j-master-spring-transactional-use/index.html](https://www.ibm.com/developerworks/cn/java/j-master-spring-transactional-use/index.html)
|
||||
|
||||
5. Spring 事务的传播特性:[https://github.com/love-somnus/Spring/wiki/Spring 事务的传播特性](https://github.com/love-somnus/Spring/wiki/Spring事务的传播特性)
|
||||
|
||||
6. [Spring 事务传播行为详解](https://segmentfault.com/a/1190000013341344) :[https://segmentfault.com/a/1190000013341344](https://segmentfault.com/a/1190000013341344)
|
||||
|
||||
7. 全面分析 Spring 的编程式事务管理及声明式事务管理:[https://www.ibm.com/developerworks/cn/education/opensource/os-cn-spring-trans/index.html](https://www.ibm.com/developerworks/cn/education/opensource/os-cn-spring-trans/index.html)
|
||||
- [总结]Spring 事务管理中@Transactional 的参数:[http://www.mobabel.net/spring 事务管理中 transactional 的参数/](http://www.mobabel.net/spring事务管理中transactional的参数/)
|
||||
- Spring 官方文档:[https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html](https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html)
|
||||
- 《Spring5 高级编程》
|
||||
- 透彻的掌握 Spring 中@transactional 的使用: [https://www.ibm.com/developerworks/cn/java/j-master-spring-transactional-use/index.html](https://www.ibm.com/developerworks/cn/java/j-master-spring-transactional-use/index.html)
|
||||
- Spring 事务的传播特性:[https://github.com/love-somnus/Spring/wiki/Spring 事务的传播特性](https://github.com/love-somnus/Spring/wiki/Spring事务的传播特性)
|
||||
- [Spring 事务传播行为详解](https://segmentfault.com/a/1190000013341344) :[https://segmentfault.com/a/1190000013341344](https://segmentfault.com/a/1190000013341344)
|
||||
- 全面分析 Spring 的编程式事务管理及声明式事务管理:[https://www.ibm.com/developerworks/cn/education/opensource/os-cn-spring-trans/index.html](https://www.ibm.com/developerworks/cn/education/opensource/os-cn-spring-trans/index.html)
|
||||
|
||||
|