SpringBoot 入门教程 (十) | 声明式事务

本贴最后更新于 1427 天前,其中的信息可能已经沧海桑田

前言

如题,今天介绍 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 的事务支持

  1. 自动配置的事务管理器

在使用 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() }
  1. 自动开启注解事务的支持

SpringBoot 专门用于配置事务的类为 org.springframework.boot.autoconfigure.transcation.TransactionAutoConfiguration,此配置类依赖于 JpaBaseConfigurationDataSourceTransactionManagerAutoConfiguration

而在 DataSourceTransactionManagerAutoConfiguration 配置里还开启了对声明式事务的支持,代码如下:

@ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class) @Configuration @EnableTransactionManagement protected static class TransactionManagementConfiguration{ }

所以在 SpringBoot 中,无须显式开启使用 @EnableTransactionManagement 注解。

实战

演示如何使用 Transactional 使用异常导致数据回滚与使用异常导致数据不回滚。

  1. 准备工作:
    SpringBoot 2.1.3
    JDK 1.8
    IDEA
  2. pom.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>

代码注释很清楚,没啥好说的。

  1. 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
  1. 实体类:
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; }
  1. 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> { }
  1. 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; } }

代码注释同样很清楚,没啥好说的。

  1. 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 (调试模式) 启动程序。并在 StudentServiceImplsaveStudentWithRollBack 方法上打上断点。

测试前数据库结果:

测试前数据库结果

  1. Postman 测试回滚

Postman 测试异常导致数据回滚

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

回滚

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

导致数据回滚,控制台打印出信息

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

测试后数据库结果

  1. Postman 测试不回滚

测试前数据库结果:
测试前数据库结果

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

数据不回滚,控制台打印信息

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

新增数据,数据不回滚

源码下载

https://github.com/turoDog/Demo/tree/master/springboot_transaction_demo

后语

以上为 SpringBoot 声明式事务的教程。

初次见面,也不知道送你们啥。干脆就送几百本电子书2021 最新面试资料吧。微信搜索 JavaFish 回复电子书送你 1000+ 本编程电子书;回复面试获取 50 套大厂面试题;回复 1024 送你一套完整的 java 视频教程。

面试题都是有答案的,如下所示:有需要的就来拿吧,绝对免费,无套路获取

面试题

公众号

  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    948 引用 • 1460 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...