SpringBoot 系列教程之编程式事务使用姿势介绍篇
前面介绍的几篇事务的博文,主要是利用@Transactional
注解的声明式使用姿势,其好处在于使用简单,侵入性低,可辨识性高(一看就知道使用了事务);然而缺点也比较明显,不够灵活,稍不注意,可能就因为姿势不对,导致事务不生效
本文将介绍另外一种事务的使用姿势,借助TransactionTemplate
的编程式事务
I. 配置
本篇主要介绍的是jdbcTemplate
+transactionTemplate
来完成一个编程式事务的实例 demo
创建一个 SpringBoot 项目,版本为2.2.1.RELEASE
,使用 mysql 作为目标数据库,存储引擎选择Innodb
,事务隔离级别为 RR
1. 项目配置
在项目pom.xml
文件中,加上spring-boot-starter-jdbc
,会注入一个DataSourceTransactionManager
的 bean,提供了事务支持
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
2. 数据库配置
进入 spring 配置文件application.properties
,设置一下 db 相关的信息
## DataSource
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/story?useUnicode=true&characterEncoding=UTF-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=
3. 数据库
新建一个简单的表结构,用于测试
CREATE TABLE `money` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL DEFAULT '' COMMENT '用户名',
`money` int(26) NOT NULL DEFAULT '0' COMMENT '钱',
`is_deleted` tinyint(1) NOT NULL DEFAULT '0',
`create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=551 DEFAULT CHARSET=utf8mb4;
II. 使用说明
1. 初始化
创建几条数据,用于事务操作
@Service
public class ManualDemo {
@Autowired
private TransactionTemplate transactionTemplate;
@Autowired
private JdbcTemplate jdbcTemplate;
@PostConstruct
public void init() {
String sql = "replace into money (id, name, money) values (220, '初始化', 200)";
jdbcTemplate.execute(sql);
}
}
2. 使用 case
为了演示事务的特性,我们设计几个简单的 sql 操作,并抛出异常,引发回滚,如下
- 在 doUpdate 方法中,显示更新 name,输出更新的结果,然后再更新 money 的值,最后抛出一个异常,希望事务回滚
private boolean doUpdate(int id) throws Exception {
if (this.updateName(id)) {
this.query("after updateMoney name", id);
if (this.updateMoney(id)) {
return true;
}
}
throw new Exception("参数异常");
}
private boolean updateName(int id) {
String sql = "update money set `name`='更新' where id=" + id;
jdbcTemplate.execute(sql);
return true;
}
public void query(String tag, int id) {
String sql = "select * from money where id=" + id;
Map map = jdbcTemplate.queryForMap(sql);
System.out.println(tag + " >>>> " + map);
}
private boolean updateMoney(int id) {
String sql = "update money set `money`= `money` + 10 where id=" + id;
jdbcTemplate.execute(sql);
return false;
}
上面这一端逻辑,如果看了前面几篇博文,会比较熟悉,区别在于 doUpdate 方法上面没有添加@Transactional
注解,当下它的调用并不会在事务中执行
接下来我们看一下编程式事务的核心写法
public void testTransaction(int id) {
transactionTemplate.execute(new TransactionCallback<Boolean>() {
@Override
public Boolean doInTransaction(TransactionStatus transactionStatus) {
try {
return doUpdate(id);
} catch (Exception e) {
transactionStatus.setRollbackOnly();
return false;
}
}
});
}
如上,将方法的调用,封装在transactionTemplate.execute
的调用中,通过设置transactionStatus.setRollbackOnly()
来标记回滚
通过前面几篇博文的学习我们知道实际使用时,事务的隔离级别,传递属性也很重要,在编程式事务中,当然也是可以设置的
// 设置隔离级别
transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
// 设置传播属性
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
最后写一个测试代码,验证一下是否生效
@Component
public class TransactionalSample {
@Autowired
private ManualDemo manualDemo;
public void testManualCase() {
System.out.println("======= 编程式事务 start ========== ");
manualDemo.query("transaction before", 220);
manualDemo.testTransaction(220);
manualDemo.query("transaction end", 220);
System.out.println("======= 编程式事务 end ========== ");
}
}
输出结果如下,最终数据 big 没有被修改
======= 编程式事务 start ==========
transaction before >>>> {id=220, name=初始化, money=200, is_deleted=false, create_at=2020-02-03 13:52:39.0, update_at=2020-02-03 13:52:39.0}
after updateMoney name >>>> {id=220, name=更新, money=200, is_deleted=false, create_at=2020-02-03 13:52:39.0, update_at=2020-02-03 13:52:39.0}
transaction end >>>> {id=220, name=初始化, money=200, is_deleted=false, create_at=2020-02-03 13:52:39.0, update_at=2020-02-03 13:52:39.0}
======= 编程式事务 end ==========
III. 其他
0. 系列博文&源码
系列博文
- 180926-SpringBoot 高级篇 DB 之基本使用
- 190407-SpringBoot 高级篇 JdbcTemplate 之数据插入使用姿势详解
- 190412-SpringBoot 高级篇 JdbcTemplate 之数据查询上篇
- 190417-SpringBoot 高级篇 JdbcTemplate 之数据查询下篇
- 190418-SpringBoot 高级篇 JdbcTemplate 之数据更新与删除
- 200119-SpringBoot 系列教程之声明式事务 Transactional
- 200120-SpringBoot 系列教程之事务隔离级别知识点小结
- 200202-SpringBoot 系列教程之事务传递属性
- 200203-SpringBoot 系列教程之事务不生效的几种 case
源码
- 工程:https://github.com/liuyueyi/spring-boot-demo
- 实例源码: https://github.com/liuyueyi/spring-boot-demo/blob/master/spring-boot/101-jdbctemplate-transaction
1. 一灰灰 Blog
尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现 bug 或者有更好的建议,欢迎批评指正,不吝感激
下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛
- 一灰灰 Blog 个人博客 https://blog.hhui.top
- 一灰灰 Blog-Spring 专题博客 http://spring.hhui.top
SpringBoot 系列教程之编程式事务使用姿势介绍篇的更多相关文章
- SpringBoot系列教程JPA之新增记录使用姿势
SpringBoot系列教程JPA之新增记录使用姿势 上一篇文章介绍了如何快速的搭建一个JPA的项目环境,并给出了一个简单的演示demo,接下来我们开始业务教程,也就是我们常说的CURD,接下来进入第 ...
- SpringBoot系列教程之事务传递属性
200202-SpringBoot系列教程之事务传递属性 对于mysql而言,关于事务的主要知识点可能几种在隔离级别上:在Spring体系中,使用事务的时候,还有一个知识点事务的传递属性同样重要,本文 ...
- SpringBoot 系列教程之事务隔离级别知识点小结
SpringBoot 系列教程之事务隔离级别知识点小结 上一篇博文介绍了声明式事务@Transactional的简单使用姿势,最文章的最后给出了这个注解的多个属性,本文将着重放在事务隔离级别的知识点上 ...
- SpringBoot 系列教程之事务不生效的几种 case
SpringBoot 系列教程之事务不生效的几种 case 前面几篇博文介绍了声明式事务@Transactional的使用姿势,只知道正确的使用姿势可能还不够,还得知道什么场景下不生效,避免采坑.本文 ...
- Spring编程式事务管理及声明式事务管理
本文将深入讲解 Spring 简单而强大的事务管理功能,包括编程式事务和声明式事务.通过对本教程的学习,您将能够理解 Spring 事务管理的本质,并灵活运用之. Spring 事务属性分析 事务管理 ...
- 全面分析 Spring 的编程式事务管理及声明式事务管理
开始之前 关于本教程 本教程将深入讲解 Spring 简单而强大的事务管理功能,包括编程式事务和声明式事务.通过对本教程的学习,您将能够理解 Spring 事务管理的本质,并灵活运用之. 先决条件 本 ...
- 全面分析 Spring 的编程式事务管理及声明式事务管理--转
开始之前 关于本教程 本教程将深入讲解 Spring 简单而强大的事务管理功能,包括编程式事务和声明式事务.通过对本教程的学习,您将能够理解 Spring 事务管理的本质,并灵活运用之. 先决条件 本 ...
- spring事务管理——编程式事务、声明式事务
本教程将深入讲解 Spring 简单而强大的事务管理功能,包括编程式事务和声明式事务.通过对本教程的学习,您将能够理解 Spring 事务管理的本质,并灵活运用之. 先决条件 本教程假定您已经掌握了 ...
- Java工程师之SpringBoot系列教程前言&目录
前言 与时俱进是每一个程序员都应该有的意识,当一个Java程序员在当代步遍布的时候,你就行该想到我能多学点什么.可观的是后端的框架是稳定的,它们能够维持更久的时间在应用中,而不用担心技术的更新换代.但 ...
随机推荐
- css 文本溢出省略号
单行溢出显示省略号: white-space: nowrap; overflow: hidden; text-overflow: ellipsis; 多行溢出显示省略号: text-overflow: ...
- sklearn的train_test_split()各函数参数含义解释(非常全)
sklearn之train_test_split()函数各参数含义(非常全) 在机器学习中,我们通常将原始数据按照比例分割为“测试集”和“训练集”,从 sklearn.model_selection ...
- js左右选项移动
<!--网页代码--><div class="modal" id="modal-primary7"> <div class=&qu ...
- Windows编程常用api
转载网络 黑客常用WIN API函数整理 一.进程 创建进程: CreateProcess (,,,,,,,&si,&pi); WinExec("notepad", ...
- PAT (Advanced Level) 1124~1127:1124模拟 1125优先队列 1126欧拉通路 1127中序后序求Z字形层序遍历
1124 Raffle for Weibo Followers(20 分) 题意:微博抽奖,有M个人,标号为1~M.从第S个人开始,每N个人可以获奖,但是已获奖的人不能重复获奖,需要跳过该人把机会留给 ...
- leetCode242 有效的字母异位词
引言: 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词. 示例 1: 输入: s = "anagram", t = "nagaram&qu ...
- 002、创建第一个Java程序HelloWord
代码如下: package TIANPAN; public class TestDemo { public static void main(String args[]) { System.out.p ...
- JS: 图片轮播模板——左右移动,点击编码移动,自动轮播
<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title> ...
- POJ 2823 滑动窗口 单调队列模板
我们从最简单的问题开始: 给定一个长度为N的整数数列a(i),i=0,1,...,N-1和窗长度k. 要求: f(i) = max{a(i-k+1),a(i-k+2),..., a(i)},i = 0 ...
- 2.10 学习总结 之 JQ加强
一.说在前面 昨天 完成了体温统计APP的编写 今天 学习json数据结构 二.学习总结 1.json数据结构 1)什么是json: JSON(JavaScript Object Notation) ...