以下是在 Spring Boot 中整合 Seata Saga 模式、PostgreSQL 和 MyBatis 的完整配置和示例:
1. 项目结构
src/
├── main/
│ ├── java/
│ │ └── com/example/
│ │ ├── saga/ # Saga 状态机定义
│ │ │ └── OrderSaga.java
│ │ ├── service/ # 服务层
│ │ │ ├── InventoryService.java
│ │ │ └── OrderService.java
│ │ ├── mapper/ # MyBatis Mapper接口
│ │ │ ├── InventoryMapper.java
│ │ │ └── OrderMapper.java
│ │ ├── model/ # 实体类
│ │ │ ├── Inventory.java
│ │ │ └── Order.java
│ │ └── Application.java # 启动类
│ └── resources/
│ ├── statemachine/ # Saga 状态机配置
│ │ └── order_saga.json
│ ├── mapper/ # MyBatis XML
│ │ ├── InventoryMapper.xml
│ │ └── OrderMapper.xml
│ ├── application.yml # 配置文件
│ └── seata.conf # Seata 配置(可选)
2. 依赖配置(pom.xml)
<dependencies>
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- PostgreSQL 驱动 -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.6.0</version>
</dependency>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<!-- Seata Saga -->
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.5.2</version>
</dependency>
</dependencies>
3. Seata Server 配置
-
下载并启动 Seata Server:
-
从 Seata GitHub Release 下载并解压。
-
修改
conf/registry.conf,配置注册中心(如 Nacos)和配置中心:registry { type = "nacos" nacos { serverAddr = "localhost:8848" namespace = "" cluster = "default" } } config { type = "nacos" nacos { serverAddr = "localhost:8848" namespace = "" group = "SEATA_GROUP" } } -
启动 Seata Server:
sh bin/seata-server.sh -p 8091 -h 0.0.0.0
-
4. Spring Boot 应用配置
(1) application.yml
spring:
application:
name: order-service
datasource:
url: jdbc:postgresql://localhost:5432/order_db
username: postgres
password: 123456
driver-class-name: org.postgresql.Driver
# Seata 配置
seata:
enabled: true
application-id: order-service
tx-service-group: my-tx-group
service:
vgroup-mapping:
my-tx-group: default
grouplist:
default: 127.0.0.1:8091
registry:
type: nacos
nacos:
server-addr: localhost:8848
config:
type: nacos
5. Saga 状态机定义
(1) 状态机 JSON 配置(statemachine/order_saga.json)
{
"Name": "createOrderSaga",
"Comment": "创建订单的 Saga 流程",
"StartState": "CreateOrder",
"States": {
"CreateOrder": {
"ServiceName": "orderService",
"ServiceMethod": "createOrder",
"CompensateState": "CompensateCreateOrder",
"Next": "DeductInventory"
},
"DeductInventory": {
"ServiceName": "inventoryService",
"ServiceMethod": "deductInventory",
"CompensateState": "CompensateDeductInventory",
"IsEnd": true
},
"CompensateCreateOrder": {
"ServiceName": "orderService",
"ServiceMethod": "compensateCreateOrder",
"IsEnd": true
},
"CompensateDeductInventory": {
"ServiceName": "inventoryService",
"ServiceMethod": "compensateDeductInventory",
"IsEnd": true
}
}
}
(2) 加载状态机配置
@Configuration
public class SagaConfig {
@Bean
public StateMachineEngine stateMachineEngine(SeataSagaClient seataSagaClient) {
return new StateMachineEngine(seataSagaClient)
.withStateMachineRepository(new ResourceStateMachineRepository())
.addStateMachineConfig("classpath:statemachine/order_saga.json");
}
}
6. 服务层实现
(1) 订单服务(正向操作 + 补偿)
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@SagaStart(timeoutSeconds = 60) // 启动 Saga 事务
public void createOrder(Order order) {
orderMapper.createOrder(order);
System.out.println("订单创建成功:" + order.getId());
}
public void compensateCreateOrder(Order order) {
orderMapper.deleteOrder(order.getId());
System.out.println("补偿:订单已删除:" + order.getId());
}
}
(2) 库存服务(正向操作 + 补偿)
@Service
public class InventoryService {
@Autowired
private InventoryMapper inventoryMapper;
public void deductInventory(Long productId, Integer count) {
inventoryMapper.deductInventory(productId, count);
System.out.println("库存扣减成功:" + productId + ", 数量:" + count);
}
public void compensateDeductInventory(Long productId, Integer count) {
inventoryMapper.addInventory(productId, count);
System.out.println("补偿:库存已恢复:" + productId + ", 数量:" + count);
}
}
7. MyBatis Mapper 与实体类
(1) 订单实体类 Order.java
public class Order {
private Long id;
private Long productId;
private Integer count;
private String status;
// Getters/Setters
}
(2) 订单 Mapper 接口 OrderMapper.java
@Mapper
public interface OrderMapper {
@Insert("INSERT INTO orders(product_id, count, status) VALUES(#{productId}, #{count}, 'CREATED')")
@Options(useGeneratedKeys = true, keyProperty = "id")
void createOrder(Order order);
@Delete("DELETE FROM orders WHERE id = #{id}")
void deleteOrder(Long id);
}
(3) 库存实体类 Inventory.java
public class Inventory {
private Long id;
private Long productId;
private Integer total;
// Getters/Setters
}
(4) 库存 Mapper 接口 InventoryMapper.java
@Mapper
public interface InventoryMapper {
@Update("UPDATE inventory SET total = total - #{count} WHERE product_id = #{productId}")
void deductInventory(@Param("productId") Long productId, @Param("count") Integer count);
@Update("UPDATE inventory SET total = total + #{count} WHERE product_id = #{productId}")
void addInventory(@Param("productId") Long productId, @Param("count") Integer count);
}
8. PostgreSQL 表结构
(1) 订单表 orders
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
product_id BIGINT NOT NULL,
count INT NOT NULL,
status VARCHAR(20)
);
(2) 库存表 inventory
CREATE TABLE inventory (
id SERIAL PRIMARY KEY,
product_id BIGINT UNIQUE NOT NULL,
total INT NOT NULL
);
9. 测试与验证
(1) 正常流程测试
@SpringBootTest
public class OrderServiceTest {
@Autowired
private OrderService orderService;
@Test
public void testSagaSuccess() {
Order order = new Order();
order.setProductId(1001L);
order.setCount(10);
orderService.createOrder(order); // 触发 Saga
// 验证订单和库存均更新成功
}
}
(2) 补偿流程测试
@SpringBootTest
public class OrderServiceTest {
@Autowired
private OrderService orderService;
@Test
public void testSagaRollback() {
Order order = new Order();
order.setProductId(1001L);
order.setCount(10);
try {
orderService.createOrder(order);
throw new RuntimeException("模拟库存扣减失败"); // 触发补偿
} catch (Exception e) {
// 验证订单被删除,库存恢复
}
}
}
10. 关键注意事项
-
幂等性处理:
- 确保补偿操作可重复执行(如删除订单前检查是否存在)。
- 在数据库操作中增加版本号或状态字段。
-
空回滚处理:
- 若正向操作未执行但补偿被触发,需跳过无效操作。
-
状态机配置:
- 确保 JSON 文件路径正确,状态名称与代码一致。
- 使用
@SagaStart注解触发事务。
总结
通过以上步骤,Spring Boot 应用能够:
- 实现 Saga 事务:通过状态机定义长事务流程。
- 整合 PostgreSQL 和 MyBatis:确保数据操作正确。
- 自动补偿机制:在异常情况下回滚已执行操作。
此方案适用于需要最终一致性的分布式系统,尤其在微服务架构中处理复杂业务流程时表现出色。