Java框架-Spring框架-3
背景
本文是《Java 后端从小白到大神》修仙系列之框架学习,Java框架之Spring框架第三篇
。本篇文章主要聊Java框架
,那么必然从Spring
框架开始,可以说Spring框架是Java企业级开发的基石,若想详细学习请点击首篇博文,我们现在开始吧。
文章概览
- 数据访问与事务管理(Data Access & Tx)
Spring框架
数据访问与事务管理(Data Access & Tx)
1. 数据访问与事务管理
Spring 给我们提供了强大的数据访问机制(如 JdbcTemplate、ORM 集成、MyBatis 支持)和灵活的事务控制(包括声明式事务、编程式事务、事务传播行为、隔离级别、以及多数据源与分布式事务等),极大地简化了企业级应用的数据库操作和事务管理。
下图简要概述了 Spring 如何从数据库连接开始,层层递进地提供数据访问和对象映射能力:
graph TD
A[应用代码] --> B{Spring 数据访问层}
B --> C[DataSource - 数据源]
C -- 实现 --> D[DriverManagerDataSource]
C -- 实现 --> E[连接池: HikariCP / Druid]
D --> F[数据库]
E --> F
B --> G[JdbcTemplate]
G -- 依赖 --> C
G -- 操作SQL --> F
B --> H[ORM 集成: JPA/Hibernate]
H -- 依赖 --> C
H -- 映射Java对象 <--> 数据库表 --> F
H -- JPA标准实现 --> I[Hibernate]
B --> J[MyBatis 支持]
J -- 依赖 --> C
J -- Mapper接口 + XML/注解SQL --> F
J -- 核心会话 --> K[SqlSession]
K -- Spring管理 --> L[SqlSessionTemplate]
L --> J
subgraph 核心流程
C --> G
C --> H
C --> J
end
style A fill:#f9f,stroke:#333,stroke-width:2px
style B fill:#bbf,stroke:#333,stroke-width:2px
style C fill:#dff,stroke:#333,stroke-width:2px
style G fill:#fbf,stroke:#333,stroke-width:2px
style H fill:#fdf,stroke:#333,stroke-width:2px
style J fill:#efe,stroke:#333,stroke-width:2px
2. JdbcTemplate:简化 JDBC 操作
- 概念:Spring JDBC 提供的
JdbcTemplate
是对原生 JDBC API 的高度封装,旨在简化样板代码、自动管理资源(如数据库连接的获取与关闭)、并提供统一的异常翻译,从而大幅提升开发效率和代码可维护性。 - 配置:在使用
JdbcTemplate
前,你需要向 Spring 容器注入(Dependency Injection)一个DataSource
Bean。DataSource
是一个标准的 Java 接口(javax.sql.DataSource
),它定义了获取数据库连接的方法。- 实现方式:
DriverManagerDataSource
:这是 Spring 框架提供的一个最简单的DataSource
实现,每次需要数据库连接时,它都会直接创建一个新的 JDBC 连接。它不具备连接池功能,通常只用于开发和测试环境,不适合高并发的生产环境。- 连接池(如 HikariCP、Druid、Tomcat JDBC Connection Pool 等):这些是更高级、性能更优的
DataSource
实现。它们会维护一个数据库连接的“池子”。当应用程序需要连接时,它们从池中获取一个已存在且空闲的连接;当连接使用完毕后,连接不会被关闭,而是放回池中复用。这显著提高了连接效率和系统吞吐量,是生产环境的首选。
- 实现方式:
- 统一的异常封装:Spring 将底层 JDBC 抛出的、数据库厂商特定的
SQLException
捕获,并将其**翻译(Translate)**成 Spring 自己的、统一且更具语义化的DataAccessException
异常体系及其子类(例如DuplicateKeyException
表示主键重复,DataIntegrityViolationException
表示数据完整性违规)。这使得你的错误处理代码更清晰,不依赖于具体的数据库厂商,提升了代码的可移植性。
配置示例
|
|
常用 API
jdbcTemplate.update(sql, args...)
:执行 INSERT/UPDATE/DELETE 操作。jdbcTemplate.queryForObject(sql, rowMapper, args...)
:查询并返回单行结果。jdbcTemplate.query(sql, rowMapper, args...)
:查询并返回多行结果(列表)。jdbcTemplate.batchUpdate(sql, batchArgs)
:执行批量更新。jdbcTemplate.queryForList(sql, elementType)
:返回简单类型(如 String, Integer)列表。- 命名参数支持:对于复杂的 SQL 语句,Spring 还提供了
NamedParameterJdbcTemplate
,允许使用命名参数(例如:username
)代替传统的问号占位符?
,大大提高了 SQL 语句的可读性和维护性。
示例 DAO
|
|
3. ORM 集成(Hibernate / JPA):对象与关系的映射
- 概念:
- JPA(Java Persistence API):它是一个 Java 标准/规范,定义了对象关系映射(ORM)的 API。它规定了如何使用注解将 Java 对象映射到数据库表,以及如何通过
EntityManager
来执行数据库操作。 - Hibernate:它是 JPA 规范的一个具体实现。当你配置使用 JPA 时,底层通常会选择一个实现,Hibernate 是其中最流行和功能最强大的。
- Spring 通过
LocalContainerEntityManagerFactoryBean
(用于 JPA)或LocalSessionFactoryBean
(用于原生 Hibernate)将 ORM 框架深度集成到其容器中。
- JPA(Java Persistence API):它是一个 Java 标准/规范,定义了对象关系映射(ORM)的 API。它规定了如何使用注解将 Java 对象映射到数据库表,以及如何通过
- 配置:在 Spring 应用中集成 ORM,你需要进行以下配置,告诉 Spring 如何设置 ORM 环境:
- 数据源:与 JdbcTemplate 配置类似,ORM 也需要一个
DataSource
Bean 来获取数据库连接。 - 实体扫描:
packagesToScan
属性指定了 Spring 应该去哪个包下扫描带有@Entity
注解的实体类,以便 ORM 框架能够建立 Java 对象与数据库表之间的映射关系。 - 方言、DDL 自动化等属性:这些是 ORM 框架(如 Hibernate)特有的配置,例如
hibernate.dialect
用于告诉 Hibernate 当前使用的数据库类型,以便它生成正确的 SQL 语句;hibernate.hbm2ddl.auto
用于控制启动时数据库表结构的自动更新策略。
- 数据源:与 JdbcTemplate 配置类似,ORM 也需要一个
配置示例:JPA 和 Spring 事务管理器
这段代码定义了两个核心的 Spring Bean,用于初始化 JPA/Hibernate 环境,并将其与 Spring 的事务管理体系整合:
-
LocalContainerEntityManagerFactoryBean
(emf
Bean):- 作用:这是一个 Spring 提供的工厂 Bean,负责创建 JPA 的核心接口
EntityManagerFactory
。它就像是 JPA 的“会话工厂”,负责创建EntityManager
实例(EntityManager
是用于实际数据库操作的接口)。 - 关键配置:包括设置数据源、扫描实体类所在的包、指定 JPA 供应商(这里是 Hibernate)以及配置 Hibernate 的具体属性。
- 作用:这是一个 Spring 提供的工厂 Bean,负责创建 JPA 的核心接口
-
JpaTransactionManager
(transactionManager
Bean):- 作用:这是 Spring 事务管理的核心接口
PlatformTransactionManager
的一个 JPA 实现。它负责将 Spring 的事务管理(特别是@Transactional
注解)与底层的 JPA 事务进行集成,确保你的数据库操作能够正确地开启、提交或回滚事务。
- 作用:这是 Spring 事务管理的核心接口
|
|
- 使用:在完成上述配置后,你就可以开始定义你的数据模型和数据操作接口了:
- 实体类:在普通的 Java 类上使用
@Entity
、@Table
、@Id
、@GeneratedValue
等 JPA 注解来标注,将它们映射到数据库表。通过这些实体类,你可以在 Java 代码中面向对象地操作数据库数据。 - Repository 接口:你可以通过两种主要方式进行数据操作:
- 继承
JpaRepository
(Spring Data JPA 推荐):这是最推荐和最简化的方式。通过继承 Spring Data JPA 提供的JpaRepository
接口,你可以自动获得大量的基本 CRUD(创建、读取、更新、删除)操作方法,而无需编写任何实现代码。 - 手写
@Repository
并注入EntityManager
:如果你需要更细粒度的控制或执行复杂的 JPA 操作,也可以自定义一个 DAO 类,用@Repository
标注,并注入EntityManager
实例来直接进行数据库交互。
- 继承
- 实体类:在普通的 Java 类上使用
示例:Spring Data JPA(定义实体和 Repository)
这段代码展示了如何利用 Spring Data JPA 来定义数据模型和数据访问接口,极大地简化了 DAO 层开发:
-
Product
实体类:- 作用:这是一个普通的 Java 对象,但由于使用了
@Entity
和@Table
等 JPA 注解,它被映射到了数据库中的products
表。当你操作Product
对象时,ORM 框架会在底层将其转换为 SQL 操作数据库。
- 作用:这是一个普通的 Java 对象,但由于使用了
-
ProductRepo
接口:- 作用:通过继承
JpaRepository<Product, Long>
,Spring Data JPA 会在运行时自动为你生成针对Product
实体的各种 CRUD 方法的实现。你无需手动编写save()
,findById()
,findAll()
等方法的代码。 findByNameContaining(String keyword)
方法则演示了 Spring Data JPA 的“方法名解析查询”能力:Spring 会根据方法名自动生成对应的 SQL 查询语句(例如SELECT * FROM products WHERE name LIKE '%keyword%'
),进一步减少了 SQL 编写量。
- 作用:通过继承
|
|
4. SQLSession & MyBatis 支持
MyBatis 是一个优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。在 Spring 应用中,MyBatis 能够深度集成,提供灵活且强大的数据访问能力。
4.1 SqlSession
核心概念
在深入了解 MyBatis 与 Spring 的集成之前,我们首先要理解 MyBatis 中的一个核心概念:SqlSession
。
- 什么是
SqlSession
?SqlSession
是 MyBatis 对外暴露的主要操作接口,它代表了 MyBatis 与数据库之间的一次会话(Session)。你可以把它想象成 MyBatis 版的 JDBCConnection
,但它提供了更高层次的抽象和更丰富的功能。 SqlSession
的作用:- 执行 SQL 命令的门户:它是你执行 SQL 语句(无论是查询、插入、更新还是删除)的入口。当你调用 Mapper 接口中的方法时,MyBatis 内部就是通过
SqlSession
实例来完成与数据库的实际交互。 - 事务管理:
SqlSession
可以用于手动管理事务(例如调用commit()
或rollback()
)。但在 Spring 环境中,通常是由 Spring 的事务管理器来统一管理SqlSession
的事务生命周期,让你能够通过简单的@Transactional
注解来实现声明式事务。 - 一级缓存管理:每个
SqlSession
都拥有自己独立的一级缓存。在同一个SqlSession
的生命周期内,如果多次查询相同的数据,MyBatis 会直接从一级缓存中获取,从而避免不必要的数据库访问,提高性能。
- 执行 SQL 命令的门户:它是你执行 SQL 语句(无论是查询、插入、更新还是删除)的入口。当你调用 Mapper 接口中的方法时,MyBatis 内部就是通过
- 生命周期:
SqlSession
是非线程安全的,而且是短生命周期的对象。最佳实践是将其视为一次请求或一个业务操作的“工作单元”,在每次数据库操作时被创建,并在操作完成后立即关闭。
4.2 MyBatis 与 Spring 的集成关键点
在 Spring 框架中,MyBatis 与 Spring 深度集成,通常不会直接手动创建和管理 SqlSession
。这是因为 Spring 提供了一系列集成组件,简化了 SqlSession
的管理,使其与 Spring 的 IoC 容器和事务管理无缝协作。
SqlSessionFactory
和SqlSessionTemplate
:SqlSessionFactory
:类似于 JPA 中的EntityManagerFactory
,它是创建SqlSession
实例的工厂。它是线程安全的,通常一个应用只需要一个实例。SqlSessionTemplate
:这是 Spring 集成 MyBatis 的核心。它是线程安全的,并且可以被安全地注入到你的 DAO 或 Service 中。SqlSessionTemplate
会智能地管理SqlSession
的生命周期,确保每个线程拥有独立的SqlSession
实例,并在操作完成后正确关闭。同时,它会自动参与到 Spring 的事务管理中,使得SqlSession
的操作能够被 Spring 的@Transactional
注解所控制。- 在 Spring Boot 项目中,MyBatis 的集成非常简单,只需添加相应依赖,Spring Boot 会自动配置好
SqlSessionFactory
和SqlSessionTemplate
,大大简化了配置。在非 Spring Boot 环境下,你可能需要手动配置SqlSessionFactoryBean
来创建SqlSessionFactory
,以及SqlSessionTemplate
Bean。
- Mapper 接口 + XML 映射:MyBatis 允许你将 SQL 语句写在独立的 XML 映射文件中,并通过 Mapper 接口与这些 SQL 语句进行绑定。这种方式提供了极大的灵活性,允许你编写复杂的、高度定制化的 SQL。
- 支持分页插件、缓存等功能:MyBatis 生态系统提供了丰富的插件机制,例如著名的 PageHelper 用于实现方便的分页查询,以及自带的二级缓存功能(与
SqlSession
的一级缓存不同,二级缓存是跨SqlSession
共享的,通常需要额外配置和开启)。
示例代码:
4.3 Spring Data 简介
概述:Spring Data 是一个 umbrella project(伞形项目),旨在提供统一的数据访问抽象,并简化不同数据存储(如关系型数据库、NoSQL 数据库、云数据服务等)的访问。其主要子项目有 Spring Data JPA、Spring Data MongoDB、Spring Data Redis 等,专注于简化 DAO 层开发。
- Repository 接口自动实现:通过继承 Spring Data 提供的 Repository 接口,开发者无需编写 DAO 实现类,Spring Data 会在运行时自动生成实现。
- 方法命名自动派生 SQL:Spring Data JPA 尤其擅长根据 Repository 接口中定义的方法名(如
findByUsernameAndEmail
)自动生成对应的 SQL 查询。 - 自定义查询:支持通过
@Query
注解编写 JPQL 或原生 SQL,或者使用 QueryDSL 进行类型安全的查询。
示例代码:
5. 异常处理与统一封装:更友好的数据库错误处理
概述:Spring 致力于提供一套统一且可移植的数据访问异常体系,以解决底层数据库异常的厂商相关性问题。
DataAccessException
统一异常体系:Spring 会捕获底层数据库(如 JDBC 的SQLException
)或 ORM 框架抛出的具体异常,并将其翻译成org.springframework.dao.DataAccessException
及其子类。这个异常体系是非检查异常(Unchecked Exception),这意味着你通常不需要强制try-catch
。- 不再暴露 JDBC 原生异常:这种机制使得业务代码无需关心底层数据库的具体实现细节(如不同的错误码),只需处理 Spring 统一的
DataAccessException
即可。
示例代码:
6. 事务管理:保证数据操作的原子性
- 概念:事务是数据库操作中不可或缺的一部分,它确保一组操作要么全部成功,要么全部失败(原子性)。事务具备 ACID 特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。Spring 提供统一抽象
PlatformTransactionManager
,支持多种事务管理器(如DataSourceTransactionManager
、JpaTransactionManager
、HibernateTransactionManager
、JtaTransactionManager
),并支持声明式和编程式事务管理。
6.1 声明式事务管理:更简洁、面向注解
- 方式:这是 Spring 事务管理最常用和推荐的方式。通过在配置类上添加
@EnableTransactionManagement
注解,并在 Service 层的方法或类上添加@Transactional
注解,Spring 就能自动地为这些方法创建 AOP 代理,在方法执行前开启事务,方法成功执行后提交事务,发生异常时回滚事务,无需手动编写事务管理代码。 - 底层实现:声明式事务的强大之处在于它依赖于 Spring 的 AOP (面向切面编程) 机制。
@EnableTransactionManagement
会激活 Spring 容器,使其能够识别@Transactional
注解,并为被@Transactional
标注的 Bean 自动创建代理对象(通常是 JDK 动态代理或 CGLIB 代理)。当客户端调用被代理的方法时,实际上是代理对象拦截了调用,并在方法执行前后织入(weave)了事务管理逻辑。
|
|
- 事务属性详解:
- 传播行为(
propagation
):定义了当一个方法在一个事务中调用另一个方法时,事务应该如何传播。常用的有:Propagation.REQUIRED
(默认):如果当前没有事务,就新建一个;如果当前存在事务,就加入到这个事务中。Propagation.REQUIRES_NEW
:总是新建一个事务,如果当前存在事务,就将当前事务挂起。Propagation.NESTED
:如果当前存在事务,则在嵌套事务中执行;如果当前没有事务,则新建一个事务。
- 隔离级别(
isolation
):定义了事务之间的隔离程度,以解决并发事务可能导致的问题:Isolation.READ_UNCOMMITTED
:最低级别,可能发生脏读、不可重复读、幻读。Isolation.READ_COMMITTED
(大多数数据库默认):避免脏读,可能发生不可重复读、幻读。Isolation.REPEATABLE_READ
(MySQL 默认):避免脏读、不可重复读,可能发生幻读。Isolation.SERIALIZABLE
:最高级别,避免所有并发问题,但性能最低。
- 只读(
readOnly
):true
表示事务是只读的。对于查询操作,可以设置为true
,有助于底层数据库进行性能优化,但如果在此事务中执行了写入操作,则会抛出异常。 - 回滚策略(
rollbackFor
,noRollbackFor
):控制哪些异常会触发事务回滚。- Spring 默认回滚规则:Spring 事务默认只对运行时异常(
RuntimeException
及其子类)和Error
进行回滚。对于受检异常(Checked Exception,即非RuntimeException
的Exception
子类,例如IOException
),默认不触发回滚。 rollbackFor = {Exception.class}
:明确指定遇到Exception
类或其任何子类都回滚。noRollbackFor = {MyCheckedException.class}
:明确指定即使遇到MyCheckedException
也不回滚。
- Spring 默认回滚规则:Spring 事务默认只对运行时异常(
- 传播行为(
测试事务
|
|
6.2 编程式事务管理:手动控制事务边界
- 方式:编程式事务管理需要你手动编写代码来控制事务的开始、提交和回滚。虽然不如声明式事务常用,但在某些特殊场景(例如事务边界需要非常动态地控制,或在非 Spring 管理的线程中执行事务)下仍然有用。
- 主要方式:
- 使用
PlatformTransactionManager
接口:直接调用其getTransaction()
、commit()
和rollback()
方法。这种方式代码量较大,且需要手动处理异常。 - 使用
TransactionTemplate
类:Spring 推荐的编程式事务方式。它封装了PlatformTransactionManager
的样板代码和异常处理,提供了更简洁的回调机制。
- 使用
示例:使用 TransactionTemplate
(推荐)
|
|
6.3 事务同步(Transaction Synchronization)
- 概念:Spring 允许你注册事务同步回调,这些回调会在事务成功提交或回滚之后执行。它们常用于在事务完成后执行一些资源清理、事件发布、缓存更新、发送通知等操作,确保这些操作与事务的最终结果一致。
- 使用:通过
TransactionSynchronizationManager.registerSynchronization()
方法注册TransactionSynchronization
接口的实现。这个接口提供了afterCommit()
,afterCompletion()
(无论提交还是回滚),beforeCommit()
,beforeCompletion()
等回调方法。
7. 分布式事务与多数据源支持
概述:在复杂的企业级应用中,常常需要操作多个独立的数据源,或者在跨多个服务/数据库的场景下保证操作的原子性,这就涉及到了多数据源和分布式事务。Spring 对此提供了完善的支持,包括管理多个 DataSource
和集成 JTA(Java Transaction API)分布式事务管理器。
下图展示了多数据源和分布式事务的常见场景及解决方案:
graph TD
A[Spring应用] --> B{多数据源};
subgraph 单个事务管理器
B --> C1[DataSource1 - DB1];
B --> C2[DataSource2 - DB2];
C1 --> TM1[DataSourceTransactionManager1];
C2 --> TM2[DataSourceTransactionManager2];
TM1 --> S1[ServiceMethodA];
TM2 --> S2[ServiceMethodB];
end
subgraph 跨服务/数据库的分布式事务
D[分布式事务需求];
D --> E[XA事务 - 两阶段提交];
E -- 协调者 --> F[JTA事务管理器: Atomikos/Narayana];
F -- 资源 --> G1[DB1];
F -- 资源 --> G2[DB2];
E -- 痛点 --> H[复杂、性能差、阻塞];
D --> I[柔性事务 - 微服务推荐];
I --> J[Seata - AT/TCC/SAGA/XA];
J -- 实现模式 --> K1[AT模式 - 自动补偿];
J -- 实现模式 --> K2[TCC模式 - Try-Confirm-Cancel];
J -- 实现模式 --> K3[SAGA模式 - 长事务];
I -- 特点 --> L[最终一致性、高可用、高性能];
end
style A fill:#f9f,stroke:#333,stroke-width:2px;
style B fill:#bbf,stroke:#333,stroke-width:2px;
style D fill:#fbd,stroke:#333,stroke-width:2px;
style E fill:#fdd,stroke:#333,stroke-width:2px;
style I fill:#dfd,stroke:#333,stroke-width:2px;
style J fill:#afa,stroke:#333,stroke-width:2px;
- 多数据源配置:在 Spring 中,你可以配置多个
DataSource
Bean,每个 Bean 对应一个独立的数据库连接。通过@Primary
和@Qualifier
注解,你可以明确指定哪个数据源是默认的,以及在需要时引用特定的数据源。 - 事务管理器与数据源绑定:每个
DataSource
可以对应一个DataSourceTransactionManager
。在 Service 层方法上,可以通过@Transactional(transactionManager = "xxx")
来指定使用哪个数据源的事务管理器。 - JTA 分布式事务:对于跨多个独立数据库的事务(即 XA 事务),Spring 可以集成 JTA 事务管理器,如 Atomikos、Narayana 等。JTA 负责协调多个资源管理器(如不同的数据库)之间的事务提交或回滚,实现两阶段提交(Two-Phase Commit, 2PC)。
- 微服务推荐方案:传统 XA 分布式事务虽然能保证强一致性,但通常配置复杂、性能较差(因为 2PC 协议的阻塞特性),并且在微服务架构下表现不佳。因此,在微服务场景中,更推荐使用柔性事务方案,如 Seata 或 TCC(Try-Confirm-Cancel)模式。
多数据源配置示例(以 Spring Java Config 为例)
|
|
使用注解指定事务管理器
|
|
分布式事务方案(XA vs Seata 等)
-
传统 XA 模式:
- 实现:通常借助 Atomikos、Bitronix、Narayana 等 JTA 事务管理器来实现。
- 原理:依赖底层数据库支持 XA 协议,通过两阶段提交(2PC)协议保证强一致性。
- 特点:配置复杂、性能相对较差(因为涉及多个协调和锁定阶段),但能保证强一致性。适用于对数据一致性要求极高、跨多个独立数据库的传统单体应用或少数场景。
-
推荐替代方案(柔性事务):在微服务架构和高并发场景下,为了追求更好的性能和可用性,通常会采用柔性事务方案,牺牲部分隔离性以换取性能和扩展性:
-
Seata:一款开源的分布式事务解决方案,支持 AT(自动事务)、TCC(Try-Confirm-Cancel)、SAGA(长事务)和 XA 模式。它是一个强大的分布式事务协调器,尤其适合微服务架构,能有效解决分布式环境下的数据一致性问题。
-
Seata 支持多种模式,以适应不同的业务场景和对侵入性的要求:
-
AT 模式(Automatic Transaction):
- 推荐程度:通常是首选模式,因为它对业务代码的侵入性最小。
- 原理:基于两阶段提交(2PC)的优化,由 Seata 自动进行事务的注册、提交和回滚。它通过解析业务 SQL,自动生成回滚 SQL,并锁定资源,以确保数据一致性。
- 优点:对业务代码改动量小,易于接入。
- 缺点:仍涉及一定程度的资源锁定,在极端高并发下可能存在性能瓶颈,且对某些复杂 SQL 或存储过程支持有限。
-
TCC 模式(Try-Confirm-Cancel):
- 推荐程度:适用于对一致性要求非常高,且业务逻辑可以清晰划分为 Try、Confirm、Cancel 三个阶段的场景。
- 原理:需要业务方手动实现 Try(资源预留)、Confirm(确认提交)、Cancel(回滚补偿)三个阶段的逻辑。
- 优点:不依赖于数据库的事务,性能通常更高,且没有资源长期锁定。
- 缺点:对业务代码侵入性最强,开发成本较高。
-
SAGA 模式:
- 推荐程度:适用于长事务、补偿机制复杂、不需要强一致性(最终一致性即可)的业务流程。
- 原理:将一个分布式事务分解为多个本地事务,每个本地事务都有一个对应的补偿操作。当某个本地事务失败时,通过执行之前已成功事务的补偿操作来回滚整个业务流程。
- 优点:无需锁定资源,性能高,支持异步处理。
- 缺点:最终一致性,补偿逻辑复杂,需要业务方设计和实现补偿方案。
-
XA 模式:
- 推荐程度:Seata 也支持 XA 模式,但其本质与传统的 XA 事务管理器相同,通常不作为微服务下的主要推荐。
-
AT 模式示例代码(基于 Spring Cloud + Seata)
AT 模式的核心是通过 Seata 代理数据源自动生成 undo/redo 日志,无需手动编写补偿逻辑,对业务代码侵入性极低。
-
业务场景 模拟电商下单流程:创建订单(订单服务)→ 扣减库存(库存服务),两个服务跨库操作,通过 AT 模式保证一致性。
-
订单服务代码
|
|
- 库存服务代码
|
|
- 核心配置(application.yml)
AT 模式关键点:
- 仅需在发起分布式事务的方法上添加 @GlobalTransactional 注解,无其他业务侵入。
- Seata 会自动拦截 SQL,生成 undo 日志(用于回滚),并通过 TC(事务协调者)协调两阶段提交。
TCC 模式示例代码(基于 Spring Cloud + Seata)
TCC 模式需要手动实现 Try(资源检查 + 预留)、Confirm(确认提交)、Cancel(补偿回滚)三个阶段,侵入业务代码但性能更高。
-
业务场景 同样是电商下单,但要求更灵活的资源控制(如预留库存避免超卖),使用 TCC 模式。
-
订单服务 TCC 接口定义
|
|
- 订单服务 TCC 实现
|
|
- 库存服务 TCC 实现(核心部分)
|
|
|
|
总结
Spring 框架是 Java 生态的基石,必知必会。
文章作者 会写代码的小郎中
上次更新 2025-07-04
许可协议 CC BY-NC-ND 4.0