易筋SpringBoot 2.1 | 第十七篇:SpringBoot的事务Transaction
写作时间:2019-08-15
Spring Boot: 2.1 ,JDK: 1.8, IDE: IntelliJ IDEA
说明
事务管理是应用系统开发中必不可少的一部分。Spring 为事务管理提供了丰富的功能支持。Spring 事务管理分为编程式和声明式的两种方式。编程式事务指的是通过编码方式实现事务;声明式事务基于 AOP,将具体业务逻辑与事务处理解耦。声明式事务管理使业务代码逻辑不受污染, 因此在实际使用中声明式事务用的比较多。声明式事务有两种方式,一种是在配置文件(xml)中做相关的事务规则声明,另一种是基于 @Transactional 注解的方式。
声明式事务图解
需要明确几点:
默认配置下 Spring 只会回滚运行时、未检查异常(继承自 RuntimeException 的异常)或者 Error。参考#transaction-declarative-rolling-back
@Transactional 注解只能应用到 public 方法才有效。参考这里 Method visibility and @Transactional.
Method visibility and @TransactionalWhen using proxies, you should apply the @Transactional annotation only to methods with public visibility.
If you do annotate protected, private or package-visible methods with the @Transactional annotation,
no error is raised, but the annotated method does not exhibit the configured transactional settings.
Consider the use of AspectJ (see below) if you need to annotate non-public methods.
一致的事务抽象
- JDBC/Hibernate/myBatis
- DataSource/JTA
PlatformTransactionManager
- DataSourceTransactionManager
- HibernateTransactionManager
- JtaTransactionManger
TransactionDefinition
- Propagation
- Isolation
- Timeout
- Read-only status
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
事务的传播特性
事务传播行为类型 | 值 | 说明 |
---|---|---|
PROPAGATION_REQUIRED | 0 | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。 |
PROPAGATION_SUPPORTS | 1 | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 2 | 使用当前的事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 3 | 新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED | 4 | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 5 | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 6 | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类 似的操作。 |
Required 图解
Required New 图解
事务隔离级别
隔离级别是指若干个并发的事务之间的隔离程度。TransactionDefinition 接口中定义了四个表示隔离级别的常量:
隔离性 | 值 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|---|
ISOLATION_READ_UNCOMMITTED | 1 | ✔️ | ✔️ | ✔️ |
ISOLATION_READ_COMMITTED | 2 | ❌ | ✔️ | ✔️ |
ISOLATION_REPEATABLE_READ | 3 | ❌ | ❌ | ✔️ |
ISOLATION_REPEATABLE_SERIALIZABLE | 4 | ❌ | ❌ | ❌ |
工程建立
参照教程【SpringBoot 2.1 | 第一篇:构建第一个SpringBoot工程】新建一个Spring Boot项目,名字叫demodbtransaction, 在目录src/main/java/resources
下找到配置文件application.properties
,重命名为application.yml
。
在Dependency中选择
Developer Tools > Lombok
Web > Spring Web Starter
SQL > H2 DataBase / JDBC API
Ops > Spring Boot Actuator。
设置日志打印格式化ANSI
ANSI - American National Standards Institute
Support classes to provide ANSI color output.
src > main > resources > application.yml
spring:output:ansi:enabled: always
创建表Foo
路径 src > main > resources > schema.sql
CREATE TABLE FOO (ID INT IDENTITY, BAR VARCHAR(64));
编程式事务
TransactionTemplate
- TransactionCallback
- TransactionCallbackWithoutResult
PlatforomTransactionManager
- 可以传入TransactionDefinition进行定义
Controller 实现编程式事务
com.zgpeace.demodbtransaction.DemodbtransactionApplication
package com.zgpeace.demodbtransaction;import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;@SpringBootApplication
@Slf4j
public class DemodbtransactionApplication implements CommandLineRunner {@Autowiredprivate TransactionTemplate transactionTemplate;@Autowiredprivate JdbcTemplate jdbcTemplate;public static void main(String[] args) {SpringApplication.run(DemodbtransactionApplication.class, args);}@Overridepublic void run(String... args) throws Exception {programTransaction();}private void programTransaction() {log.info("COUNT BEFORE TRANSACTION: {}", getCount());transactionTemplate.execute(new TransactionCallbackWithoutResult() {@Overrideprotected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {jdbcTemplate.execute("INSERT INTO FOO (ID, BAR) VALUES (1, 'zgpeace')");log.info("COUNT IN TRANSACTION: {}", getCount());transactionStatus.setRollbackOnly();}});log.info("COUNT AFTER TRANSACTION: {}", getCount());}private long getCount() {return (long) jdbcTemplate.queryForList("SELECT COUNT(*) AS CNT FROM FOO").get(0).get("CNT");}
}
注释:这里在执行插入数据之前,当中,之后分别打印数据表中数据的count的变化。因为执行的setRollbackOnly(), 所以之前跟之后的数据是一样的。
启动成功,查看setRollbackOnly的日志
COUNT BEFORE TRANSACTION: 0
COUNT IN TRANSACTION: 1
COUNT AFTER TRANSACTION: 0
注释: RollbackOnly, 只改变过程,达到预期。
声明式事务
图解可以参考文章开头的图片
基于注解的配置方式
开启事务注解的方式
- @EnableTransactionMangement
<tx:annotation-driven/>
一些配置
- proxyTargetClass
- mode
- order
@Transactional
- transactionManager
当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器。 - propagation
事务的传播行为,默认值为 Propagation.REQUIRED。 - isolation
事务的隔离级别,默认值为 Isolation.DEFAULT。
可选的值有:
Isolation.DEFAULT 使用底层数据库默认的隔离级别。
Isolation.READ_UNCOMMITTED
Isolation.READ_COMMITTED
Isolation.REPEATABLE_READ
Isolation.SERIALIZABLE
- timeout
事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。 - readOnly
指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。 - rollbackFor 属性
用于指定能够触发事务回滚的异常类型,可以指定多个异常类型。 - noRollbackFor 属性
抛出指定的异常类型,不回滚事务,也可以指定多个异常类型。
创建自定义Exception类
com.zgpeace.demodbtransaction.RollbackException
package com.zgpeace.demodbtransaction;public class RollbackException extends Exception {}
数据操作Service
接口 com.zgpeace.demodbtransaction.FooService
package com.zgpeace.demodbtransaction;public interface FooService {void insertRecord();void insertThenRollback() throws RollbackException;void invokeInsertThenRollback() throws RollbackException;
}
实现类 com.zgpeace.demodbtransaction.FooServiceImpl
package com.zgpeace.demodbtransaction;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;@Component
public class FooServiceImpl implements FooService {@Autowiredprivate JdbcTemplate jdbcTemplate;@Override@Transactionalpublic void insertRecord() {jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('AAA')");}@Override@Transactional(rollbackFor = RollbackException.class)public void insertThenRollback() throws RollbackException {jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('BBB')");throw new RollbackException();}@Overridepublic void invokeInsertThenRollback() throws RollbackException {insertThenRollback();}
}
Controller 声明式事务
com.zgpeace.demodbtransaction.DemodbtransactionApplication 更新类如下内容
package com.zgpeace.demodbtransaction;import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;@SpringBootApplication
@Slf4j
@EnableTransactionManagement(mode = AdviceMode.PROXY)
public class DemodbtransactionApplication implements CommandLineRunner {@Autowiredprivate FooService fooService;@Autowiredprivate TransactionTemplate transactionTemplate;@Autowiredprivate JdbcTemplate jdbcTemplate;public static void main(String[] args) {SpringApplication.run(DemodbtransactionApplication.class, args);}@Overridepublic void run(String... args) throws Exception {//programTransaction();anotateTransaction();}private void anotateTransaction() {fooService.insertRecord();log.info("AAA {}", jdbcTemplate.queryForObject("SELECT COUNT(*) FROM FOO WHERE BAR='AAA'", Long.class));try {fooService.insertThenRollback();} catch (Exception e) {log.info("BBB {}", jdbcTemplate.queryForObject("SELECT COUNT(*) FROM FOO WHERE BAR='BBB'", Long.class));}try {fooService.invokeInsertThenRollback();} catch (Exception e) {log.info("BBB {}", jdbcTemplate.queryForObject("SELECT COUNT(*) FROM FOO WHERE BAR='BBB'", Long.class));}}
注释: invokeInsertThenRollback() 方法事务不起作用,是因为通过方法去调用字方法,没有作用到proxy类的调用。
启动成功,查看声明式事务结果
AAA
BBB 0
BBB 1
事务的本质
- Spring的声明式事务本质上是通过AOP来增强了类的功能
- Spring的AOP本质上就是为类做了一个代理
看似在调用自己写的类,实际用的是增强后的代理类。 - 问题的解法
访问增强后的代理类的方法,而非直接访问自身的方法。
在ServiceImp类中,声明父类的对象,调用父类对象来调用方法即可。
@Component
public class FooServiceImpl implements FooService {@Autowiredprivate JdbcTemplate jdbcTemplate;@Autowiredprivate FooService fooService;@Override@Transactional(rollbackFor = RollbackException.class)public void insertThenRollback() throws RollbackException {jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('BBB')");throw new RollbackException();}@Overridepublic void invokeInsertThenSuperRollback() throws RollbackException {fooService.insertThenRollback();}
}
总结
恭喜你,学会了SpringBoot的事务Transaction编程实现和声明式实现。
代码下载:
https://github.com/zgpeace/Spring-Boot2.1/tree/master/db/demodbtransaction
参考
https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#transaction-declarative-annotations
https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#transaction-declarative-rolling-back
https://blog.csdn.net/nextyu/article/details/78669997
https://github.com/geektime-geekbang/geektime-spring-family/tree/master/Chapter%202/programmatic-transaction-demo
https://github.com/geektime-geekbang/geektime-spring-family/tree/master/Chapter%202/declarative-transaction-demo
易筋SpringBoot 2.1 | 第十七篇:SpringBoot的事务Transaction相关推荐
- SpringBoot非官方教程 | 第十七篇:上传文件
转载请标明出处: http://blog.csdn.net/forezp/article/details/71023752 本文出自方志朋的博客 这篇文章主要介绍,如何在springboot工程作 ...
- 【笑小枫的SpringBoot系列】【十七】SpringBoot文件上传下载
本文简介
- spring boot学习(十三)SpringBoot缓存(EhCache 2.x 篇)
SpringBoot 缓存(EhCache 2.x 篇) SpringBoot 缓存 在 Spring Boot中,通过@EnableCaching注解自动化配置合适的缓存管理器(CacheManag ...
- MySQL事务(transaction) (有这篇就足够了..)
MySQL事务处理(TransAction) 大家好,我是胡亦,一名热爱分享技术干货的博主. 思考了很久,决定写一篇关于mysql事务(transaction)的博客,一来嘛,因为最近在复习mysql ...
- springboot定时发送短信_SpringBoot第十七篇:定时任务
引言 相信大家对定时任务很熟悉,其重要性也不言而喻.定时发短信.定时批量操作.定时统计数据等,都离不开定时任务.本文将讲解定时任务在 SpringBoot 项目中的应用. 版本信息 JDK:1.8 S ...
- 写代码时发现......还是 SpringBoot 牛逼!一篇拿下
关注了很多技术类公众号的读者肯定有这样一个感受,SpringBoot相关的文章铺天盖地,并且SpringBoot相关的文章阅读量.收藏量都很高,这也从侧面反映了SpringBoot技术的火爆. 一切都 ...
- SpringBoot非官方教程 | 第三篇:SpringBoot用JdbcTemplates访问Mysql
SpringBoot非官方教程 | 第三篇:SpringBoot用JdbcTemplates访问Mysql 本文介绍springboot通过jdbc访问关系型mysql,通过spring的JdbcTe ...
- SpringBoot进阶教程 | 第四篇:整合Mybatis实现多数据源
这篇文章主要介绍,通过Spring Boot整合Mybatis后如何实现在一个工程中实现多数据源.同时可实现读写分离. 准备工作 环境: windows jdk 8 maven 3.0 IDEA 创建 ...
- springboot项目搭建0000-导航篇
前言:这篇帖子没有任何涉及技术的内容,如果你想找springboot相关的技术知识,可以不用往下看了. 这篇帖子仅仅是对我的springboot相关博客,开辟一个导航页,方便读者.技术大牛略过 主要参 ...
- Java学习篇——SpringBoot
1. 关于Spring Boot Spring Boot框架主要解决了创建工程后需要进行繁琐的配置的问题,是一个"开箱即用"的框架,其核心思想是"约定大于配置" ...
最新文章
- 第二章:3、BP神经网络
- 32位 shell.efi x86_通过grub,让32位的efi也能运行64位的Linux发行版
- 利用Squid反向代理搭建CDN缓存服务器加快Web访问速度
- thinkphp5是不是php,我对ThinkPHP5和Laravel5的一些看法
- Excel 自动更正选项
- 打造轻量级可视化数据爬取工具-菩提
- 简书 php三级联动,JS 实现三级联动
- Oracle B-Tree Index 原理
- 小米第一款智能手表来了 或支持安装手机App
- c# 利用t4模板,自动生成Model类
- 数据库的数据进行改动,Cognos报表展示未及时更新
- 2017国民行业分类sql-存储过程_存储函数-MySQL
- mysql 建表语句
- 《一天学懂深度学习》PPT翻译一
- 如何对C盘进行扩容重新分区?
- hdu 4498 自适应simpson
- Golang中的并发:如何使用Goroutines?详细指南
- Wing IDE中文乱码问题
- 零代码搭建一个温度传感器数据采集与显示软件
- 制作类似于淘宝点击简单的轮播图