SpringBoot 使用 @Transactional 注解配置事务[通俗易懂]

SpringBoot项目中需要配置事务管理,所以在这里系统地整理下关于@Transactional注解相关的知识!1、详细介绍事务管理是应用系统开发中必不可少的一部分。Spring为事务管理提供了丰富的功能支持。Spring事务管理分为编程式和声明式的两种方式。编程式事务指的是通过编码方式实现事务;声明式事务基于AOP,将具体业务逻辑与事务处理解耦。声明式事务管理使业务代…

大家好,又见面了,我是你们的朋友全栈君。

SpringBoot项目中需要配置事务管理,所以在这里系统地整理下关于@Transactional 注解相关的知识!


1、详细介绍

事务管理是应用系统开发中必不可少的一部分。Spring 为事务管理提供了丰富的功能支持。Spring 事务管理分为编程式声明式的两种方式。
编程式事务指的是通过编码方式实现事务;声明式事务基于 AOP,将具体业务逻辑与事务处理解耦。声明式事务管理使业务代码逻辑不受污染, 因此在实际使用中声明式事务用的比较多。声明式事务有两种方式,一种是在配置文件(xml)中做相关的事务规则声明,另一种是基于 @Transactional 注解的方式。本文将着重介绍基于 @Transactional 注解的事务管理。

@Transactional 可以作用于接口接口方法以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。

虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。

默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。


2、使用方法

:SpringBoot项目会自动配置一个 DataSourceTransactionManager,所以我们只需在方法(或者类)加上 @Transactional 注解,就自动纳入 Spring 的事务管理了

如下在方法加上 @Transactional 注解:

@Transactional
public void insertUser() {
    User user = new User("abysscat");
    userMapper.insertOneUser(user);          //向数据库插入一条记录

    throw new RuntimeException("发生异常");   //手动模拟抛出异常
}

抛出异常之后,事务会自动回滚,数据不会插入到数据库。


3、Spring事务的默认回滚机制

Spring的事务管理默认是针对***unchecked exception**回滚,也就是默认对Error异常和RuntimeException异常以及其子类进行事务回滚,且必须抛出异常,若使用try-catch对其异常捕获则不会进行回滚!(Error异常和RuntimeException异常抛出时不需要方法调用throws或try-catch语句);而checked exception*** 则必须用try语句块进行处理或者把异常交给上级方法处理总之就是必须写代码处理它,所以必须在service捕获异常,然后再次抛出,这样事务方才起效。


4、Java 异常介绍

Java标准库内建了一些通用的异常,这些类以 Throwable 为顶层父类。
Throwable又派生出 Error 类和 Exception 类。

错误:Error类以及他的子类的实例,代表了JVM本身的错误。错误不能被程序员通过代码处理,Error很少出现。因此,程序员应该关注Exception为父类的分支下的各种异常类。

异常:Exception以及他的子类,代表程序运行时发送的各种不期望发生的事件。可以被Java异常处理机制使用,是异常处理的核心。

这里写图片描述
注意:上图中有个错误 - ClassNotFoundException不属于运行时异常!

总体上我们根据Javac对异常的处理要求,将异常类分为2类。

非检查异常(unckecked exception):Error 和 RuntimeException 以及他们的子类。javac在编译时,不会提示和发现这样的异常,不要求在程序处理这些异常。所以如果愿意,我们可以编写代码处理(使用try…catch…finally)这样的异常,也可以不处理。对于这些异常,我们应该修正代码,而不是去通过异常处理器处理 。这样的异常发生的原因多半是代码写的有问题。如***除0错误ArithmeticException***,错误的***强制类型转换错误ClassCastException***,数组索引越界ArrayIndexOutOfBoundsException,使用了***空对象NullPointerException***等等。

检查异常(checked exception):除了Error 和 RuntimeException的其它异常。javac强制要求程序员为这样的异常做预备处理工作(使用try…catch…finally或者throws)。在方法中要么用try-catch语句捕获它并处理,要么用throws子句声明抛出它,否则编译不会通过。这样的异常一般是由程序的运行环境导致的。因为程序可能被运行在各种未知的环境下,而程序员无法干预用户如何使用他编写的程序,于是程序员就应该为这样的异常时刻准备着。如SQLException , IOException,ClassNotFoundException 等。

需要明确的是:检查和非检查是对于javac来说的,这样就很好理解和区分了。


5、@Transactional 注解属性介绍

value 和 transactionManager 属性
它们两个是一样的意思。当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器。

propagation 属性
事务的传播行为,默认值为 Propagation.REQUIRED
可选的值有:

  • Propagation.REQUIRED

如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。

如a方法和b方法都添加了注解,使用默认传播模式,则a方法内部调用b方法,会把两个方法的事务合并为一个事务。

这里又会存在问题,如果b方法内部抛了异常,而a方法catch了b方法的异常,那这个事务还能正常运行吗?
答案是不行!会抛出异常:org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only,因为当ServiceB中抛出了一个异常以后,ServiceB会把当前的transaction标记为需要rollback。但是ServiceA中捕获了这个异常,并进行了处理,认为当前transaction应该正常commit。此时就出现了前后不一致,也就是因为这样,抛出了前面的UnexpectedRollbackException。

  • Propagation.SUPPORTS

如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。

  • Propagation.MANDATORY

如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。

  • Propagation.REQUIRES_NEW

重新创建一个新的事务,如果当前存在事务,暂停当前的事务。这个属性可以实现:
类A中的a方法加上默认注解@Transactional(propagation = Propagation.REQUIRED),类B中的b方法加上注解@Transactional(propagation = Propagation.REQUIRES_NEW),然后在a方法中调用b方法操作数据库,再在a方法最后抛出异常,会发现a方法中的b方法对数据库的操作没有回滚,因为Propagation.REQUIRES_NEW会暂停a方法的事务。

  • Propagation.NOT_SUPPORTED

以非事务的方式运行,如果当前存在事务,暂停当前的事务。

  • Propagation.NEVER

以非事务的方式运行,如果当前存在事务,则抛出异常。

  • Propagation.NESTED

和 Propagation.REQUIRED 效果一样。

isolation 属性
事务的隔离级别,默认值为 Isolation.DEFAULT。

可选的值有:

  • Isolation.DEFAULT
    使用底层数据库默认的隔离级别。

  • Isolation.READ_UNCOMMITTED

  • Isolation.READ_COMMITTED

  • Isolation.REPEATABLE_READ

  • Isolation.SERIALIZABLE

timeout 属性
事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。

readOnly 属性
指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。

rollbackFor 属性
用于指定能够触发事务回滚的异常类型,可以指定多个异常类型。

noRollbackFor 属性
抛出指定的异常类型,不回滚事务,也可以指定多个异常类型。

6、@Transactional事务几点注意

这里面有几点需要大家留意:
A. 一个功能是否要事务,必须纳入设计、编码考虑。不能仅仅完成了基本功能就ok。
B. 如果加了事务,必须做好开发环境测试(测试环境也尽量触发异常、测试回滚),确保事务生效。
C. 以下列了事务使用过程的注意事项,请大家留意。

1.不要在接口上声明@Transactional ,而要在具体类的方法上使用 @Transactional 注解,否则注解可能无效。

2.不要图省事,将@Transactional放置在类级的声明中,放在类声明,会使得所有方法都有事务。故@Transactional应该放在方法级别,不需要使用事务的方法,就不要放置事务,比如查询方法。否则对性能是有影响的。

3.使用了@Transactional的方法,对同一个类里面的方法调用, @Transactional无效。比如有一个类Test,它的一个方法A,A再调用Test本类的方法B(不管B是否public还是private),但A没有声明注解事务,而B有。则外部调用A之后,B的事务是不会起作用的。(经常在这里出错)

4.使用了@Transactional的方法,只能是public,@Transactional注解的方法都是被外部其他类调用才有效,故只能是public。道理和上面的有关联。故在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错,但事务无效。

5.spring的事务在抛异常的时候会回滚,如果是catch捕获了,事务无效。可以在catch里面加上throw new RuntimeException();

6.最后有个关键的一点:和锁同时使用需要注意:由于Spring事务是通过AOP实现的,所以在方法执行之前会有开启事务,之后会有提交事务逻辑。而synchronized代码块执行是在事务之内执行的,可以推断在synchronized代码块执行完时,事务还未提交,其他线程进入synchronized代码块后,读取的数据不是最新的。
所以必须使synchronized锁的范围大于事务控制的范围,把synchronized加到Controller层或者大于事务边界的调用层!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/127089.html原文链接:https://javaforall.net

(1)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 十大滤波算法总结「建议收藏」

    十大滤波算法总结「建议收藏」由于MPU6050的深入,我也学会了一些滤波算法,自己写了一些算法,收集了一些算法,供大家一起学习分享,我的代码都是经过反复试验,复制到Arduino中就能开跑的成品代码,移植到自己的程序中非常方便。而且都仔细研究了各个算法,把错误都修正了的,所以也算个小原创吧,在别人基础上的原创。1、限幅滤波法(又称程序判断滤波法)2、中位值滤波法3、算术平均滤波法4、递推平均滤波法(又称滑动平

    2022年6月14日
    30
  • 百科知识题库_趣味知识竞赛题库及答案

    百科知识题库_趣味知识竞赛题库及答案.西印度群岛位于哪个大洋的西岸?大西洋2.七大洲中面积第二大的是哪个洲?非洲3.“知之为知之,不知为不知,是知也。”是古代的哪位大教育家对学生讲的话?孔子4.“不以规矩,不能成方圆”是出自古代哪位著名儒家思想家之口?孟子5.我国法律规定年满多少周岁就有选举权和被选举权?18岁6.我国民航法规定,乘坐民航班机年满多少周岁的人不能购买儿童票?12岁7.演唱《桃花朵朵开》的马来西

    2025年8月28日
    7
  • Linux安装gcc的四种方法「建议收藏」

    Linux安装gcc的四种方法「建议收藏」相比于windows系统,Linux安装程序就比较复杂了,很多需要root用户才能安装。常见的有以下几种安装方法 源码安装 rpm包安装 yum安装(RedHat、CentOS) apt-get安装(debian,ubuntu) 源码安装以安装gcc为例,登陆https://gcc.gnu.org/,下载自己想要的版本的gcc安装包上传gcc-4.1.2.tar.gz到Linux服务器任意目录,解压解压目录执行shell命令./configurat.

    2022年5月26日
    89
  • 09 idea中 git从远程仓库克隆到本地[通俗易懂]

        从服务端拉取代码                                    

    2022年4月15日
    134
  • PHP获取网站中各文章的第一张图片的代码示例

    PHP获取网站中各文章的第一张图片的代码示例

    2021年10月30日
    42
  • linux的rwx权限,linux权限管理:rwx

    linux的rwx权限,linux权限管理:rwx标签:rwx权限管理简介:r,w,x对文件及目录进行权限管理,从而达到文件及目录管理。1、rwx对于文件而言:r:可读,可以使用类似cat等命令查看文件内容;w:可写,可以编辑或删除此文件;x:可执行,exacutable,可以命令提示符下当作命令提交给内核运行;2、rwx对于目录而言:r:可以对此目录执行ls以列出内部的所有文件;w:可以在此目录创建文件;x:可以使用cd切换进此目录,也可以使用…

    2022年6月10日
    62

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号