Spring Boot2 系列教程 (十) | 实现声明式事务
前言
如题,今天介绍 SpringBoot 的 声明式事务。
Spring 的事务机制
所有的数据访问技术都有事务处理机制,这些技术提供了 API 用于开启事务、提交事务来完成数据操作,或者在发生错误时回滚数据。
而 Spring 的事务机制是用统一的机制来处理不同数据访问技术的事务处理,Spring 的事务机制提供了一个 PlatformTransactionManager 接口,不同的数据访问技术的事务使用不同的接口实现,如下表:
| 数据访问技术 | 实现 |
|---|---|
| JDBC | DataSourceTransactionManager |
| JPA | JPATransactionManager |
| Hibernate | HibernateTransactionManager |
| JDO | JdoTransactionManager |
| 分布式事务 | JtaTransactionManager |
声明式事务
Spring 支持声明式事务,即使用注解来选择需要使用事务的方法,他使用 @Transactional 注解在方法上表明该方法需要事务支持。被注解的方法在被调用时,Spring 开启一个新的事务,当方法无异常运行结束后,Spring 会提交这个事务。如:
@Transactional
public void saveStudent(Student student){
// 数据库操作
}
注意,@Transactional 注解来自于 org.springframework.transcation.annotation 包,而不是 javax.transaction。
Spring 提供一个 @EnableTranscationManagement 注解在配置类上来开启声明式事务的支持。使用了 @EnableTranscationManagement 后,Spring 容器会自动扫描注解 @Transactional 的方法与类。@EnableTranscationManagement 的使用方式如下:
@Configuration
@EnableTranscationManagement
public class AppConfig{
}
注解事务行为
@Transactional 有如下表所示的属性来定制事务行为。
| 属性 | 含义 |
|---|---|
| propagation | 事务传播行为 |
| isolation | 事务隔离级别 |
| readOnly | 事务的读写性,boolean型 |
| timeout | 超时时间,int型,以秒为单位。 |
| rollbackFor | 一组异常类,遇到时回滚。(rollbackFor={SQLException.class}) |
| rollbackForCalssName | 一组异常类名,遇到回滚,类型为 string[] |
| noRollbackFor | 一组异常类,遇到不回滚 |
| norollbackForCalssName | 一组异常类名,遇到时不回滚。 |
类级别使用 @Transactional
@Transactional 不仅可以注解在方法上,还可以注解在类上。注解在类上时意味着此类的所有 public 方法都是开启事务的。如果类级别和方法级别同时使用了 @Transactional 注解,则使用在类级别的注解会重载方法级别的注解。
SpringBoot 的事务支持
- 自动配置的事务管理器
在使用 JDBC 作为数据访问技术时,配置定义如下:
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(DataSource.class)
public PlatformTransactionManager transactionManager(){
return new DataSourceTransactionManager(this.dataSource)
}
在使用 JPA 作为数据访问技术时,配置定义如下:
@Bean
@ConditionalOnMissingBean(PlatformTransactionManager.class)
public PlatformTransactionManager transactionManager(){
return new JpaTransactionManager()
}
- 自动开启注解事务的支持
SpringBoot 专门用于配置事务的类为 org.springframework.boot.autoconfigure.transcation.TransactionAutoConfiguration,此配置类依赖于 JpaBaseConfiguration 和 DataSourceTransactionManagerAutoConfiguration。
而在 DataSourceTransactionManagerAutoConfiguration 配置里还开启了对声明式事务的支持,代码如下:
@ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)
@Configuration
@EnableTransactionManagement
protected static class TransactionManagementConfiguration{
}
所以在 SpringBoot 中,无须显式开启使用 @EnableTransactionManagement 注解。
实战
演示如何使用 Transactional 使用异常导致数据回滚与使用异常导致数据不回滚。
准备工作:
SpringBoot 2.1.3
JDK 1.8
IDEApom.xml 依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.nasus</groupId>
<artifactId>transaction</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>transaction</name>
<description>transaction Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- JPA 相关 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- web 启动类 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mysql 连接类 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- lombok 插件,简化实体代码 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.20</version>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
代码注释很清楚,没啥好说的。
- application.yaml 配置:
spring:
# \u6570\u636E\u5E93\u76F8\u5173
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&useSSL=true
username: root
password: 123456
# jpa \u76F8\u5173
jpa:
hibernate:
ddl-auto: update # ddl-auto:\u8BBE\u4E3A create \u8868\u793A\u6BCF\u6B21\u90FD\u91CD\u65B0\u5EFA\u8868
show-sql: true
- 实体类:
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
public class Student {
@Id
@GeneratedValue
private Integer id;
private String name;
private Integer age;
}
- dao 层
import com.nasus.transaction.domain.Student;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface StudentRepository extends JpaRepository<Student, Integer> {
}
- service 层
import com.nasus.transaction.domain.Student;
public interface StudentService {
Student saveStudentWithRollBack(Student student);
Student saveStudentWithoutRollBack(Student student);
}
实现类:
import com.nasus.transaction.domain.Student;
import com.nasus.transaction.repository.StudentRepository;
import com.nasus.transaction.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class StudentServiceImpl implements StudentService {
@Autowired
// 直接注入 StudentRepository 的 bean
private StudentRepository studentRepository;
// 使用 @Transactional 注解的 rollbackFor 属性,指定特定异常时,触发回滚
@Transactional(rollbackFor = {IllegalArgumentException.class})
@Override
public Student saveStudentWithRollBack(Student student) {
Student s = studentRepository.save(student);
if ("高斯林".equals(s.getName())){
//硬编码,手动触发异常
throw new IllegalArgumentException("高斯林已存在,数据将回滚");
}
return s;
}
// 使用 @Transactional 注解的 noRollbackFor 属性,指定特定异常时,不触发回滚
@Transactional(noRollbackFor = {IllegalArgumentException.class})
@Override
public Student saveStudentWithoutRollBack(Student student) {
Student s = studentRepository.save(student);
if ("高斯林".equals(s.getName())){
throw new IllegalArgumentException("高斯林已存在,数据将不会回滚");
}
return s;
}
}
代码注释同样很清楚,没啥好说的。
- controller 层
import com.nasus.transaction.domain.Student;
import com.nasus.transaction.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/student")
public class StudentController {
// 注入 studentservice 的 bean
@Autowired
private StudentService studentService;
// 测试回滚情况
@PostMapping("/withRollBack")
public Student saveStudentWithRollBack(@RequestBody Student student){
return studentService.saveStudentWithRollBack(student);
}
// 测试不回滚情况
@PostMapping("/withOutRollBack")
public Student saveStudentWithoutRollBack(@RequestBody Student student){
return studentService.saveStudentWithoutRollBack(student);
}
}
Postman 测试结果
为了更清楚地理解回滚,以 debug (调试模式) 启动程序。并在 StudentServiceImpl 的 saveStudentWithRollBack 方法上打上断点。
测试前数据库结果:

- Postman 测试回滚

debug 模式下可见数据已保存,且获得 id 为 1。:

继续执行抛出异常 IllegalArgumentException,将导致数据回滚:

测试后数据库结果:并无新增数据,回滚成功。

- Postman 测试不回滚
测试前数据库结果:

遇到 IllegalArgumentException 异常数据不会回滚:

测试后数据库结果:新增数据,数据不回滚。

源码下载
https://github.com/turoDog/Demo/tree/master/springboot_transaction_demo
最后
如果看到这里,说明你喜欢这篇文章,请转发、点赞。微信搜索「一个优秀的废人」,关注后回复「1024」送你一套完整的 java 教程。
Spring Boot2 系列教程 (十) | 实现声明式事务的更多相关文章
- Spring Boot2 系列教程(十)Spring Boot 整合 Freemarker
今天来聊聊 Spring Boot 整合 Freemarker. Freemarker 简介 这是一个相当老牌的开源的免费的模版引擎.通过 Freemarker 模版,我们可以将数据渲染成 HTML ...
- Spring Boot2 系列教程 (十八) | 整合 MongoDB
微信公众号:一个优秀的废人.如有问题,请后台留言,反正我也不会听. 前言 如题,今天介绍下 SpringBoot 是如何整合 MongoDB 的. MongoDB 简介 MongoDB 是由 C++ ...
- Spring Boot2 系列教程 (十四) | 统一异常处理
如题,今天介绍 SpringBoot 是如何统一处理全局异常的.SpringBoot 中的全局异常处理主要起作用的两个注解是 @ControllerAdvice 和 @ExceptionHandler ...
- Spring Boot2 系列教程 (十二) | 整合 thymeleaf
前言 如题,今天介绍 Thymeleaf ,并整合 Thymeleaf 开发一个简陋版的学生信息管理系统. SpringBoot 提供了大量模板引擎,包含 Freemarker.Groovy.Thym ...
- Spring Boot2 系列教程(十二)@ControllerAdvice 的三种使用场景
严格来说,本文并不算是 Spring Boot 中的知识点,但是很多学过 SpringMVC 的小伙伴,对于 @ControllerAdvice 却并不熟悉,Spring Boot 和 SpringM ...
- Spring Boot2 系列教程(十四)CORS 解决跨域问题
今天和小伙伴们来聊一聊通过CORS解决跨域问题. 同源策略 很多人对跨域有一种误解,以为这是前端的事,和后端没关系,其实不是这样的,说到跨域,就不得不说说浏览器的同源策略. 同源策略是由 Netsca ...
- Spring Boot2 系列教程(十五)定义系统启动任务的两种方式
在 Servlet/Jsp 项目中,如果涉及到系统任务,例如在项目启动阶段要做一些数据初始化操作,这些操作有一个共同的特点,只在项目启动时进行,以后都不再执行,这里,容易想到web基础中的三大组件( ...
- Spring Boot2 系列教程(十六)定时任务的两种实现方式
在 Spring + SpringMVC 环境中,一般来说,要实现定时任务,我们有两中方案,一种是使用 Spring 自带的定时任务处理器 @Scheduled 注解,另一种就是使用第三方框架 Qua ...
- Spring Boot2 系列教程(十八)Spring Boot 中自定义 SpringMVC 配置
用过 Spring Boot 的小伙伴都知道,我们只需要在项目中引入 spring-boot-starter-web 依赖,SpringMVC 的一整套东西就会自动给我们配置好,但是,真实的项目环境比 ...
随机推荐
- 小程序与HTML5的区别
小程序与传统HTML5还是有明显的区别,主要区别在于: 开发工具不同: 区别于H5的开发工具+浏览器Device Mode预览的模式,小程序的开发基于自己的开发者工具,可以实现同步本地文件+开发调试+ ...
- 获取exe和dll里面的资源
有时候需要仿照另一个程序实现一些对话框,比较笨的办法是打开那个程序,照着样子自己在VC里面画啊画.这样的效率实在有点低. 现在有很多工具可以从exe和dll里面取出图片.图片.字符串.对话框等资源.比 ...
- H3C 入站包过滤工作流程
- 140种Python标准库、第三方库和外部工具
导读:Python数据工具箱涵盖从数据源到数据可视化的完整流程中涉及到的常用库.函数和外部工具.其中既有Python内置函数和标准库,又有第三方库和工具. 这些库可用于文件读写.网络抓取和解析.数据连 ...
- python基础十四之匿名函数
匿名函数 处理简单问题的简化函数,关键字lambda. # 格式:函数名 = lambda 参数:返回值 anonymity = lambda s: s ** 0.5 print(anonymity( ...
- linux 手动睡眠
在 Linux 内核的之前的版本, 正式的睡眠要求程序员手动处理所有上面的步骤. 它是一 个繁琐的过程, 包含相当多的易出错的样板式的代码. 程序员如果愿意还是可能用那种方 式手动睡眠; <li ...
- 【t056】智力问答(multiset做法)
Time Limit: 1 second Memory Limit: 128 MB [问题描述] 新年联欢会上,G.Sha负责组织智力问答节目.G.Sha建立了一个很大很大的超级题库,并衡量了每道题的 ...
- 2019-2-27-win10-uwp-去掉-Flyout-边框
title author date CreateTime categories win10 uwp 去掉 Flyout 边框 lindexi 2019-02-27 17:48:46 +0800 201 ...
- Shave Beaver! CodeForces - 331B2 (线段树)
题面 The Smart Beaver has recently designed and built an innovative nanotechnologic all-purpose beaver ...
- cglib的动态代理
前言 jdk中的动态代理通过反射类Proxy和InvocationHandler回调接口实现,要求委托类必须实现一个接口,只能对该类接口中定义的方法实现代理,这在实际编程中有一定的局限性. cglib ...