事务

什么是事务

1
保证业务操作完整性的一种数据库机制

事务(ACID)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 事务四大特性(ACID)

## 原子性(Atomicity)
原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚。
因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。

## 一致性(Consistency)
事务开始前和结束后,数据库的完整性约束没有被破坏。比如A向B转账,不可能A扣了钱,B却没收到

## 隔离性(lsolation)
隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。

## 持久性
持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。


如何控制事务

1
2
3
4
5
6
7
8
9
JDBC:
Connection.setAutoCommit(false); //开启
Connection.commit(); //提交
Connection.rollback(); //回滚

Mybatis: (sqlSeesion在底层封装了Connection)
Mybatis自动开启事务 //开启
sqlSession.commit(); //提交
sqlSession.rollback(); //回滚
1
2
3
# 小结
对于事务,有三个操作,开启、提交、回滚
控制事务的底层 都是Connection对象完成的

事务的属性

1
2
3
4
5
6
事务属性:描述事务特征的一系列值
1. 隔离属性
2. 传播属性
3. 只读属性
4. 超时属性
5. 异常属性

如何添加事务的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@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 {};//事务回滚规则

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public interface TransactionDefinition { //事务属性设置

//传播属性设置
int PROPAGATION_REQUIRED = 0;
int PROPAGATION_SUPPORTS = 1;
int PROPAGATION_MANDATORY = 2;
int PROPAGATION_REQUIRES_NEW = 3;
int PROPAGATION_NOT_SUPPORTED = 4;
int PROPAGATION_NEVER = 5;
int PROPAGATION_NESTED = 6;

//隔离级别属性设置
int ISOLATION_DEFAULT = -1;
int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;
int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;
int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;
int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;

//超时机制设置
int TIMEOUT_DEFAULT = -1;

int getPropagationBehavior();
int getIsolationLevel();
int getTimeout();

//是否只读属性
boolean isReadOnly();
String getName();

}

隔离属性

1
2
3
4
5
6
# 隔离属性
他描述了事务解决并发问题的特征
# 什么是并发
多个事务(用户)在同一时间,访问操作了相同的数据
# 如何解决并发
通过隔离属性解决,隔离属性中设置不同的值,解决并发处理过程中的问题

并发产生的问题

1
2
3
4
5
6
7
8
9
10
11
12
# 脏读
事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到数据时脏数据
解决方案 @Transational(isolation=Isolation.READ_COMMITED)
# 不可重复读
事务A多次读取同一数据,事务B在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果因此本事务先后读到的数据结果会不一致。
解决方案 @Transational(isolation=Isolation.REPEATABLE_READ)
本质:一把行锁
# 幻读
一个事务中,多次对整张表进行查询统计,但是结果不一样,会在本事务中产生数据不一致的问题
例如:事务 T1 对一个表中所有的行的某个数据项做了从“1”修改为“2”的操作 这 时事务 T2 又对这个表中插入了一行数据项,而这个数据项的数值还是为“1”并且 提交给数据库。 而操作事务 T1 的用户如果再查看刚刚修改的数据,会发现还 有跟没有修改一样,其实这行是从事务 T2 中添加的,就好像产生幻觉一样,这 就是发生了幻读。
解决方案 @Transational(isolation=Isolation.SERIALIZABLE)
本质:一把表锁
1
2
# 小结
不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表。

事务的隔离级别

事务隔离级别 脏读 不可重复读 幻读
读未提交 read - uncommitted
不可重复读 read - committed
可重复读 repeatable - read
串行化 serializable
1
2
3
4
5
6
7
8
9
10
11
# 读未提交
另一个事务修改了数据,但尚未提交,而本事务中的SELECT会读到这些未被提交的数据脏读

# 不可重复读
事务A多次读取同一数据,事务B在事务A多次读取的过程中。对数据作了更新并提交,导致事务A多次读取同一数据时,结果因此本事务先后两次读到的数据结果会不一致。

# 可重复读
在同一事务里,SELECT的结果是事务开始时时间点的状态,因此,同样的SELECT操作读到的结果会是一致的,但是会有幻读的现象

# 串行化
最高的隔离级别,在这个隔离级别下,不会产生任何异常。并发的事务,就像事务是在一个个按照顺序执行一样。

MySQL默认的事务隔离级别

1
MySQL默认的事务隔离级别  -- 可重复读 (repeatable - read)

传播属性

1
2
3
4
5
6
7
8
# 传播属性
他描述了事务解决嵌套问题的特征
# 在开发中出现的场景
在开发中,我们通常是service调用DAO,并且在业务中绝大部分都满足这种
但会出现service调用service,这是就有事务的嵌套了
# 出现的问题
在AService()中调用了两个BService()和CService()
如果在执行过程中,BService()正常执行,且已提交。但在CService()执行过程中出现错误,需要回滚,但BService()已经提交,无法回滚

image-20210308104857566

传播属性级别

传播属性的值 外部不存在事务 外部存在事务
REQUIRED(增删改) 开启新的事务 融合到外部事务中
SUPPORTS(查询) 不开启新的事务 融合到外部事物中

只读属性(readOnly)

1
2
3
4
5
# 只读属性
针对只进行查询操作的业务方法,可以加入只读属性,提供运行效率

# 默认值
false

超时属性(timeout)

1
2
3
4
5
6
7
8
# 超时属性
指定了事务等待的最长时间
# 为什么事务进行等待
当前事务访问数据是,有可能访问的数据被别的事务进行加锁的处理,那么此时本事务就必须进行等待
# 运用
@Transactional(timeout = )
# 默认值
-1 (最终有对应的数据库来指定)

异常属性

1
2
3
4
5
6
在Spring事务处理过程中
默认 对于RuntimeException及其子类 采用的是回滚的策略
默认 对于Exception及其子类 采用的是提交的策略

rollbackFor = {java.lang.Exception,...} Exception设置为回滚
noRollbackFor = {java.lang.RuntimeException,...} RuntimeException设置为提交

事务
https://johnjoyjzw.github.io/2020/09/06/事务/
Author
John Joy
Posted on
September 6, 2020
Licensed under