SpringBoot 实战 (十) | 声明式事务

2024-04-27 12:48

本文主要是介绍SpringBoot 实战 (十) | 声明式事务,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

微信公众号:一个优秀的废人
如有问题或建议,请后台留言,我会尽力解决你的问题。

前言

如题,今天介绍 SpringBoot 的 声明式事务。

Spring 的事务机制

所有的数据访问技术都有事务处理机制,这些技术提供了 API 用于开启事务、提交事务来完成数据操作,或者在发生错误时回滚数据。

而 Spring 的事务机制是用统一的机制来处理不同数据访问技术的事务处理,Spring 的事务机制提供了一个 PlatformTransactionManager 接口,不同的数据访问技术的事务使用不同的接口实现,如下表:

数据访问技术实现
JDBCDataSourceTransactionManager
JPAJPATransactionManager
HibernateHibernateTransactionManager
JDOJdoTransactionManager
分布式事务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,此配置类依赖于 JpaBaseConfiguration 和 DataSourceTransactionManagerAutoConfiguration。
    而在 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\u5173datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&useSSL=trueusername: rootpassword: 123456# jpa \u76F8\u5173jpa:hibernate:ddl-auto: update   # ddl-auto:\u8BBE\u4E3A create \u8868\u793A\u6BCF\u6B21\u90FD\u91CD\u65B0\u5EFA\u8868show-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@GeneratedValueprivate 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 的 beanprivate StudentRepository studentRepository;// 使用 @Transactional 注解的 rollbackFor 属性,指定特定异常时,触发回滚@Transactional(rollbackFor = {IllegalArgumentException.class})@Overridepublic Student saveStudentWithRollBack(Student student) {Student s = studentRepository.save(student);if ("高斯林".equals(s.getName())){//硬编码,手动触发异常throw new IllegalArgumentException("高斯林已存在,数据将回滚");}return s;}// 使用 @Transactional 注解的 noRollbackFor 属性,指定特定异常时,不触发回滚@Transactional(noRollbackFor = {IllegalArgumentException.class})@Overridepublic 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@Autowiredprivate 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 方法上打上断点。

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

  1. Postman 测试回滚
    Postman 测试异常导致数据回滚
    debug 模式下可见数据已保存,且获得 id 为 1。:
    回滚

继续执行抛出异常 IllegalArgumentException,将导致数据回滚:
导致数据回滚,控制台打印出信息

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

  1. Postman 测试不回滚

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

遇到 IllegalArgumentException 异常数据不会回滚:
数据不回滚,控制台打印信息

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

源码下载

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

后语

以上为 SpringBoot 声明式事务的教程。最后,对 Python 、Java 感兴趣请长按二维码关注一波,我会努力带给你们价值,如果觉得本文对你哪怕有一丁点帮助,请帮忙点好看,让更多人知道。

另外,关注之后在发送 1024 可领取免费学习资料。资料内容详情请看这篇旧文:Python、C++、Java、Linux、Go、前端、算法资料分享

这篇关于SpringBoot 实战 (十) | 声明式事务的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/940544

相关文章

解释下Java中的IO流和文件操作,以及如何使用Java进行文件读写操作?

在Java中,IO流(Input/Output Stream)是用于处理输入和输出操作的机制,而文件操作则是通过IO流对文件进行读取、写入、创建、删除等操作的具体实现。 一、Java中的IO流 IO流是Java程序中用来处理数据输入和输出的抽象概念。它基于流的概念,将数据的输入和输出看作是一个连续的流。数据从一个地方流向另一个地方,流的方向可以是输入(读取数据)或输出(写入数据)。Java的I

java中的oop(二)、方法、对象数组、重载、参数.

public class Person {/*成员变量存储在堆中*/String name;int age;public void sleep() {String hour = "18小时"; //局部变量;存储在栈中;System.out.println("he is sleepping!");}public void setAge(int age) { //局部变量--形式参数;}publ

Java IO - Reader

前言 JavaIO一共包括两种,一种是stream,一种是reader/writer,每种又包括in/out,所以一共是四种包。Java 流在处理上分为字符流和字节流。字符流处理的单元为 2 个字节的 Unicode 字符,分别操作字符、字符数组或字符串,而字节流处理单元为 1 个字节,操作字节和字节数组。 Java 内用 Unicode 编码存储字符,字符流处理类负责将外部的其他编码的字符流和

【java系列】java开发环境搭建

【java系列】java开发环境搭建 描述 本篇文章主要讲解基于windows 10系统搭建java开发环境,主要内容包括如下: (1)安装资料准备 (2)安装过程讲解 (3)测试是否安装成功 (4)Hello Word测试 1   安装材料准备 java开发环境搭建前,需要准备JDK和Eclipse。 1.1  JDK 下载地址:http://www.oracle.com/t

Java常见错误列表

Java常见错误列表 Java常见错误列表: 找不到符号(symbol)类X是public的,应该被声明在名为X.java的文件中缺失类、接口或枚举类型缺失X缺失标识符非法的表达式开头类型不兼容非法的方法声明;需要返回类型数组越界(java.lang.ArrayIndexOutOfBoundsException)字符越界(java.lang.StringIndexOutOfBound

MySQL变量声明与使用

#MySQL变量声明与使用 变量命名规范 #1 标识符不能以数字作为开头 #2 只能使用_或着$符号 #3 不允许使用系统关键字 set @userName = '刘德华';select @userName:='刘青云';#将赋值与查询结合 查询变量/使用变量 匿名的时候建议加上as select @userName as '读取到的userName变量值'; 整数类型与浮点数

Springboot-Jedis实现分布式锁

依赖 <dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.3.0</version></dependency> 配置类 @Configuration@EnableConfigurationProperties(RedisProperties.class)public

【原创】springboot+mysql物资库存管理系统设计与实现

个人主页:程序猿小小杨 个人简介:从事开发多年,Java、Php、Python、前端开发均有涉猎 博客内容:Java项目实战、项目演示、技术分享 文末有作者名片,希望和大家一起共同进步,你只管努力,剩下的交给天意。 前言: 传统的物资管理方式往往依赖于人工操作和纸质记录,存在数据不准确、操作繁琐、效率低下等问题。这些问题不仅增加了企业的运营成本,还可能影响企业的市场竞争力。因此,开发一

基于springboot+mybatis+vue的项目实战之前端

步骤: 1、项目准备:新建项目,并删除自带demo程序,修改application.properties. 2、使用Apifox准备好json数据的mock地址 3、编写基于vue的静态页面 4、运行 整个的目录结构如下: 0、项目准备 新建项目,并删除自带demo程序,修改application.properties. 必须配置,注意必须修改为自己的数据库名称,以及数据库

【Java 8】Lambda: mAA::get 返回函数式接口实例

正文   代码介绍:分别有两个函数式接口Lazy跟LazyDependencyCreator,一个HashMap存储<Object, LazyDependencyCreator>键值对。重点在于mProviders.put(AA.class,mAA::get),看了很久都没看懂,mAA::get返回的不是mAA本身吗,为什么能转化为LazyDependencyCreator存储在map