Springboot下事务管理的简单使用
关于事务管理的概念这里就不多介绍了,在我的博客“JDBC事务之理论篇”中也有介绍。
关于Spring的事务管理,主要是通过事务管理器来进行的。这里看个Spring事务管理的接口图:(来自博客https://www.cnblogs.com/yixianyixian/p/8372832.html)

大概就是TransactionDefinition接口的实现类对对事务的一些配置设置进行定义;PlatformTransaction接口的实现类,是对事务进行管理的,像commit啊rollback等操作;然后TransactionStatus接口的实现类,是获得事务的一些状态的。
然后,对于事务管理,Spring提供了声明式和编程式的方法。 所谓编程式,就是在业务逻辑代码中精确地定位事务管理,而声明式事务管理,是基于AOP的,也就是说可以将事务管理代码和业务逻辑代码解耦。
在SpringBoot中使用Spring的事务管理非常简单,相当于Spring声明式事务管理,而且是注释版的。
我们只需要用两个注解:
@EnableTransactionManagement @Transactional
@EnableTransactionManagement是要加载SpringBoot启动类上的,相当于是配置文件中的:
<!-- 声明式事务管理 配置事物的注解方式注入-->
<tx:annotation-driven transaction-manager="transactionManager"/>
而这个@Transaction是放在你要进行事务管理的类或者是方法上的。
在@Transaction注解中有一些属性可以设置:
@Transactional注解中常用参数说明
|
参数名称 |
功能描述 |
|
readOnly |
该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。例如:@Transactional(readOnly=true) |
|
rollbackFor |
该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。例如: 指定单一异常类:@Transactional(rollbackFor=RuntimeException.class) 指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class}) |
|
rollbackForClassName |
该属性用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。例如: 指定单一异常类名称:@Transactional(rollbackForClassName="RuntimeException") 指定多个异常类名称:@Transactional(rollbackForClassName={"RuntimeException","Exception"}) |
|
noRollbackFor |
该属性用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。例如: 指定单一异常类:@Transactional(noRollbackFor=RuntimeException.class) 指定多个异常类:@Transactional(noRollbackFor={RuntimeException.class, Exception.class}) |
|
noRollbackForClassName |
该属性用于设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。例如: 指定单一异常类名称:@Transactional(noRollbackForClassName="RuntimeException") 指定多个异常类名称: @Transactional(noRollbackForClassName={"RuntimeException","Exception"}) |
|
propagation |
该属性用于设置事务的传播行为,具体取值可参考表6-7。 例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true) |
|
isolation |
该属性用于设置底层数据库的事务隔离级别,事务隔离级别用于处理多事务并发的情况,通常使用数据库的默认隔离级别即可,基本不需要进行设置 |
|
timeout |
该属性用于设置事务的超时秒数,默认值为-1表示永不超时 |
这里要讲的也就设置的比较多的两个:
首先是propagation。翻译过来是传播行为。什么意思呢?
我们一般都是将事务设置在Service层 那么当我们调用Service层的一个方法的时候它能够保证我们的这个方法中执行的所有的对数据库的更新操作保持在一个事务中,在事务层里面调用的这些方法要么全部成功,要么全部失败。那么事务的传播特性也是从这里说起的。
如果你在你的Service层的这个方法中,除了调用了Dao层的方法之外,还调用了本类的其他的Service方法,那么在调用其他的Service方法的时候,这个事务是怎么规定的呢,我必须保证我在我方法里掉用的这个方法与我本身的方法处在同一个事务中,否则如果保证事物的一致性。事务的传播特性就是解决这个问题的,“事务是会传播的”在Spring中有针对传播特性的多种配置我们大多数情况下只用其中的一种:PROPGATION_REQUIRED:这个配置项的意思是说当我调用service层的方法的时候开启一个事务(具体调用那一层的方法开始创建事务,要看你的aop的配置),那么在调用这个service层里面的其他的方法的时候,如果当前方法产生了事务就用当前方法产生的事务,否则就创建一个新的事务。这个工作使由Spring来帮助我们完成的。
这里做一个各个Propagation的汇总:
1: PROPAGATION_REQUIRED
加入当前正要执行的事务不在另外一个事务里,那么就起一个新的事务
比如说,ServiceB.methodB的事务级别定义为PROPAGATION_REQUIRED, 那么由于执行ServiceA.methodA的时候,
ServiceA.methodA已经起了事务,这时调用ServiceB.methodB,ServiceB.methodB看到自己已经运行在ServiceA.methodA
的事务内部,就不再起新的事务。而假如ServiceA.methodA运行的时候发现自己没有在事务中,他就会为自己分配一个事务。
这样,在ServiceA.methodA或者在ServiceB.methodB内的任何地方出现异常,事务都会被回滚。即使ServiceB.methodB的事务已经被
提交,但是ServiceA.methodA在接下来fail要回滚,ServiceB.methodB也要回滚 2: PROPAGATION_SUPPORTS
如果当前在事务中,即以事务的形式运行,如果当前不再一个事务中,那么就以非事务的形式运行
这就跟平常用的普通非事务的代码只有一点点区别了。不理这个,因为我也没有觉得有什么区别 3: PROPAGATION_MANDATORY
必须在一个事务中运行。也就是说,他只能被一个父事务调用。否则,他就要抛出异常。 4: PROPAGATION_REQUIRES_NEW
这个就比较绕口了。 比如我们设计ServiceA.methodA的事务级别为PROPAGATION_REQUIRED,ServiceB.methodB的事务级别为PROPAGATION_REQUIRES_NEW,
那么当执行到ServiceB.methodB的时候,ServiceA.methodA所在的事务就会挂起,ServiceB.methodB会起一个新的事务,等待ServiceB.methodB的事务完成以后,
他才继续执行。他与PROPAGATION_REQUIRED 的事务区别在于事务的回滚程度了。因为ServiceB.methodB是新起一个事务,那么就是存在
两个不同的事务。如果ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚,ServiceB.methodB是不会回滚的。如果ServiceB.methodB失败回滚,
如果他抛出的异常被ServiceA.methodA捕获,ServiceA.methodA事务仍然可能提交。 5: PROPAGATION_NOT_SUPPORTED
当前不支持事务。比如ServiceA.methodA的事务级别是PROPAGATION_REQUIRED ,而ServiceB.methodB的事务级别是PROPAGATION_NOT_SUPPORTED ,
那么当执行到ServiceB.methodB时,ServiceA.methodA的事务挂起,而他以非事务的状态运行完,再继续ServiceA.methodA的事务。 6: PROPAGATION_NEVER
不能在事务中运行。假设ServiceA.methodA的事务级别是PROPAGATION_REQUIRED, 而ServiceB.methodB的事务级别是PROPAGATION_NEVER ,
那么ServiceB.methodB就要抛出异常了。 7: PROPAGATION_NESTED
理解Nested的关键是savepoint。他与PROPAGATION_REQUIRES_NEW的区别是,PROPAGATION_REQUIRES_NEW另起一个事务,将会与他的父事务相互独立,
而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。也就是说,如果父事务最后回滚,他也要回滚的。
而Nested事务的好处是他有一个savepoint。
一般用的较多就这个PROPAGATION_REQUIRED
然后还要讲的要给属性是: Isolation,就是确认隔离机制。
这个就和JDBC中的隔离机制基本一致,是为了防止drity reads、non-repeatable reads、phantom reads而有的机制,隔离级别常量这里也不细讲了,和我的博客中记录的“JDBC事务管理理论篇”中的基本一样。
好的下面上代码来看例子:
在我们的service层中的一个类中新建一个测试方法:
先在接口中添加声明:
public interface UserService {
Map<String, Object> selectList(Map<String, Object> map);
Map<String, Object> insert(User user);
Map<String, Object> update(User user);
Map<String, Object> delete(String id);
void transactionTest(User user1, User user2);
}
再看实现类:(其他方法的实现省略了)
@Service
public class UserServiceImpl implements UserService { @Autowired
private UserMapper userMapper; @Override
public void transactionTest(User user1, User user2) {
// TODO Auto-generated method stub
userMapper.insert(user1);
System.out.println(1/0);
userMapper.insert(user2);
} }
然后在我们service的新建方法中,我们两次调用userMapper的insert方法,也就是说进行了两次数据库的插入操作。并且在中间人为制造了一个异常。
先来看没进行事务管理的效果:
测试类:
@RunWith(SpringRunner.class) // SpringJUnit支持,由此引入Spring-Test框架支持!
@SpringBootTest //提供spring环境
public class TransactionTest { @Autowired
private UserService userService; @Test
public void testTransaction() {
User user1 = new User();
User user2 = new User(); user1.setId("111111");
user1.setUserAccount("tt");
user1.setUserName("user1");
user1.setUserPassword("12312312"); user2.setId("222222");
user2.setUserAccount("bb");
user2.setUserName("user2");
user2.setUserPassword("12312312"); userService.transactionTest(user1, user2);
} }
结果截图:
运行前的数据:

运行后:

可见,执行了一个insert之后,因为异常,后面那个insert就没执行了。
好的现在我们加入事务管理:
加入启动事务管理注解:
@SpringBootApplication(scanBasePackages={"com.stuPayment.*"})
@EnableTransactionManagement
@MapperScan("com.stuPayment.dao")
public class StuPaymentApplication {
public static void main(String[] args) {
SpringApplication.run(StuPaymentApplication.class, args);
}
}
加入Transactional注解:
可以加在类上,这样的话,就类中的所有方法都事务管理,这里我们加在方法上:
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor=Exception.class)
@Override
public void transactionTest(User user1, User user2) {
// TODO Auto-generated method stub
userMapper.insert(user1);
System.out.println(1/0);
userMapper.insert(user2);
}
再运行看效果:

因为这个时候是REQUIRED的传播级别,也就是说两个方法都是一个事务的,只要有错误,就会回滚。
最后再说几点注意的地方:
1、用 spring 事务管理器,由spring来负责数据库的打开,提交,回滚.默认遇到运行期例外(throw new RuntimeException("注释");)会回滚,即遇到不受检查(unchecked)的例外时回滚;而遇到需要捕获的例外(throw new Exception("注释");)不会回滚,即遇到受检查的例外(就是非运行时抛出的异常,编译器会检查到的异常叫受检查例外或说受检查异常)时,需我们指定方式来让事务回滚要想所有异常都回滚,要加上 @Transactional( rollbackFor={Exception.class,其它异常}) .如果让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)
2、@Transactional 注解应该只被应用到 public 可见度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错, 但是这个被注解的方法将不会展示已配置的事务设置。
3、Spring团队的建议是你在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。你当然可以在接口上使用 @Transactional 注解,但是这将只能当你设置了基于接口的代理时它才生效。因为注解是不能继承的,这就意味着如果你正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装(将被确认为严重的)。因此,请接受Spring团队的建议并且在具体的类上使用 @Transactional 注解。
参考过的博客:
这两个是介绍Spring的事务管理的综述的
https://www.cnblogs.com/yixianyixian/p/8372832.html
https://blog.csdn.net/donggua3694857/article/details/69858827
这两个是介绍传播行为的概念的,第一个讲概念可以,第二个的例子挺清晰:
https://blog.csdn.net/wwh578867817/article/details/51736723
https://blog.csdn.net/hsgao_water/article/details/52860380
SpringBoot中的事务管理:
https://blog.csdn.net/wohaqiyi/article/details/72895983
@Transactional注解介绍:
https://www.cnblogs.com/caoyc/p/5632963.html
Springboot下事务管理的简单使用的更多相关文章
- springboot mybatis 事务管理
本文主要讲述springboot提供的声明式的事务管理机制. 一.一些概念 声明式的事务管理是基于AOP的,在springboot中可以通过@Transactional注解的方式获得支持,这种方式的优 ...
- 架构设计 | 基于Seata中间件,微服务模式下事务管理
源码地址:GitHub·点这里 || GitEE·点这里 一.Seata简介 1.Seata组件 Seata是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务.Seata将为用 ...
- springboot开启事务管理
spring中开启事务管理需要在xml配置文件中配置,springboot中采取java config的配置方式. 核心是@EnableTransactionManager注解,该注解即为开启事务管理 ...
- spring boot学习(6) SpringBoot 之事务管理
两个操作要么同时成功,要么同时失败: 事务的一致性: 以前学ssh ssm都有事务管理service层通过applicationContext.xml配置,所有service方法都加上事务操作: 用来 ...
- SpringBoot设置事务管理
关于事务就不介绍了,前面在研究spring的时候就已经研究过了,参考:https://www.cnblogs.com/qlqwjy/p/7296493.html 这里直接研究springboot中事务 ...
- ## springboot 下策略模式的简单使用
1.灵魂三问 接手前人(已跑路)项目快乐否? 前人项目不写注释懵逼否? 一个方法中一堆if/else,且业务判断条件用简单数字(或英文字母),不带注释,想打人否? 所以,对于上述三个问题,我写 ...
- springboot下使用dubbo的简单demo
1.一些话 现在java后端开发大多用springboot来简化环境搭建,现在一直使用的是springcloud和k8s有关的东西,以前用过dubbo,但那会儿的开发环境搭建流程较为繁琐,而且不支持r ...
- Spring基于AOP的事务管理
Spring基于AOP的事务管理 事务 事务是一系列动作,这一系列动作综合在一起组成一个完整的工作单元,如果有任何一个动作执行失败,那么事务 ...
- (转)Hibernate事务管理
Hibernate的事务管理 事务(Transaction)是工作中的基本逻辑单位,可以用于确保数据库能够被正确修改,避免数据只修改了一部分而导致数据不完整,或者在修改时受到用户干扰.作为一名软件设计 ...
随机推荐
- ckplayer 中的style.swf 中的 style.xml 中的修改方法
style.swf ---- > style.zip ---- > 解压成文件夹 ---- > 打开style.xml ---- > 修改 最重要的是修改保存style.xml ...
- BestCoder7 1002 Little Pony and Alohomora Part I(hdu 4986) 解题报告
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4986 题目意思:有 n 个box(从左到右编号依次为1~n),每个box里面有一个随机的钥匙,有可能这 ...
- 通过kettle数据导入mysql时,空值的处理在插入mysql时,会自动转转换为null值,无法插入
1.windows下C:\Users\用户名\.kettle目录中找到kettle.properties文件,增加KETTLE_EMPTY_STRING_DIFFERS_FROM_NULL=Y2.Li ...
- (QA-LSTM)自然语言处理:智能问答 IBM 保险QA QA-LSTM 实现笔记.md
train集: 包含若干条与保险相关的问题,每一组问题对为一行,示意如下: 可分为四项,第三项为问题,第四项为答案: 1.build_vocab 统计训练集中出现的词,返回结果如下(一个包含3085个 ...
- Spring 事务管理高级应用难点剖析: 第 3 部分
本文是“Spring 事务管理高级应用难点剖析” 系列文章的第 3 部分,作者将继续深入剖析在实际 Spring 事务管理应用中容易遇见的一些难点,包括在使用 Spring JDBC 时如果直接获取 ...
- bzoj 2238 Mst——树链剖分
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2238 一条非树边可以对一条链的树边产生影响.注意是边,所以把边下放到点上,只要跳 top 时 ...
- jni中c代码调用java代码
原理是使用反射的机制 java中反射的例子: Class<?> forName = Class.forName("com.example.ndkcallback.DataProv ...
- calico在docker上的部署及验证
1. 背景 以下的部署以五台服务器环境为例: 服务器1: hostname为etcdnode1, IP为192.168.56.100 服务器2: hostname为etcdnode2, IP为192. ...
- linux下监控用户的操作记录---录像播放性质
想知道用户登陆系统后都操作了什么,怎么办? 别急,linux下有一个script工具,专门记录终端会话中所有输入输出结果,并存放到指定文件中. 先看看怎么录制吧! 1.创建日志存放目录 # mkdir ...
- <正则吃饺子> :关于前端往后端传递布尔值参数的问题
问题是在一个群里看到的,通过自己查找和实验,参考网上的博文,将问题列出来.以帮助后来者和共同交流学习. 博文地址:http://blog.sina.com.cn/s/blog_13c30757a0 ...