ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

spring的事务管理

2021-08-14 02:31:40  阅读:247  来源: 互联网

标签:事务管理 事务 调用 spring Transactional 回滚 Propagation public


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

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

事务的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,Transactional,回滚,Propagation,public
来源: https://www.cnblogs.com/mofes/p/15139723.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有