# SpringBoot集成Seata企业级实战教程

本教程基于企业级生产标准从零搭建SpringBoot+Seata分布式事务项目,核心适配Seata捐赠Apache后的官方规范,重点讲解Seata file模式(轻量、无中间件依赖、中小规模部署首选),Spring Cloud版本统一为2025.0.0(Spring官方最新稳定版,完美适配SpringBoot3.5.10)。所有代码、配置均经过本地实测验证,项目架构遵循「父工程+多子模块」的企业级微服务规范,全程覆盖环境搭建、配置开发、业务编码、测试验证、服务端部署全流程,可直接复用至生产环境。

# 1. 开发环境与版本选型

基础开发环境

组件 版本/规格 选型说明
IDEA 2023 Ultimate 企业级开发工具,支持多模块、远程调试等高级功能
JDK 17 SpringBoot 3.x强制要求,Jakarta EE规范适配基础
Maven 3.9.4 依赖管理工具,适配JDK17,避免版本冲突
MySQL 8.0+ 企业级主流数据库,Seata服务端/业务端均适配,支持undo_log回滚日志存储
注册中心 Eureka 2025.0.0 Spring Cloud 2025.0.0官方适配版,局域网部署带鉴权(仅业务服务注册)
持久层框架 MyBatis-Plus 3.5.5 适配JDK17+SpringBoot3.5.10,简化CRUD开发
远程调用 OpenFeign 2025.0.0 Spring Cloud 2025.0.0核心组件,与Eureka无缝整合
数据源 Druid 1.2.20 企业级数据源,替代默认Hikari,支持监控、连接池调优

核心组件版本选型

SpringBoot 3.x基于Jakarta EE规范,Seata捐赠Apache后完成标准化升级,Spring Cloud 2025.0.0为Spring官方最新稳定版,以下版本组合经企业级实测无兼容性问题:

组件 版本 核心适配说明
SpringBoot 3.5.10 核心框架,JDK17+,Jakarta EE规范实现
Spring Cloud 2025.0.0 微服务生态核心,适配SpringBoot3.5.10,Eureka/OpenFeign均基于此版本
Seata 2.5.0 Apache官方稳定版,Group ID改为org.apache.seata,原生适配Jakarta EE,无后缀
Eureka 2025.0.0 Spring Cloud 2025.0.0内置组件,局域网部署带鉴权
OpenFeign 2025.0.0 Spring Cloud 2025.0.0标配,远程调用核心
MySQL-Connector 8.0.33 适配MySQL8.0+JDK17,解决时区、SSL等企业级常见问题

Seata file模式核心说明

Seata file模式是轻量级部署方案,无需依赖Nacos/Eureka等注册/配置中心,客户端直接通过本地文件配置Seata Server地址,适合中小规模集群、测试环境或无中间件依赖的场景,核心特性:

  1. 无中间件依赖:部署成本低,仅需配置Seata Server IP+端口;
  2. 配置简化:通过application.yml或自定义file.conf配置核心参数;
  3. 兼容性强:与Seata 2.5.0+Spring Cloud 2025.0.0完全适配;
  4. 核心不变:仍需创建undo_log回滚日志表,保证事务一致性。

# 2. 项目架构设计

整体架构

采用「父工程+子模块」的职责分离架构,所有模块包名统一为cc.hep,删除test目录(全局跳过测试),符合企业级代码规范:

SpringBoot-Seata(父工程:依赖管理、全局配置)
├── hep-eureka-server(Eureka注册中心:局域网+鉴权)
├── hep-order-service(订单服务:分布式事务发起方,@GlobalTransactional)
└── hep-account-service(账户服务:分布式事务参与方,仅本地@Transactional)
1
2
3
4

核心设计思路

  1. 父工程:仅做依赖版本管理、全局配置(跳过测试、Spring Cloud 2025.0.0依赖管理),无业务代码;
  2. hep-eureka-server:独立注册中心,仅为业务服务提供注册/发现能力(Seata Client不通过Eureka发现Seata Server);
  3. hep-order-service:事务发起方,创建订单+远程调用账户服务扣减余额,添加Seata全局事务注解;
  4. hep-account-service:事务参与方,处理余额扣减,仅需本地事务注解,Seata自动纳入全局事务;
  5. Seata模式选型:file模式(客户端直接通过本地文件配置Seata Server地址,部署更轻量)。

# 3. 父工程搭建

# 3.1 新建父工程

004

# 3.2 父工程POM.xml

核心调整:Spring Cloud版本改为2025.0.0,Seata依赖为Apache官方版,全局跳过测试,统一管理所有组件版本:

<?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 https://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>3.5.10</version>
        <relativePath/>
    </parent>

    <groupId>cc.hep</groupId>
    <artifactId>SpringBoot-Seata</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>SpringBoot-Seata</name>
    <description>SpringBoot3.5.10集成Seata2.5.0(file模式)-Spring Cloud 2025.0.0</description>
    <packaging>pom</packaging>

    <!-- 核心版本配置:Spring Cloud 2025.0.0 + Apache Seata 2.5.0 -->
    <properties>
        <java.version>17</java.version>
        <spring-cloud.version>2025.0.0</spring-cloud.version> <!-- 2025稳定版 -->
        <seata.version>2.5.0</seata.version>
        <mybatis-plus.version>3.5.5</mybatis-plus.version>
        <mysql.version>8.0.33</mysql.version>
        <druid.version>1.2.20</druid.version>
        <lombok.version>1.18.30</lombok.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!-- Spring Cloud 2025.0.0依赖管理(核心) -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- Apache Seata 2.5.0依赖管理 -->
            <dependency>
                <groupId>org.apache.seata</groupId>
                <artifactId>seata-spring-boot-starter</artifactId>
                <version>${seata.version}</version>
            </dependency>
            <!-- 其余组件版本管理 -->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>${mybatis-plus.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>${druid.version}</version>
            </dependency>
            <dependency>
                <groupId>com.mysql</groupId>
                <artifactId>mysql-connector-j</artifactId>
                <version>${mysql.version}</version>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
                <optional>true</optional>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <!-- 子模块共享依赖 + 日志冲突解决 -->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- Hibernate Validator:解决Bean Validation警告 -->
        <dependency>
            <groupId>org.hibernate.validator</groupId>
            <artifactId>hibernate-validator</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- SpringBoot打包插件 -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
            <!-- 全局跳过测试 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.2.2</version>
                <configuration>
                    <skipTests>true</skipTests>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <!-- 子模块声明 -->
    <modules>
        <module>hep-eureka-server</module>
        <module>hep-order-service</module>
        <module>hep-account-service</module>
    </modules>
</project>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130

# 4. 注册中心搭建

# 4.1 子模块创建

右键父工程 → New → Module → Spring Initializr,配置:

  • Name:hep-eureka-server,Artifact:hep-eureka-server;
  • Java Version:17,Spring Boot:3.5.10;
  • 不选择依赖(手动引入)。

# 4.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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>cc.hep</groupId>
        <artifactId>SpringBoot-Seata</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>

    <groupId>cc.hep</groupId>
    <artifactId>hep-eureka-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>hep-eureka-server</name>
    <description>Eureka注册中心(Spring Cloud 2025.0.0)-局域网+鉴权</description>
    <packaging>jar</packaging>

    <dependencies>
        <!-- Eureka Server(Spring Cloud 2025.0.0) -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <!-- Spring Security:鉴权 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

# 4.3 启动类与鉴权配置

启动类HepEurekaServerApplication.java

package cc.hep;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer // 开启Eureka Server
public class HepEurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(HepEurekaServerApplication.class, args);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

鉴权配置WebSecurityConfig.java

package cc.hep.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class WebSecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                .csrf(csrf -> csrf.disable()) // 关闭CSRF(内部服务注册)
                .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
                .httpBasic(httpBasic -> {}); // 启用HTTP Basic认证
        return http.build();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 4.4 核心配置文件

application.yml

server:
  port: 8761 # Eureka默认端口

spring:
  application:
    name: hep-eureka-server
  security:
    user:
      name: hepeureka # 鉴权用户名
      password: eureka123456 # 鉴权密码

eureka:
  instance:
    hostname: 192.168.31.115 # 局域网IP,替代localhost
  client:
    register-with-eureka: false # 单机版不注册自身
    fetch-registry: false # 单机版不拉取服务列表
    service-url:
      defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@${eureka.instance.hostname}:${server.port}/eureka/
  server:
    enable-self-preservation: true # 开启自我保护(生产必开)
    renewal-percent-threshold: 0.85
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 4.5 启动验证

运行启动类,访问http://192.168.31.115:8761/,输入账号hepeureka/eureka123456,页面显示no instances available即为成功。

# 5. 业务服务搭建

# 5.1 数据库初始化

分布式事务核心是跨库操作,需创建订单库、账户库,并分别添加Seata回滚日志表undo_log(file模式下仍需该表,核心不可省略)。

# 5.1.1 订单库hep_seata_order

CREATE DATABASE IF NOT EXISTS hep_seata_order DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE hep_seata_order;

-- 订单表
CREATE TABLE tb_order (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    order_no VARCHAR(32) NOT NULL UNIQUE COMMENT '订单编号',
    user_id BIGINT NOT NULL COMMENT '用户ID',
    product_id BIGINT NOT NULL COMMENT '商品ID',
    count INT NOT NULL DEFAULT 0 COMMENT '购买数量',
    amount DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '订单金额',
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
    update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_user_id (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- Seata回滚日志表(file模式下核心)
CREATE TABLE undo_log (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    branch_id BIGINT NOT NULL,
    xid VARCHAR(128) NOT NULL,
    context VARCHAR(128) NOT NULL,
    rollback_info LONGBLOB NOT NULL,
    log_status INT NOT NULL,
    log_created DATETIME NOT NULL,
    log_modified DATETIME NOT NULL,
    ext VARCHAR(128) DEFAULT NULL,
    UNIQUE KEY ux_undo_log (xid, branch_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 5.1.2 账户库hep_seata_account

CREATE DATABASE IF NOT EXISTS hep_seata_account DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE hep_seata_account;

-- 账户表
CREATE TABLE tb_account (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    user_id BIGINT NOT NULL UNIQUE COMMENT '用户ID',
    balance DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '可用余额',
    freeze_balance DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '冻结余额',
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
    update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 初始化测试数据:用户1,余额1000元
INSERT INTO tb_account (user_id, balance) VALUES (1, 1000.00);

-- Seata回滚日志表(与订单库一致)
CREATE TABLE undo_log (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    branch_id BIGINT NOT NULL,
    xid VARCHAR(128) NOT NULL,
    context VARCHAR(128) NOT NULL,
    rollback_info LONGBLOB NOT NULL,
    log_status INT NOT NULL,
    log_created DATETIME NOT NULL,
    log_modified DATETIME NOT NULL,
    ext VARCHAR(128) DEFAULT NULL,
    UNIQUE KEY ux_undo_log (xid, branch_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 5.2 订单服务:事务发起方

# 5.2.1 子模块创建

与Eureka模块创建流程一致,Name:hep-order-service,Artifact:hep-order-service。

# 5.2.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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>cc.hep</groupId>
        <artifactId>SpringBoot-Seata</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>

    <groupId>cc.hep</groupId>
    <artifactId>hep-order-service</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>hep-order-service</name>
    <description>订单服务-分布式事务发起方(Seata file模式)</description>
    <packaging>jar</packaging>

    <dependencies>
        <!-- SpringWeb:接口能力 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- Eureka Client(Spring Cloud 2025.0.0):服务注册发现 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- OpenFeign(Spring Cloud 2025.0.0):远程调用账户服务 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!-- MyBatis-Plus:持久层 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-jsqlparser</artifactId>
        </dependency>
        <!-- MySQL驱动 -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- Druid数据源 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
        </dependency>
        <!-- Apache Seata 2.5.0(核心):排除Eureka发现模块(file模式无需) -->
        <dependency>
            <groupId>org.apache.seata</groupId>
            <artifactId>seata-spring-boot-starter</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.seata</groupId>
                    <artifactId>seata-discovery-eureka</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>io.netty</groupId>
                    <artifactId>netty-all</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

# 5.2.3 Seata核心配置:数据源代理

Seata实现分布式事务的核心前提是通过DataSourceProxy拦截SQL,file模式下该配置不可省略,包名已更新为Apache官方版,在cc.hep.config下创建SeataConfig.java:

package cc.hep.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.apache.seata.rm.datasource.DataSourceProxy; // Apache Seata包名
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import javax.sql.DataSource;

/**
 * Seata file模式核心配置:数据源代理
 * @Primary:优先注入,覆盖默认数据源,保证Seata拦截所有SQL
 */
@Configuration
public class SeataConfig {
    // 配置Druid原生数据源(从application.yml读取配置)
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druidDataSource() {
        return new DruidDataSource();
    }

    // 封装为Seata数据源代理,作为项目主数据源
    @Bean
    @Primary
    public DataSource dataSourceProxy(DataSource druidDataSource) {
        return new DataSourceProxy(druidDataSource);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# 5.2.4 启动类配置

添加MyBatis-Plus扫描、Feign开启注解,file模式无额外Seata注解,创建HepOrderServiceApplication.java:

package cc.hep;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.transaction.annotation.EnableTransactionManagement;

/**
 * 订单服务启动类
 * MapperScan:扫描Mapper接口
 * EnableFeignClients:开启OpenFeign远程调用
 */
@SpringBootApplication
@MapperScan("cc.hep.mapper")
@EnableFeignClients("cc.hep.feign")
@EnableTransactionManagement
public class HepOrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(HepOrderServiceApplication.class, args);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 5.2.5 核心配置文件

重点:Seata file模式配置,registry.type和config.type均设为file,通过grouplist直接指定Seata Server地址(无需中间件),Spring Cloud 2025.0.0的Eureka配置适配application.yml:

server:
  port: 8081 # 订单服务端口

spring:
  application:
    name: hep-order-service # Eureka注册名(hep前缀)
  # 数据库配置:订单库
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.31.27:3306/hep_seata_order?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
    username: heproot
    password: root123456
    # Druid连接池调优(企业级)
    druid:
      initial-size: 5
      max-active: 20
      min-idle: 5

# Eureka Client配置(Spring Cloud 2025.0.0):局域网+鉴权
eureka:
  client:
    service-url:
      defaultZone: http://hepeureka:eureka123456@192.168.31.115:8761/eureka/
    fetch-registry: true
    register-with-eureka: true
  instance:
    prefer-ip-address: true # 优先IP注册
    lease-expiration-duration-in-seconds: 90
    lease-renewal-interval-in-seconds: 30

# MyBatis-Plus配置
mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true # 下划线转驼峰
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开发环境打印SQL
  global-config:
    db-config:
      id-type: AUTO # 主键自增
  mapper-locations: classpath:mapper/*.xml

# ========== Seata file模式核心配置(重点) ==========
seata:
  enabled: true # 开启Seata
  application-id: ${spring.application.name} # 应用ID,与服务名一致
  tx-service-group: default_tx_group # 事务组,需与Seata Server一致
  # 注册中心:file模式(无需Eureka/Nacos)
  registry:
    type: file
    file:
      name: file.conf # 可省略,默认读取内置配置
  # 配置中心:file模式(无需远程配置中心)
  config:
    type: file
    file:
      name: file.conf # 可省略,默认读取内置配置
  # 服务端地址:file模式核心,直接指定Seata Server IP+端口
  service:
    vgroup-mapping:
      default_tx_group: default # 事务组映射,需与Seata Server一致
    grouplist:
      default: 192.168.31.114:8091 # Seata Server地址(核心,不可错)
    enable-degrade: false
    disable-global-transaction: false
  # 客户端配置(企业级)
  client:
    rm:
      async-commit-buffer-limit: 10000
      lock:
        retry-times: 30
        retry-interval: 10
    tm:
      commit-retry-count: 5
      rollback-retry-count: 5
    undo:
      log-table: undo_log # 回滚日志表名,与数据库一致
      log-serialization: jackson
  data-source-proxy-mode: AT # Seata默认模式,无侵入
  enable-auto-data-source-proxy: true # 自动数据源代理(必开)

# 日志配置:Seata日志级别(便于调试)
logging:
  level:
    root: info
    cc.hep: info
    org.apache.seata: debug # Apache Seata日志
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88

# 5.2.6 业务代码开发

实体类:Order.java(映射订单表)

package cc.hep.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;

import java.math.BigDecimal;
import java.time.LocalDateTime;

@Data
@Accessors(chain = true)
@TableName("tb_order")
public class Order {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String orderNo; // 订单编号(UUID)
    private Long userId; // 用户ID
    private Long productId; // 商品ID
    private Integer count; // 购买数量
    private BigDecimal amount; // 订单金额
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

Mapper接口:OrderMapper.java

package cc.hep.mapper;

import cc.hep.entity.Order;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;

/**
 * 订单Mapper:继承MyBatis-Plus BaseMapper,无需手动写SQL
 */
@Mapper
public interface OrderMapper extends BaseMapper<Order> {
}
1
2
3
4
5
6
7
8
9
10
11
12

Feign远程调用:AccountFeignClient.java(调用账户服务)

package cc.hep.feign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.math.BigDecimal;

/**
 * Feign客户端:调用账户服务(hep-account-service)
 * value:Eureka注册名,必须与账户服务一致
 */
@FeignClient(value = "hep-account-service")
public interface AccountFeignClient {
    /**
     * 远程扣减余额接口:路径、参数与账户服务Controller完全一致
     */
    @PostMapping("/account/deduct")
    Boolean deduct(@RequestParam("userId") Long userId, @RequestParam("amount") BigDecimal amount);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

业务层接口:OrderService.java

package cc.hep.service;

import cc.hep.entity.Order;
import com.baomidou.mybatisplus.extension.service.IService;

/**
 * 订单服务接口:继承MyBatis-Plus IService
 */
public interface OrderService extends IService<Order> {
    /**
     * 创建订单:本地创单 + 远程扣减余额(Seata全局事务)
     */
    Boolean createOrder(Order order);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

业务层实现:OrderServiceImpl.java(全局事务发起方核心)

添加Apache Seata的@GlobalTransactional注解(仅发起方需要),file模式下注解使用无差异,异常时自动触发全局回滚:

package cc.hep.service.impl;

import cc.hep.entity.Order;
import cc.hep.feign.AccountFeignClient;
import cc.hep.mapper.OrderMapper;
import cc.hep.service.OrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.seata.spring.annotation.GlobalTransactional; // Apache Seata注解
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.UUID;

/**
 * 订单服务实现:分布式事务发起方
 * @GlobalTransactional:Seata全局事务核心注解,file模式下使用无差异
 */
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private AccountFeignClient accountFeignClient;

    /**
     * 全局事务方法:name-事务名,rollbackFor-指定所有异常回滚
     */
    @Override
    @GlobalTransactional(name = "create-order-hep-seata", rollbackFor = Exception.class)
    public Boolean createOrder(Order order) {
        // 1. 生成UUID订单编号
        String orderNo = UUID.randomUUID().toString().replace("-", "");
        order.setOrderNo(orderNo);
        // 2. 本地创建订单
        int insert = orderMapper.insert(order);
        if (insert <= 0) {
            throw new RuntimeException("本地创建订单失败,触发全局回滚");
        }
        // 3. 远程调用账户服务扣减余额
        Boolean deductResult = accountFeignClient.deduct(order.getUserId(), order.getAmount());
        if (!deductResult) {
            throw new RuntimeException("远程扣减余额失败,触发全局回滚");
        }
        // 测试回滚:手动抛出异常(注释则正常提交)
        // throw new RuntimeException("测试异常,触发Seata全局回滚");
        return true;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

控制层:OrderController.java(提供下单接口)

package cc.hep.controller;

import cc.hep.entity.Order;
import cc.hep.service.OrderService;
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;

/**
 * 订单控制层:提供下单接口(RESTful)
 */
@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private OrderService orderService;

    /**
     * 下单接口:JSON请求体,企业级参数校验
     */
    @PostMapping("/create")
    public String createOrder(@RequestBody Order order) {
        try {
            // 必传参数校验
            if (order.getUserId() == null || order.getProductId() == null || order.getCount() == null || order.getAmount() == null) {
                return "下单失败:用户ID、商品ID、数量、金额为必传参数";
            }
            Boolean result = orderService.createOrder(order);
            return result ? "下单成功,Seata全局事务提交" : "下单失败";
        } catch (Exception e) {
            return "下单失败:" + e.getMessage();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

# 5.3 账户服务:分布式事务参与方

# 5.3.1 完整POM.xml配置

与订单服务一致,移除OpenFeign依赖(不发起远程调用),保留Seata file模式核心配置,排除Eureka发现模块:

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>cc.hep</groupId>
        <artifactId>SpringBoot-Seata</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>

    <groupId>cc.hep</groupId>
    <artifactId>hep-account-service</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>hep-account-service</name>
    <description>账户服务-分布式事务参与方(Seata file模式)</description>
    <packaging>jar</packaging>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-jsqlparser</artifactId>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
        </dependency>
        <!-- Apache Seata 2.5.0:与订单服务一致,排除Eureka发现模块 -->
        <dependency>
            <groupId>org.apache.seata</groupId>
            <artifactId>seata-spring-boot-starter</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.seata</groupId>
                    <artifactId>seata-discovery-eureka</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>io.netty</groupId>
                    <artifactId>netty-all</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

# 5.3.2 核心配置

  1. Seata数据源代理:直接复制订单服务的SeataConfig.java(包名cc.hep.config),file模式下参与方也必须配置;
  2. 启动类:创建HepAccountServiceApplication.java,移除Feign注解,其余与订单服务一致:
package cc.hep;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@SpringBootApplication
@MapperScan("cc.hep.mapper")
@EnableTransactionManagement
public class HepAccountServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(HepAccountServiceApplication.class, args);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 5.3.3 核心配置文件

仅修改端口、数据库(账户库),Seata file模式配置与订单服务完全一致(事务组、grouplist必须匹配)application.yml为:

server:
  port: 8082 # 账户服务端口,与订单服务区分

spring:
  application:
    name: hep-account-service
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.31.27:3306/hep_seata_account?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
    username: heproot
    password: root123456
    druid:
      initial-size: 5
      max-active: 20
      min-idle: 5

# Eureka + MyBatis-Plus 配置:与订单服务完全一致
eureka:
  client:
    service-url:
      defaultZone: http://hepeureka:eureka123456@192.168.31.115:8761/eureka/
    fetch-registry: true
    register-with-eureka: true
  instance:
    prefer-ip-address: true
    lease-expiration-duration-in-seconds: 90
    lease-renewal-interval-in-seconds: 30

mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      id-type: AUTO
  mapper-locations: classpath:mapper/*.xml

# Seata file模式配置:与订单服务完全一致(核心,不可改)
seata:
  enabled: true
  application-id: ${spring.application.name}
  tx-service-group: default_tx_group
  registry:
    type: file
  config:
    type: file
  service:
    vgroup-mapping:
      default_tx_group: default
    grouplist:
      default: 192.168.31.114:8091
    enable-degrade: false
    disable-global-transaction: false
  client:
    rm:
      async-commit-buffer-limit: 10000
      lock:
        retry-times: 30
        retry-interval: 10
    tm:
      commit-retry-count: 5
      rollback-retry-count: 5
    undo:
      log-table: undo_log
      log-serialization: jackson
  data-source-proxy-mode: AT
  enable-auto-data-source-proxy: true

logging:
  level:
    root: info
    cc.hep: info
    org.apache.seata: debug
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76

# 5.3.4 业务代码开发

实体类:Account.java(映射账户表)

package cc.hep.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;

import java.math.BigDecimal;
import java.time.LocalDateTime;

@Data
@Accessors(chain = true)
@TableName("tb_account")
public class Account {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long userId; // 唯一用户ID
    private BigDecimal balance; // 可用余额
    private BigDecimal freezeBalance; // 冻结余额
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

Mapper接口:AccountMapper.java(自定义扣减余额SQL,企业级乐观锁)

package cc.hep.mapper;

import cc.hep.entity.Account;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;

import java.math.BigDecimal;

/**
 * 账户Mapper:自定义扣减余额SQL,保证余额充足才扣减(避免超扣)
 */
@Mapper
public interface AccountMapper extends BaseMapper<Account> {
    /**
     * 扣减余额:WHERE balance >= amount 保证余额充足
     * @return 受影响行数:>0成功,<=0失败
     */
    @Update("UPDATE tb_account SET balance = balance - #{amount} WHERE user_id = #{userId} AND balance >= #{amount}")
    int deductBalance(@Param("userId") Long userId, @Param("amount") BigDecimal amount);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

业务层接口:AccountService.java

package cc.hep.service;

import cc.hep.entity.Account;
import com.baomidou.mybatisplus.extension.service.IService;

import java.math.BigDecimal;

public interface AccountService extends IService<Account> {
    /**
     * 扣减用户余额(本地事务)
     */
    Boolean deduct(Long userId, BigDecimal amount);
}
1
2
3
4
5
6
7
8
9
10
11
12
13

业务层实现:AccountServiceImpl.java(仅本地@Transactional)

参与方无需任何Seata注解,仅需Spring本地事务保证原子性,Seata通过数据源代理自动将其纳入全局事务:

package cc.hep.service.impl;

import cc.hep.mapper.AccountMapper;
import cc.hep.service.AccountService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import cc.hep.entity.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;

/**
 * 账户服务实现:分布式事务参与方
 * 仅添加本地@Transactional,Seata自动纳入全局事务(0侵入)
 */
@Service
public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements AccountService {

    @Autowired
    private AccountMapper accountMapper;

    /**
     * 本地事务方法:所有异常触发回滚,Seata感知后全局回滚
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deduct(Long userId, BigDecimal amount) {
        int update = accountMapper.deductBalance(userId, amount);
        if (update <= 0) {
            throw new RuntimeException("账户余额不足,扣减失败");
        }
        return true;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

控制层:AccountController.java(提供扣减余额接口,供订单服务远程调用)

package cc.hep.controller;

import cc.hep.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.math.BigDecimal;

/**
 * 账户控制层:提供扣减余额接口,路径/参数与Feign客户端完全一致
 */
@RestController
@RequestMapping("/account")
public class AccountController {

    @Autowired
    private AccountService accountService;

    /**
     * 扣减余额接口:与AccountFeignClient匹配,不可修改
     */
    @PostMapping("/deduct")
    public Boolean deduct(@RequestParam("userId") Long userId, @RequestParam("amount") BigDecimal amount) {
        return accountService.deduct(userId, amount);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

Seata file模式核心配置要点

本次教程全程采用Seata file模式,是中小规模项目、测试环境的最优选择,核心配置要点需严格遵循,否则分布式事务失效:

  1. 注册/配置中心均为file:seata.registry.type=file、seata.config.type=file,无需依赖Eureka/Nacos,部署成本极低;
  2. grouplist是核心:通过seata.service.grouplist直接指定Seata Server的IP+端口(默认8091),是客户端与服务端通信的唯一入口;
  3. 事务组必须匹配:seata.tx-service-group和seata.service.vgroup-mapping必须与Seata Server的配置完全一致(默认default_tx_group);
  4. 数据源代理不可省:无论发起方还是参与方,都必须配置DataSourceProxy并添加@Primary注解,保证Seata拦截所有SQL;
  5. 无需传统配置文件:Seata 2.5.0的file模式无需手动创建registry.conf/file.conf,默认读取内置配置,仅需在application.yml中配置核心参数;
  6. undo_log表必创:每个业务数据库都必须创建undo_log回滚日志表,Seata通过该表实现事务回滚,file模式下该表不可省略。

# 6. 项目启动与分布式事务测试

# 6.1 企业级启动顺序

Seata file模式下,启动顺序仍需遵循「基础服务→核心中间件→业务服务」,确保服务间无连接异常:

  1. 第一步:启动hep-eureka-server(注册中心):访问http://192.168.31.115:8761/,登录成功即为就绪;
  2. 第二步:启动Seata Server 2.5.0(分布式事务核心):控制台打印Global transaction service is ready即为启动成功;
  3. 第三步:启动hep-account-service(事务参与方):Eureka控制台看到HEP-ACCOUNT-SERVICE状态为UP;
  4. 第四步:启动hep-order-service(事务发起方):Eureka控制台看到HEP-ORDER-SERVICE状态为UP。

# 6.2 启动成功验证

  1. Eureka验证:访问Eureka控制台,能看到3个服务实例(hep-eureka-server、hep-account-service、hep-order-service),状态均为UP;
  2. Seata验证:业务服务启动日志中无Seata connection failed等报错,打印init client success即为Seata Client初始化成功;
  3. 数据库验证:确认账户库中用户1的初始余额为1000.00元,订单库无数据。

# 6.3 测试工具与接口信息

我这里给出一个成功的截图,一个没有userId等于2的账户,所以会回滚。

007

使用IDEA内置HttpClient/Postman/Curl测试,核心测试接口为订单服务的下单接口,信息如下:

  • 请求方式:POST
  • 请求地址:http://192.168.31.xxx:8081/order/create(xxx为订单服务所在机器IP,本地可填localhost)
  • 请求体(JSON):测试基础数据(用户1,购买2件商品,金额200元)
{
    "userId": 1,
    "productId": 1001,
    "count": 2,
    "amount": 200.00
}
1
2
3
4
5
6

测试场景1:正常无异常,Seata全局事务提交

测试步骤

  1. 确保订单服务OrderServiceImpl中的手动异常代码已注释;
  2. 调用下单接口,传递上述JSON数据;
  3. 查看接口返回、数据库数据、Seata Server日志。

预期结果

  1. 接口返回:下单成功,Seata全局事务提交;
  2. 订单库:hep_seata_order.tb_order新增1条订单记录,订单编号为UUID,字段与请求体一致;
  3. 账户库:hep_seata_account.tb_account中用户1的余额从1000.00元变为800.00元,扣减成功;
  4. Seata日志:服务端打印Global transaction commit successfully, xid=xxx,表示全局事务提交成功;
  5. undo_log表:事务提交后,Seata自动清理回滚日志,表中无残留数据。

测试场景2:手动抛出异常,Seata全局事务回滚

测试步骤

  1. 恢复订单服务OrderServiceImpl中的手动异常代码(throw new RuntimeException("测试异常,触发Seata全局回滚"););
  2. 重启订单服务,再次调用下单接口;
  3. 查看接口返回、数据库数据、Seata Server日志。

预期结果(核心验证一致性)

  1. 接口返回:下单失败:测试异常,触发Seata全局回滚;
  2. 订单库:tb_order无新增记录,创建订单的操作被Seata回滚;
  3. 账户库:用户1的余额仍为800.00元,扣减余额的操作被Seata回滚;
  4. Seata日志:服务端打印Global transaction rollback successfully, xid=xxx,表示全局事务回滚成功;
  5. undo_log表:Seata通过该表记录的SQL执行记录实现回滚,回滚完成后日志被清理。

测试场景3:余额不足,Seata全局事务回滚(生产常见边界场景)

测试步骤

  1. 注释手动异常代码,重启订单服务;
  2. 调用下单接口,将amount改为900.00元(账户当前余额800元,不足扣减);
  3. 查看接口返回、数据库数据。

预期结果

  1. 接口返回:下单失败:账户余额不足,扣减失败;
  2. 订单库:无新增记录,本地创单操作被回滚;
  3. 账户库:余额仍为800.00元,无任何修改;
  4. 核心结论:参与方抛出的异常会被发起方捕获,Seata自动触发全局回滚,保证跨库数据一致性。

# 7. Seata Server 2.5.0 企业级部署

# 7.1 下载Seata Server 2.5.0部署包

Apache Seata官方提供编译后的部署包,无需手动编译,直接解压即可使用:

  • 官方下载地址:Apache Seata 2.5.0 Release (opens new window)
  • 下载文件:seata-server-2.5.0.zip(编译后的部署包,约200MB,Windows/Linux通用)
  • 解压要求:解压到无中文、无空格的目录(例:Windows→D:\seata-server-2.5.0,Linux→/usr/local/seata-server-2.5.0)。

# 7.2 Seata Server核心配置

Seata Server 2.5.0的配置文件为conf/application.yml,file模式下仅需简单配置,无需注册到Eureka/Nacos,核心修改存储模式和事务组,以下为完整可直接使用的配置:

server:
  port: 8091 # Seata Server默认端口,客户端grouplist需匹配

spring:
  application:
    name: seata-server

# ========== Seata Server核心配置 ==========
seata:
  # 服务端节点ID
  server:
    node: 1
    service-port: 8091 # 事务服务端口,与server.port一致
  # 注册中心:file模式(无需中间件)
  registry:
    type: file
  # 配置中心:file模式(无需中间件)
  config:
    type: file
  # 事务组配置:与客户端一致
  service:
    vgroup-mapping:
      default_tx_group: default # 事务组映射,客户端必须匹配
    grouplist:
      default: 127.0.0.1:8091
  # 存储模式:推荐测试用file,生产用db(需初始化数据库)
  store:
    mode: file # file模式:轻量,无需数据库;db模式:支持集群,生产必用
    # file模式存储配置(默认)
    file:
      store-dir: file-store
      max-branch-session-size: 16384
      max-global-session-size: 512
      file-write-buffer-cache-size: 16384
      session-reload-read-size: 100
      flush-disk-mode: async
  # 安全配置(默认)
  security:
    secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
    tokenValidityInMilliseconds: 1800000

# 日志配置
logging:
  config: classpath:logback-spring.xml
  file:
    path: ${user.home}/seata/logs
  level:
    org.apache.seata: info
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

# 7.3 Seata Server启动命令

Windows启动

双击解压目录下的bin/seata-server.bat,控制台无报错且打印Global transaction service is ready即为启动成功。

Linux/Mac启动

# 进入bin目录
cd /usr/local/seata-server-2.5.0/bin
# 后台启动,日志输出到seata-server.log
nohup ./seata-server.sh > seata-server.log 2>&1 &
# 查看启动状态
ps -ef | grep seata-server
1
2
3
4
5
6

本教程的所有配置和代码均为企业级实测可用,基于Apache Seata官方最新规范,是SpringBoot3.x集成Seata分布式事务的最佳实践方案,可直接复用到实际生产项目中。

最近更新: 2026/2/6
 |  Copyright © -Present Kelsen All Rights Reserved. | Author KelsenJing