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. 系列博文&源码

系列博文

源码

1. 一灰灰 Blog

尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现 bug 或者有更好的建议,欢迎批评指正,不吝感激

下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

SpringBoot 系列教程之编程式事务使用姿势介绍篇的更多相关文章

  1. SpringBoot系列教程JPA之新增记录使用姿势

    SpringBoot系列教程JPA之新增记录使用姿势 上一篇文章介绍了如何快速的搭建一个JPA的项目环境,并给出了一个简单的演示demo,接下来我们开始业务教程,也就是我们常说的CURD,接下来进入第 ...

  2. SpringBoot系列教程之事务传递属性

    200202-SpringBoot系列教程之事务传递属性 对于mysql而言,关于事务的主要知识点可能几种在隔离级别上:在Spring体系中,使用事务的时候,还有一个知识点事务的传递属性同样重要,本文 ...

  3. SpringBoot 系列教程之事务隔离级别知识点小结

    SpringBoot 系列教程之事务隔离级别知识点小结 上一篇博文介绍了声明式事务@Transactional的简单使用姿势,最文章的最后给出了这个注解的多个属性,本文将着重放在事务隔离级别的知识点上 ...

  4. SpringBoot 系列教程之事务不生效的几种 case

    SpringBoot 系列教程之事务不生效的几种 case 前面几篇博文介绍了声明式事务@Transactional的使用姿势,只知道正确的使用姿势可能还不够,还得知道什么场景下不生效,避免采坑.本文 ...

  5. Spring编程式事务管理及声明式事务管理

    本文将深入讲解 Spring 简单而强大的事务管理功能,包括编程式事务和声明式事务.通过对本教程的学习,您将能够理解 Spring 事务管理的本质,并灵活运用之. Spring 事务属性分析 事务管理 ...

  6. 全面分析 Spring 的编程式事务管理及声明式事务管理

    开始之前 关于本教程 本教程将深入讲解 Spring 简单而强大的事务管理功能,包括编程式事务和声明式事务.通过对本教程的学习,您将能够理解 Spring 事务管理的本质,并灵活运用之. 先决条件 本 ...

  7. 全面分析 Spring 的编程式事务管理及声明式事务管理--转

    开始之前 关于本教程 本教程将深入讲解 Spring 简单而强大的事务管理功能,包括编程式事务和声明式事务.通过对本教程的学习,您将能够理解 Spring 事务管理的本质,并灵活运用之. 先决条件 本 ...

  8. spring事务管理——编程式事务、声明式事务

    本教程将深入讲解 Spring 简单而强大的事务管理功能,包括编程式事务和声明式事务.通过对本教程的学习,您将能够理解 Spring 事务管理的本质,并灵活运用之. 先决条件 本教程假定您已经掌握了 ...

  9. Java工程师之SpringBoot系列教程前言&目录

    前言 与时俱进是每一个程序员都应该有的意识,当一个Java程序员在当代步遍布的时候,你就行该想到我能多学点什么.可观的是后端的框架是稳定的,它们能够维持更久的时间在应用中,而不用担心技术的更新换代.但 ...

随机推荐

  1. 记录下spingboot连接阿里云服务器上的MySQL数据库报错

    错误大概如下: create connection SQLException, url: jdbc:mysql://'IP地址':3306/code007?useUnicode=true&ch ...

  2. c# Thread、ThreadPool、Task的区别

    Thread与ThreadPoll 前台线程:主程序必须等待线程执行完毕后才可退出程序.Thread默认为前台线程,也可以设置为后台线程 后台线程:主程序执行完毕后就退出,不管线程是否执行完毕.Thr ...

  3. Linux关于文件处理命令

    一.登陆用户和机器名称 示例:[root@hadoop01 ~]# root:表示用户名 @hadoop01表示机器名称 ~表示当前文件目录是家目录 #表示输入命令提示符,用户可以在其后输入命令:非r ...

  4. 记-ItextPDF+freemaker 生成PDF文件---导致服务宕机

    摘要:已经上线的项目,出现服务挂掉的情况. 介绍:该服务是专门做打印的,业务需求是生成PDF文件进行页面预览,主要是使用ItextPDF+freemaker技术生成一系列PDF文件,其中生成流程有:解 ...

  5. php hash算法实现memcached分布式

    一.概述Memcached和mysql一样,是一款客户端/服务器端(C/S)系统管理软件,有IP.端口,一旦启动,服务器就一直处于可用状态.Mysql是通过SQL语句管理“磁盘中”的文件,Memcac ...

  6. 富文本API

    这个笔记来自网络资料的总结 简书大佬三省吾身_9862 tuobaye个人博客 富文本有相关3个API和一个新属性 var selection = window.getSelection(); var ...

  7. Day8 - B - Non-Secret Cypher CodeForces - 190D

    Berland starts to seize the initiative on the war with Flatland. To drive the enemy from their nativ ...

  8. 使用Spring Data JPA的Spring Boot

    本文教你开始使用Spring Data JPA.来自优锐课JAVA架构专业讲师精心整理. 欢迎使用带有Spring Data JPA的Spring Boot教程!在本教程中,我们将看到Spring D ...

  9. xaml与CSS中的Margin顺序不同

    XAML中  Margin:左 上 右 下 CSS中      Margin:上 右 下 左

  10. c# GlobalAddAtom GlobalDeleteAtom

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...