spring的事务管理

2021/8/14 6:06:08

本文主要是介绍spring的事务管理,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

点赞再看,养成习惯,微信搜索「小大白日志」关注这个搬砖人。

文章不定期同步公众号,还有各种一线大厂面试原题、我的学习系列笔记。

事务的4种隔离级别,7种事务传播级别

mysql的特性?

Spring事务管理基于底层数据库本身的事务处理机制,mysql事务具备ACID四种特性,ACID是Atomic(原子性)、Consistency(一致性)、Isolation(隔离性)和Durability(持久性)

  • 原子性:事务执行要么全部成功,要么全部失败,不存在第三种状态
  • 一致性:事务完成后,数据库中的变化按期待的那样完成,保证了事务的原子性却不一定能保证一致性
  • 隔离性:并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间,某个并发事务所做的修改必须与任何其他并发事务所做的修改隔离,事务查看数据更新时,数据所处的状态要么是另一事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看到中间状态的数据
  • 持久性:只要事务成功结束,它对数据库所做的更新就必须永久保存下来

如果不考虑mysql事务的隔离级别,会发生什么问题?

会发生脏读、不可重复读、幻读(简称三读)

  • 脏读

a事务正在访问数据,并且对“数据”进行了修改改为“数据1”,但这种修改还没有提交到数据库中,这时b事务也访问了这个“数据1”,然后使用了这个“数据1”。因为这个“数据1”的还没有被a事务提交,a事务可能还要对该“数据1”进行修改,故b事务读取到的这个“数据1”就是脏数据,对脏数据所做的操作是不正确的。

  • 不可重复读

在a事务内,需要多次读同一数据,在a事务还没有执行结束时,b事务也访问了这个数据,那么在a事务中的两次读取该数据之间,由于b事务的修改使a事务两次读到的该数据可能不一样,这样就发生了在一个事物内两次连续读到的该数据是不一样的,此为不可重复读。

  • 幻读

事务A第一次查询有N条记录,第二次查询却有N+1(N-1)条记录,对A来说这太诡异了,因为B事务在A事务的两次操作之间增加(减少)了一条数据,导致A事务两次操作得到的数据不同,此为幻读

对比脏读和不可重复读:脏读是读取了其他事务未提交的数据,不可重复度是读取了其他事务已提交的数据。

对比幻读和不可重复读:不可重复读针对的是某条数据,幻读针对的是一批多条数据

由于以上问题频发,所以才有了mysql事务的四种事务隔离级别:
  • uncommited:最低的事务隔离级别,它允许另外一个事务可以看到这个事务未提交的数据,不能解决三读的任何一种【对应spring的@Transactional(isolation = Isolation.READ_UNCOMMITTED)】
  • commited:保证一个事物提交后,数据才能被另外一个事务读取,另外一个事务不能读取该事物未提交的数据,只能解决脏读(oracle的默认事务隔离级别)【对应spring的@Transactional(isolation = Isolation.READ_COMMITTED)】
  • repeatable:只能解决脏读、不可重复读(mysql默认事务隔离级别)【对应spring的@Transactional(isolation = Isolation.REPEATABLE_READ)】
  • serializable:这是花费最高代价但最可靠的事务隔离级别,事务被处理为顺序执行,能解决脏读、不可重复读幻读【对应spring的@Transactional(isolation = Isolation.SERIALIZABLE)】

spring中事务的传播级别是怎样的?

常见的有7种事务传播级别:
Propagation.REQUIRED、
Propagation.SUPPORTS、
Propagation.MANDATORY、
Propagation.REQUIRES_NEW、
Propagation.PROPAGATION_NESTED、
Propagation.PROPAGATION_NOT_SUPPORTED、
Propagation.NEVER、

  • Propagation.REQUIRED:如果当前方法上下文有事务则抛弃自身的事务加入到当前事务,如果当前方法上下文没有事务则新建一个事务。如下若调用A,因为A一开始没有事务,故新建事务,然后A调用B,B加入A的事务,不管A、B谁抛异常它们都会同时回滚;若直接调用B,则B一开始没有事务,会新建事务
@Transactional(propagation=Propagation.REQUIRED)
public void A(){
    B();
}
@Transactional(propagation=Propagation.REQUIRED)
public void B(){
    ...
}
  • Propagation.SUPPORTS:若当前方法上下文有事务则加入,若当前方法上下文没有事务则就以无事务状态运行0。如下若调用A,因为A一开始没有事务,故新建事务,然后A调用B,B加入A的事务,不管A、B谁抛异常它们都会同时回滚;若直接调用B,则B一开始没有事务,就会以无事务的状态运行
@Transactional(propagation = Propagation.REQUIRED)
public void A() {
 B();
}
@Transactional(propagation = Propagation.SUPPORTS)
public void B() {
    ...
}
  • Propagation.MANDATORY:若当前方法上下文有事务则加入,没有事务则抛出异常,即强制要求需要事务才行执行。如下若调用A,因为A一开始没有事务,故新建事务,然后A调用B,B加入A的事务,不管A、B谁抛异常它们都会同时回滚;若直接调用B,则B一开始没有事务,抛出异常IllegalTransactionStateException
@Transactional(propagation = Propagation.REQUIRED)
public void A() {
 B();
}
@Transactional(propagation = Propagation.MANDATORY)
public void B() {
   ...
}
  • Propagation.REQUIRES_NEW:无论当前方法上下文有没有事务,都会新建一个新事务去执行,新建的事务与方法上下文的其他事务无关,各自独立,谁抛异常就回滚谁的,互不影响。如下若调用A,因为A一开始没有事务,故新建事务,然后A调用B,B会把A的事务挂起然后新建一个自己的事务;若直接调用B,则B直接新建一个新事务去执行。若A抛异常,B没抛,则A回滚,不会影响B,所以B是不会回滚的
@Transactional(propagation = Propagation.REQUIRED)
public void A() {
 B();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void B() {
    ...
}
  • Propagation.PROPAGATION_NESTED:NESTED=嵌套,若当前方法上下文有事务则加入,若当前方法上下文没有事务则PROPAGATION_NESTED等价于REQUIRED,即新建一个事务再运行。如下若调用A,因为A一开始没有事务,故新建事务,然后A调用B,B加入A的事务,当A抛异常时A、B都会回滚,但B抛异常时不会影响A,从而只有B自己回滚(这是和REQUIRES_NEW最大的区别);若直接调用B,则B一开始没有事务,就会像REQUIRED一样新建一个事务再运行
@Transactional(propagation = Propagation.REQUIRED)
public void A() {
 B();
}
@Transactional(propagation = Propagation.PROPAGATION_NESTED)
public void B() {
    ...
}
  • Propagation.PROPAGATION_NOT_SUPPORTED:无论当前方法的上下文是否有事务,都会以无事务状态执行(对于上下文已存在事务的不会抛出异常)。如下若调用A,因为A一开始没有事务,故新建事务,然后A调用B,B会把A的事务挂起然后以无事务状态执行;若直接调用B,则B直接以无事务状态执行。
@Transactional(propagation = Propagation.REQUIRED)
public void A() {
 B();
}
@Transactional(propagation = Propagation.PROPAGATION_NOT_SUPPORTED)
public void B() {
    ...
}
  • Propagation.NEVER:无论当前方法的上下文是否有事务,都会以无事务状态执行(对于上下文已存在事务的抛出异常,这是和PROPAGATION_NOT_SUPPORTED最大的区别);如下若调用A,因为A一开始没有事务,故新建事务,然后A调用B,B会抛出异常;若直接调用B,则B直接以无事务状态执行。
@Transactional(propagation = Propagation.REQUIRED)
public void A() {
 B();
}
@Transactional(propagation = Propagation.NEVER)
public void B() {
    ...
}

对比:

(1)Propagation.REQUIRES_NEW和 Propagation.PROPAGATION_NESTED:REQUIRES_NEW是新建事务,PROPAGATION_NESTED是可以嵌套在别的事务中;REQUIRES_NEW的事务是各自完全独立互不影响的,谁也不决定谁回滚,只有自身事务抛异常时才会回滚;而PROPAGATION_NESTED则当外层事务抛异常时必然会回滚内层事务,可内存事务抛异常时不会对外层事务造成影响。所以它们一个是真正地互不影响,一个是假的互不影响

(2)Propagation.PROPAGATION_NOT_SUPPORTED和 Propagation.NEVER:它们两个都是不支持事务的,唯一区别是在上下文已经存在事务的时,PROPAGATION_NOT_SUPPORTED和会把事务挂起,而NEVER则会抛异常

有哪几种事务失效的场景?

共6种。

  • 数据库引擎不支持事务:比如mysql的myisam引擎不支持事务,而默认的innodb则支持
  • 异常被try catch吞掉了:方法只有抛出RuntimeException异常及其子类异常、Error异常及其子类异常,并且在方法返回的时候异常没有被catch住,才会回滚
  • @Transactional的rollbackFor属性设置错误:只有抛出RuntimeException异常及其子类异常、Error异常及其子类异常时,才会回滚,其他异常的回滚需要用rollbackFor指定
  • @Transactional的propagation属性设置错误:比如当前方法的事务传播级别为Propagation.SUPPORTS,单独执行时,它一开始没有事务,则直接以无事务的方式运行
  • @Transactional加在了非public方法上:Spring AOP 代理只会获取public方法上的@Transactional配置信息,其他的如private、protected方法上使用@Transactional无效
  • 外部调用A方法,A再调用同类中的B方法,A方法没有开启事务而B方法开启了:Spring AOP代理决定了只有当外部调用某个方法时,该方法的事务在抛出异常时才可以回滚,B方法被同类的A方法调用,且A没有开启事务所以A不会回滚,B也不会回滚

OK,如果文章哪里有错误或不足,欢迎各位留言。

创作不易,各位的「三连」是二少创作的最大动力!我们下期见!



这篇关于spring的事务管理的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程