面试官:说说Spring中的事务传播行为
前言
在开发中,相信大家都使用过Spring的事务管理功能。那么,你是否有了解过,Spring的事务传播行为呢?
Spring中,有7种类型的事务传播行为。事务传播行为是Spring框架提供的一种事务管理方式,它不是数据库提供的。不知道大家是否听说过“不要在service事务方法中嵌套事务方法,这样会提交多个事务”的说法,其实这是不准确的。了解了事务传播行为之后,相信你就会明白!
原创声明
本文首发于头条号【Happyjava】。Happy的掘金地址:https://juejin.im/user/5cc2895df265da03a630ddca,Happy的个人博客:(http://blog.happyjava.cn)[http://blog.happyjava.cn]。欢迎转载,但须保留此段声明。
Spring中七种事务传播行为
事务的传播行为,默认值为 Propagation.REQUIRED。可以手动指定其他的事务传播行为,如下:
- Propagation.REQUIRED
如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。
- Propagation.SUPPORTS
如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。
- Propagation.MANDATORY
如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。
- Propagation.REQUIRES_NEW
重新创建一个新的事务,如果当前存在事务,延缓当前的事务。
- Propagation.NOT_SUPPORTED
以非事务的方式运行,如果当前存在事务,暂停当前的事务。
- Propagation.NEVER
以非事务的方式运行,如果当前存在事务,则抛出异常。
- Propagation.NESTED
如果没有,就新建一个事务;如果有,就在当前事务中嵌套其他事务。
准备工作
数据库表:
CREATE TABLE `t_user` (
`id` int(11) NOT NULL,
`password` varchar(255) DEFAULT NULL,
`username` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
一个整合了Spring Data JPA的SpringBoot工程,这里就不多说了。
REQUIRED(默认的事务传播行为)
默认的事务传播行为是Propagation.REQUIRED,也就是说:如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。
下面,我们就验证下前面说的“不要循环嵌套事务方法”的问题:
现在有两个Service,如下:
UserService.java
@Service
public class UserService {
@Autowired
private UserRepo userRepo;
@Transactional(propagation = Propagation.REQUIRED)
public void insert() {
UserEntity user = new UserEntity();
user.setUsername("happyjava");
user.setPassword("123456");
userRepo.save(user);
}
}
这里很简单,就一个insert插入用户的方法。
UserService2.java
@Service
public class UserService2 {
@Autowired
private UserService userService;
@Transactional
public void inserBatch() {
for (int i = 0; i < 10; i++) {
if (i == 9) {
throw new RuntimeException();
}
userService.insert();
}
}
}
注入UserService,循环十次调用参数方法。并且第十次抛出异常。调用inserBatch方法,查看结果:
@Test
public void insertBatchTest() {
userService2.inserBatch();
}
结果如下:
数据库中没有记录:
这也证明了“如果当前存在事务,则加入该事务”的概念。如果以后还碰到有人说不要循环嵌套事务的话,可以叫他回去好好看看Spring的事务传播行为。
SUPPORTS
如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。也就是说,该模式是否支持事务,看调用它的方法是否有事务支持。测试代码如下:
UserService
@Transactional(propagation = Propagation.SUPPORTS)
public void insert() {
UserEntity user = new UserEntity();
user.setUsername("happyjava");
user.setPassword("123456");
userRepo.save(user);
throw new RuntimeException();
}
UserService2
public void insertWithoutTx() {
userService.insert();
}
调用的方法没有开启事务,运行结果:
运行报错了,但是数据却没有回滚掉。说明了insert方法是没有在事务中运行的。
MANDATORY
如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。mandatory中文是强制性的意思,表明了被修饰的方法,一定要在事务中去调用,否则会抛出异常。
UserService.java
@Transactional(propagation = Propagation.MANDATORY)
public void insert() {
UserEntity user = new UserEntity();
user.setUsername("happyjava");
user.setPassword("123456");
userRepo.save(user);
}
UserService2.java
public void insertWithoutTx() {
userService.insert();
}
调用:
@Test
public void insertWithoutTxTest() {
userService2.insertWithoutTx();
}
运行结果:
抛出了异常,提示没有存在的事务。
REQUIRES_NEW
这个理解起来可能会比较绕,官方的解释是这样子的:
Create a new transaction, and suspend the current transaction if one exists.
大意就是:重新创建一个新的事务,如果当前存在事务,延缓当前的事务。这个延缓,或者说挂起,可能理解起来比较难,下面通过例子来分析:
UserService.java
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void insert() {
UserEntity user = new UserEntity();
user.setUsername("happyjava");
user.setPassword("123456");
userRepo.save(user);
}
这个insert方法的传播行为改为REQUIRES_NEW。
UserService2.java
@Transactional
public void inserBatch() {
UserEntity user = new UserEntity();
user.setUsername("初次调用");
user.setPassword("123456");
userRepo.save(user);
for (int i = 0; i < 10; i++) {
if (i == 9) {
throw new RuntimeException();
}
userService.insert();
}
}
inserBatch拥有事务,然后后面循环调用的insert方法也有自己的事务。根据定义,inserBatch的事务会被延缓。具体表现就是:后面的10次循环的事务在每次循环结束之后都会提交自己的事务,而inserBatch的事务,要等循环方法走完之后再提交。但由于第10次循环会抛出异常,则inserBatch的事务会回滚,既数据库中不会存在:“初次调用”的记录:
测试代码:
@Test
public void insertBatchTest() {
userService2.inserBatch();
}
执行结果:
这种情况,符合开始说的“不要循环嵌套事务方法”的说话,当然是否需要循环嵌套,还是要看业务逻辑的。
NOT_SUPPORTED
Execute non-transactionally, suspend the current transaction if one exists.
以非事务的方式运行,如果当前存在事务,暂停当前的事务。这种方式与REQUIRES_NEW有所类似,但是NOT_SUPPORTED修饰的方法其本身是没有事务的。这里就不做代码演示了。
NEVER
以非事务的方式运行,如果当前存在事务,则抛出异常。
@Transactional(propagation = Propagation.NEVER)
public void insert() {
UserEntity user = new UserEntity();
user.setUsername("happyjava");
user.setPassword("123456");
userRepo.save(user);
}
@Transactional
public void insertWithTx() {
userService.insert();
}
执行结果:
NESTED
如果没有事务,就新建一个事务;如果有,就在当前事务中嵌套其他事务。
这个也是理解起来比较费劲的一个行为。我们一步一步分析。
外围方法没有事务:这种情况跟REQUIRED是一样的,会新建一个事务。
外围方法如果存在事务:这种情况就会嵌套事务。所谓嵌套事务,大意就是,外围事务回滚,内嵌事务一定回滚,而内嵌事务可以单独回滚而不影响外围主事务和其他子事务。
由于本人使用Spring Data JPA 进行的演示代码,使用嵌套事务会提示:
org.springframework.transaction.NestedTransactionNotSupportedException: JpaDialect does not support savepoints - check your JPA provider's capabilities
搜索了下,hibernate似乎不支持这种事务传播方式。所以这里就不做演示了
总结
事务传播行为,在开发中可能不会特别的留意到它(更多时候,我们可能只是使用默认的方式),但是还是需要对其要有所理解。希望本篇文章能让大家明白Spring的7种事务传播行为。
面试官:说说Spring中的事务传播行为的更多相关文章
- Spring中的事务传播行为
Spring在TransactionDefinition接口中定义了7种类型的事务传播行为,它们规定了事务方法是怎样传播的. PROPAGATION_REQUIRED(最经常使用!):支持当前事务,假 ...
- spring中的事务传播机制
1.事务的实现思想 在spring中要想某个方法具有事务,只要在方法前加一个@Transactional注解.然后spring就会利用aop思想,在这个方法执行前开启事务, 在方法执行后选择提交事务或 ...
- Spring中的事务传播行为与隔离级别
事务传播行为 事务传播行为(为了解决业务层方法之间互相调用的事务问题): 当事务方法被另一个事务方法调用时,必须指定事务应该如何传播.例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己 ...
- 面试官:spring中定义bean的方法有哪些?我一口气说出了12种,把面试官整懵了。
前言 在庞大的java体系中,spring有着举足轻重的地位,它给每位开发者带来了极大的便利和惊喜.我们都知道spring是创建和管理bean的工厂,它提供了多种定义bean的方式,能够满足我们日常工 ...
- 【原创】spring中的事务传播特性
关于spring的传播特性,我对其进行了详细的叙述了下: PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务.这是最常见的选择. 比如方法A调用方法B,如果方法 ...
- SSM-Spring-23:概念《Spring中的事务是什么?》
------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 本篇博客会详细讲述Spring中的事务,会展开来用语言解释,用于了解概念和准备面试 事务的概念: 一个或者一组 ...
- Spring中的事务管理
事务简介: 事务管理是企业级应用程序开发中必不可少的技术,用来确保数据的完整性和一致性 事务就是一系列的动作,它们被当作一个单独的工作单元.这些动作要么全部完成,要么全部不起作用 事务的四个关键属性( ...
- Spring中的事务管理详解
在这里主要介绍Spring对事务管理的一些理论知识,实战方面参考上一篇博文: http://www.cnblogs.com/longshiyVip/p/5061547.html 1. 事务简介: 事务 ...
- Spring中的事务操作
事务的特性 原子性:强调事务的不可分割. 一致性:事务的执行的前后数据的完整性保持一致. 隔离性:一个事务执行的过程中,不应该受到其他事务的干扰. 持久性:事务一旦结束,数据就持久化到数据库. 如果不 ...
随机推荐
- it兼职以及行业门户网
程序员接私活的七大平台 https://www.jianshu.com/p/61a3fabe75fc 1.程序员客栈:程序员的经纪人 https://www.proginn.com/ 2.快码 ...
- 【原】shell增加定时任务
通过shell脚本增加定时任务:#CRON_FILE="/var/spool/cron/root" (centos系统) CRON_FILE="/var/spool/cr ...
- Knapsack Cryptosystem 牛客团队赛
时限2s题意: 第一行包含两个整数,分别是n(1 <= n <= 36)和s(0 <= s <9 * 10 18) 第二行包含n个整数,它们是{a i }(0 <a i ...
- vue天气查询
天气查询包括回车查询和点击查询两种功能 回车查询 1.按下回车(v-on+.enter) 2.查询数据(axios+接口+v-model) 3.渲染数据(v-for+arr) 点击查询 1.点击城市查 ...
- Java - Test - TestNG: Idea 引入 testng.xml 自动生成插件
1. 概述 Idea 引入自动生成 testng.xml 插件 自动生成 testng.xml 2. 背景 testng 调试 调试 testng, 主要是这两种方法 ide 下直接执行测试 方法 类 ...
- jquery实现登录后右下角弹窗提醒(附带简单样式)
页面代码如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://w ...
- 【Python爬虫程序】抓取MM131美女图片,并将这些图片下载到本地指定文件夹。
一.项目名称 抓取MM131美女写真图片,并将这些图片下载到本地指定文件夹. 共有6种类型的美女图片: 性感美女 清纯美眉 美女校花 性感车模 旗袍美女 明星写真 抓取后的效果图如下,每个图集是一个独 ...
- MySQL存储引擎优化
如何在两种存储引擎中进行选择? ① 是否有事务操作?有,InnoDB. ②是否存储并发修改?有,InnoDB. ③是否追求快速查询,且数据修改较少?是,MyISAM. ④是否使用全文索引?如果不引用第 ...
- Django objects.all()、objects.get()与objects.filter()之间的区别介绍
前言 本文主要介绍的是关于Django objects.all().objects.get()与objects.filter()直接区别的相关内容,文中介绍的非常详细,需要的朋友们下面来一起看看详细的 ...
- js 设计模式:观察者和发布订阅模式
总是把这两个当作同一个模式,但其实是不太一样的,现在重温一下. 观察者模式 观察者直接订阅目标,当目标触发事件时,通知观察者进行更新 简单实现 class Observer { constructor ...