【JavaEE】Spring事务(2)
文章目录
- 【JavaEE】Spring事务(2)
- 1. @Transactional 参数介绍
- 1.1 value 和 transactionManager
- 1.2 timeout
- 1.3 readOnly
- 1.4 后面四个
- 1.5 isolation 与 propagation
- 2. Spring 事务隔离级别 - isolation
- 2.1 MySQL事务隔离级别
- 2.2 MySQL默认有没有解决幻读
- 2.3 Spring的五种事务隔离级别
- 3. Spring 事务传播机制 - propagation
- 3.1 事务传播机制是什么?
- 3.2 为什么需要事务传播机制?
- 3.2.1 事务隔离级别
- 3.2.2 事务传播机制
- 3.3 Spring 事务的七种事务传播机制
- 3.3.1 propagation的七个取值
- 3.3.2 propagation的七个取值各自的作用
- 3.4 支持当前事务的三种传播机制
- 3.4.1 REQUIRED
- 3.4.2 REQUIRED例子
- 3.4.3 SUPPORTS
- 3.4.4 SUPPORTS例子
- 3.4.5 MANDATORY
- 3.4.6 MANDATORY例子
- 3.5 不支持当前事务的三种传播机制
- 3.5.1 REQUIRES_NEW
- 3.5.2 NOT_SUPPORTED 与 NEVER
- 3.6 嵌套事务
- 3.6.1 NESTED
- 3.6.2 NESTED例子
- 3.6.3 加入事务和嵌套事务的区别
- 3.6.4 嵌套事务的逻辑(加入事务和嵌套事务的区别:有无保存点)
参数 | 作用 |
---|---|
value | 当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器 |
transactionManager | 当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器 |
isolation | 事务的隔离级别.默认值为solation.DEFAULT |
propagation | 事务的传播机制,默认值为 Propagation.REQUIRED |
timeout | 事务的超时时间,默认值为-1.如果超过该时间限制但事务还没有完成,则自动回滚事务 |
readOnly | 指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置read-only为 true. |
rollbackFor | 用于指定能够触发事务回滚的异常类型,可以指定多个异常类型 |
rollbackForClassName | 用于指定能够触发事务回滚的异常类型,可以指定多个异常类型 |
noRollbackFor | 抛出指定的异常类型,不回滚事务,也可以指定多个异常类型 |
noRollbackForClassName | 抛出指定的异常类型,不回滚事务,也可以指定多个异常类型 |
其实默认就是一个Spring事务管理器,但是如果你是多个数据源等情况下,是可能用到多个事务管理器的,所以要使用哪个事务管理器,用这两个属性来区分(两个的作用一致,同时存在就是为了保证兼容性呗)
目前只看作只有一个事务管理器,之后遇到再说~
指定超时时间:
只是读操作的话,其实不需要回滚,但是又需要配合一些事务的传播行为以及保证一些查询结果一致性,就可以设置为true去优化性能,了解即可
无非就是:
并且可以传数组…
而
随后重点讲解
Spring设置事务隔离解绑是通过@Transactional的isolation属性,并且用Isolation枚举类去访问设置常量
补充说明:本文一些具体细节就不讲了,比如MySQL的知识
- 脏读:一个事务读取到了另⼀个事务修改的数据之后,后⼀个事务又进行了回滚操作,从而导致第⼀个事务读取的数据是错误的。
- 不可重复读:⼀个事务两次查询得到的结果不同,因为在两次查询中间,有另⼀个事务把数据修改了。
- 幻读:⼀个事务两次查询中得到的结果集不同,因为在两次查询中另⼀个事务有新增了⼀部分数据。
四种隔离级别对着三种情况的应对情况是这样的:
事务隔离级别(isolation) | 脏读问题 | 不可重复读问题 | 幻读 |
---|---|---|---|
READ UNCOMMITTED:读未提交 | 没有解决 | 没有解决 | 没有解决 |
READ COMMITTED:读已提交 | 解决 | 没有解决 | 没有解决 |
REPEATABLE READ:可重复读 | 解决 | 解决 | 有解决,但没完全解决 |
SERIALIZABLE:序列化 | 解决 | 解决 | 解决 |
MySQL默认:
select @@global.tx_isolation,@@tx_isolation;
有,但是没有彻底解决
对于MySQL默认是RR(REPEATABLE READ:可重复读)+ MVCC(Multi-VersionConcurrency Control:多版本并发控制) 去解决幻读的,对于
所以要想彻底解决幻读:
其实其中四种跟MySQL一致:
而多了的一种就是DEFAULT:含义就是,以配置的数据库的全局事务隔离级别为准~
@Transactional(isolation = Isolation.DEFAULT)
//默认设置
网络资料:
如果使用
@Transactional
注解**设置了一个在当前数据库中不支持的隔离级别**,会发生下列情况之一:
- 编译错误:编译过程中可能会出现错误,指示该隔离级别不可用。
- 运行时异常:编译器无法在编译时捕获到错误,那么在运行时可能会抛出异常。具体的异常类型和处理方式取决于使用的事务管理框架和数据库驱动程序。
通常情况下,当设置了一个不支持的隔离级别时,事务管理框架会尝试使用一个与之最接近的可用隔离级别。例如,假设设置的隔离级别是"SERIALIZABLE"
但数据库只支持"READ_COMMITTED"和"REPEATABLE_READ"两个隔离级别。在这种情况下,事务管理框架可能会自动选择使用"REPEATABLE_READ"作为隔离级别。
但是需要注意的是,不同的事务管理框架和数据库驱动程序的行为可能会有所不同。因此,建议在设置隔离级别之前,先了解所使用的数据库是否支持所需的隔离级别,并确保事务管理框架和数据库驱动程序的兼容性。
Spring 事务传播机制定义了多个事务的方法,相互调用时,事务是如何在这些方法之间进行传递的
传播行为定义了事务方法之间的事务边界,控制着事务方法在调用过程中对事务的影响
不必纠结传播这个资源,它就只是个词语罢了,其含义就是 事务方法被调用时进行的“行为逻辑”
- propagation传播就是事务扩展嘛,就是因为调用了方法,事务就要扩展(你可以理解为调用者事务和被调用者事务是extends的关系)了,要怎么扩展(事务之间的继承规则是咋样的)就是行为逻辑的机制咯
事务隔离级别解决的问题是多个事务同时调用数据库的问题,即并发事务执行的可控性和稳定性:
事务传播机制解决的则是保证了一个事务在多个调用方法的可控性和稳定性
举一个例子,事务A调用事务B,那么整体看成一个事务呢,还是分开来看,还是要咋样咋样,这些都是要规定的,而我们就是通过设置一个事务的传播机制,去控制,达到我们想要的效果~
一样的,Propagation也是个枚举类型,通过访问其内部常量来给propagation赋值:
Spring事务的默认值为:Propagation.REQUIRED
@Transactional(propagation = Propagation.REQUIRED)
// 默认设置
propagation | 作用 |
---|---|
REQUIRED | 默认的事务传播级别,它表示如果当前存在事务,则加入该事务;如果当前没有事务,则创建⼀个新的事务。 |
SUPPORTS | 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的⽅式继续运行。 |
MANDATORY | (mandatory:强制性)如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。 |
REQUIRES_NEW | 表示创建⼀个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部方法是否开启事务,Propagation.REQUIRES_NEW 修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。 |
NOT_SUPPORTED | 以非事务方式运行,如果当前存在事务,则把当前事务挂起。 |
NEVER | 以非事务方式运行,如果当前存在事务,则抛出异常。 |
NESTED | 如果当前存在事务,则创建⼀个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 PROPAGATION_REQUIRED。 |
对于七种传播机制,可以分为三类:
非事务方式运行代表的含义就是,这个方法不是个事务,但是参与了事务传播机制的逻辑规范
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// 代码逻辑
}
methodA()
方法使用REQUIRED传播机制
methodA()
将会使用该事务,或者说是加入该事务(融合成一个大事务)methodA()
创建一个新的事务调用链就是这样的:
没有报异常的现象:
在insert方法返回前加异常:
控制台:
无新增,代表已回滚
验证,不存在事务也可以创建:
效果:
控制台:
无新增
@Transactional(propagation = Propagation.SUPPORTS)
public void methodB() {
// 代码逻辑
}
methodB()
方法使用SUPPORTS传播机制
methodB()
将会使用该事务存在事务的情况效果:
无新增
不存在事务的情况:
效果:
新增数据,代表回滚失败
补充一点,你会发现一些id不连续,其实就是因为空着的id加过了,只不过被回滚了
分析:
一开始没有事务,最终导致,这些“妈宝”一个都不创建事务,没有事务自然就没有回滚
@Transactional(propagation = Propagation.MANDATORY)
public void methodC() {
// 代码逻辑
}
methodC()
方法使用MANDATORY传播机制
methodC()
将会使用该事务效果:
无新增
报异常情况的演示:
效果:
- 这里的异常就不是算术异常了
压根没添加进入那两个方法,自然没有添加数据
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodD() {
// 代码逻辑
}
methodD()
方法使用REQUIRES_NEW传播机制
methodD()
创建一个新的事务,并挂起当前事务(如果存在)不存在的话,就是简单的创建一个事务~
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void methodE() {
// 代码逻辑
}
@Transactional(propagation = Propagation.NEVER)
public void methodF() {
// 代码逻辑
}
methodE()
方法使用NOT_SUPPORTED传播机制
methodF()
方法使用NEVER传播机制
非事务方式运行代表的含义就是,这个方法不是个事务,但是参与了事务传播机制的逻辑规范
不支持当前事务的这三种传播机制,存在即合理,只是那种情景我们还没遇到呢~
@Transactional(propagation = Propagation.NESTED)
public void methodG() {
// 代码逻辑
}
methodG()
方法使用NESTED传播机制
调用链是这样的:
效果:
只增加了一条~
- 代表insert方法是回滚了的,而testService方法事务提交成功
- 而insert也是有返回值的,因为try后的代码还是会继续运行
在前面加入事务的例子可以看出,加入事务就相当于融合成大事务,看成一个整体,在任何一个地方回滚,影响的都是全部(不再做演示)
而嵌套事务NEXTED,则允许整个事务,部分事务回滚(允许部分代码失败)
嵌套这个词就体现在这特性:
内部事务就是个部件吧,相对于整个事务
加入事务:
嵌套事务:
可以将嵌套事务理解为“尝试”的动作,这个方法失败了就失败了,没关系,咱还是一条好汉~
嵌套事务之所以能够实现部分事务的回滚,是因为事务中有⼀个保存点(savepoint)的概念:
实心黑点就相当于保存点~
REQUIRED 是加⼊到当前事务中,并没有创建事务的保存点,因此出现了回滚就是整个事务回滚, 这也是嵌套事务和加入事务的区别
官方文档:MySQL :: MySQL 5.7 Reference Manual :: 13.3.4 SAVEPOINT, ROLLBACK TO SAVEPOINT, and RELEASE SAVEPOINT Statements
文章到此结束!谢谢观看
可以叫我 小马,我可能写的不好或者有错误,但是一起加油鸭!代码:事务/src/main · 游离态/马拉圈2023年8月 - 码云 - 开源中国 (gitee.com)
实际情况可能简单,也可能复杂,希望你能通过已学知识去“排列组合”,去实现和分析!