写作时间: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.

一致的事务抽象

  1. JDBC/Hibernate/myBatis
  2. DataSource/JTA

PlatformTransactionManager

  1. DataSourceTransactionManager
  2. HibernateTransactionManager
  3. JtaTransactionManger

TransactionDefinition

  1. Propagation
  2. Isolation
  3. Timeout
  4. 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, 只改变过程,达到预期。

声明式事务

图解可以参考文章开头的图片

基于注解的配置方式

开启事务注解的方式

  1. @EnableTransactionMangement
  2. <tx:annotation-driven/>

一些配置

  1. proxyTargetClass
  2. mode
  3. order

@Transactional

  1. transactionManager
    当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器。
  2. propagation
    事务的传播行为,默认值为 Propagation.REQUIRED。
  3. isolation
    事务的隔离级别,默认值为 Isolation.DEFAULT。
可选的值有:
Isolation.DEFAULT 使用底层数据库默认的隔离级别。
Isolation.READ_UNCOMMITTED
Isolation.READ_COMMITTED
Isolation.REPEATABLE_READ
Isolation.SERIALIZABLE
  1. timeout
    事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。
  2. readOnly
    指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。
  3. rollbackFor 属性
    用于指定能够触发事务回滚的异常类型,可以指定多个异常类型。
  4. 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

事务的本质

  1. Spring的声明式事务本质上是通过AOP来增强了类的功能
  2. Spring的AOP本质上就是为类做了一个代理
    看似在调用自己写的类,实际用的是增强后的代理类。
  3. 问题的解法
    访问增强后的代理类的方法,而非直接访问自身的方法。

在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相关推荐

  1. SpringBoot非官方教程 | 第十七篇:上传文件

    转载请标明出处:  http://blog.csdn.net/forezp/article/details/71023752  本文出自方志朋的博客 这篇文章主要介绍,如何在springboot工程作 ...

  2. 【笑小枫的SpringBoot系列】【十七】SpringBoot文件上传下载

    本文简介

  3. spring boot学习(十三)SpringBoot缓存(EhCache 2.x 篇)

    SpringBoot 缓存(EhCache 2.x 篇) SpringBoot 缓存 在 Spring Boot中,通过@EnableCaching注解自动化配置合适的缓存管理器(CacheManag ...

  4. MySQL事务(transaction) (有这篇就足够了..)

    MySQL事务处理(TransAction) 大家好,我是胡亦,一名热爱分享技术干货的博主. 思考了很久,决定写一篇关于mysql事务(transaction)的博客,一来嘛,因为最近在复习mysql ...

  5. springboot定时发送短信_SpringBoot第十七篇:定时任务

    引言 相信大家对定时任务很熟悉,其重要性也不言而喻.定时发短信.定时批量操作.定时统计数据等,都离不开定时任务.本文将讲解定时任务在 SpringBoot 项目中的应用. 版本信息 JDK:1.8 S ...

  6. 写代码时发现......还是 SpringBoot 牛逼!一篇拿下

    关注了很多技术类公众号的读者肯定有这样一个感受,SpringBoot相关的文章铺天盖地,并且SpringBoot相关的文章阅读量.收藏量都很高,这也从侧面反映了SpringBoot技术的火爆. 一切都 ...

  7. SpringBoot非官方教程 | 第三篇:SpringBoot用JdbcTemplates访问Mysql

    SpringBoot非官方教程 | 第三篇:SpringBoot用JdbcTemplates访问Mysql 本文介绍springboot通过jdbc访问关系型mysql,通过spring的JdbcTe ...

  8. SpringBoot进阶教程 | 第四篇:整合Mybatis实现多数据源

    这篇文章主要介绍,通过Spring Boot整合Mybatis后如何实现在一个工程中实现多数据源.同时可实现读写分离. 准备工作 环境: windows jdk 8 maven 3.0 IDEA 创建 ...

  9. springboot项目搭建0000-导航篇

    前言:这篇帖子没有任何涉及技术的内容,如果你想找springboot相关的技术知识,可以不用往下看了. 这篇帖子仅仅是对我的springboot相关博客,开辟一个导航页,方便读者.技术大牛略过 主要参 ...

  10. Java学习篇——SpringBoot

    1. 关于Spring Boot Spring Boot框架主要解决了创建工程后需要进行繁琐的配置的问题,是一个"开箱即用"的框架,其核心思想是"约定大于配置" ...

最新文章

  1. 第二章:3、BP神经网络
  2. 32位 shell.efi x86_通过grub,让32位的efi也能运行64位的Linux发行版
  3. 利用Squid反向代理搭建CDN缓存服务器加快Web访问速度
  4. thinkphp5是不是php,我对ThinkPHP5和Laravel5的一些看法
  5. Excel 自动更正选项
  6. 打造轻量级可视化数据爬取工具-菩提
  7. 简书 php三级联动,JS 实现三级联动
  8. Oracle B-Tree Index 原理
  9. 小米第一款智能手表来了 或支持安装手机App
  10. c# 利用t4模板,自动生成Model类
  11. 数据库的数据进行改动,Cognos报表展示未及时更新
  12. 2017国民行业分类sql-存储过程_存储函数-MySQL
  13. mysql 建表语句
  14. 《一天学懂深度学习》PPT翻译一
  15. 如何对C盘进行扩容重新分区?
  16. hdu 4498 自适应simpson
  17. Golang中的并发:如何使用Goroutines?详细指南
  18. Wing IDE中文乱码问题
  19. 零代码搭建一个温度传感器数据采集与显示软件
  20. 制作类似于淘宝点击简单的轮播图

热门文章

  1. 在Web开发中完美控制IE标题栏
  2. [转自华尔街的强帖]怎样才能嫁给有钱人
  3. HA模式下的java api访问要点
  4. WindowsXP、Windows2003本地密码清除方法
  5. Spring中的jar包详解
  6. 通过css使文字有渐变的效果
  7. Apache PDFBox 1.8.1 发布
  8. 内部人示范(野生项目笔记02)
  9. SpringBoot法律知识分享问答论坛 lawbbs.liuyanzhao.com
  10. python 通过逗号分割字符串_「Python 秘籍」使用多个界定符分割字符串