SpringBoot系列教程之编程式事务使用姿势介绍篇

编程

SpringBoot 系列教程之编程式事务使用姿势介绍篇

前面介绍的几篇事务的博文,主要是利用@Transactional注解的声明式使用姿势,其好处在于使用简单,侵入性低,可辨识性高(一看就知道使用了事务);然而缺点也比较明显,不够灵活,稍不注意,可能就因为姿势不对,导致事务不生效

本文将介绍另外一种事务的使用姿势,借助TransactionTemplate的编程式事务

<!-- more -->

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系列教程之编程式事务使用姿势介绍篇 的全部内容, 来源链接: utcz.com/z/513180.html

回到顶部