内容简介:前面介绍的几篇事务的博文,主要是利用本文将介绍另外一种事务的使用姿势,借助本篇主要介绍的是
前面介绍的几篇事务的博文,主要是利用 @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相关的信息 ```properties ## 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
打赏 如果觉得我的文章对您有帮助,请随意打赏。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 佈署 Angular 應用程式至 IIS 虛擬目錄 / 應用程式
- 佈署 Angular 應用程式至 IIS 虛擬目錄 / 應用程式
- matlab—方程式求根
- 八、Golang过程式编程
- 如何降低程式碼複雜度 ?
- 算法系列(1):Google方程式
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。