SpringBoot Seata AT 最佳实践

646 阅读3分钟

以下是在 Spring Boot 中整合 Seata AT 模式、PostgreSQL 和 MyBatis 实现分布式事务的完整配置和示例:


1. 项目结构

src/
├── main/
│   ├── java/
│   │   └── com/example/
│   │       ├── config/            # 配置类
│   │       │   └── SeataConfig.java
│   │       ├── mapper/            # MyBatis Mapper接口
│   │       │   └── OrderMapper.java
│   │       ├── model/             # 实体类
│   │       │   └── Order.java
│   │       ├── service/           # 服务层
│   │       │   └── OrderService.java
│   │       └── Application.java   # 启动类
│   └── resources/
│       ├── mapper/                # MyBatis 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 -->
    <dependency>
        <groupId>io.seata</groupId>
        <artifactId>seata-spring-boot-starter</artifactId>
        <version>1.5.2</version>
    </dependency>
</dependencies>

3. Seata Server 配置

  1. 下载 Seata ServerSeata GitHub Release 下载并解压。

  2. 配置 Seata Serverconf/registry.conf):

    registry {
      type = "nacos"  # 使用 Nacos 作为注册中心
      nacos {
        serverAddr = "localhost:8848"
        namespace = ""
        cluster = "default"
      }
    }
    ​
    config {
      type = "nacos"  # 使用 Nacos 作为配置中心
      nacos {
        serverAddr = "localhost:8848"
        namespace = ""
        group = "SEATA_GROUP"
      }
    }
    
  3. 启动 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  # 事务组名(需与 Seata Server 一致)
  service:
    vgroup-mapping:
      my-tx-group: default       # 事务组映射到 Seata Server 集群
    grouplist:
      default: 127.0.0.1:8091    # Seata Server 地址
  registry:
    type: nacos                  # 注册中心类型
    nacos:
      server-addr: localhost:8848
  config:
    type: nacos                  # 配置中心类型

(2) Seata 数据源代理配置

@Configuration
public class SeataConfig {
​
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource() {
        return new DruidDataSource();
    }
​
    // 代理数据源(关键:Seata 通过代理数据源管理事务)
    @Bean
    public DataSourceProxy dataSourceProxy(DataSource dataSource) {
        return new DataSourceProxy(dataSource);
    }
​
    // MyBatis 使用代理数据源
    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSourceProxy dataSourceProxy) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSourceProxy);
        factoryBean.setMapperLocations(
            new PathMatchingResourcePatternResolver()
                .getResources("classpath:mapper/*.xml")
        );
        return factoryBean.getObject();
    }
}

5. MyBatis Mapper 与实体类

(1) 实体类 Order.java

public class Order {
    private Long id;
    private Long userId;
    private BigDecimal amount;
    // Getters/Setters
}

(2) Mapper 接口 OrderMapper.java

@Mapper
public interface OrderMapper {
    @Insert("INSERT INTO orders(user_id, amount) VALUES(#{userId}, #{amount})")
    void insertOrder(Order order);
}

(3) Mapper XML OrderMapper.xml

<mapper namespace="com.example.mapper.OrderMapper">
    <insert id="insertOrder" parameterType="com.example.model.Order">
        INSERT INTO orders(user_id, amount)
        VALUES(#{userId}, #{amount})
    </insert>
</mapper>

6. 服务层与全局事务

(1) 开启全局事务(@GlobalTransactional

@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;

    // 全局事务注解
    @GlobalTransactional(name = "createOrder", rollbackFor = Exception.class)
    public void createOrder(Order order) {
        orderMapper.insertOrder(order);  // 本地事务
        // 调用其他微服务(如扣减库存服务)
        restTemplate.postForEntity("http://inventory-service/deduct", order.getProductId(), Void.class);
    }
}

(2) 回滚测试

@SpringBootTest
public class OrderServiceTest {

    @Autowired
    private OrderService orderService;

    @Test
    public void testGlobalRollback() {
        Order order = new Order();
        order.setUserId(1L);
        order.setAmount(new BigDecimal("100.00"));
        try {
            orderService.createOrder(order);
        } catch (Exception e) {
            // 验证数据库是否无数据插入
        }
    }
}

7. PostgreSQL 表结构调整

确保数据库表包含 UNDO_LOG(Seata AT 模式需要):

-- 在 PostgreSQL 中创建 UNDO_LOG 表
CREATE TABLE undo_log (
  id SERIAL PRIMARY KEY,
  branch_id BIGINT NOT NULL,
  xid VARCHAR(100) NOT NULL,
  context VARCHAR(128) NOT NULL,
  rollback_info BYTEA NOT NULL,
  log_status INT NOT NULL,
  log_created TIMESTAMP NOT NULL,
  log_modified TIMESTAMP NOT NULL
);

-- 业务表 orders
CREATE TABLE orders (
  id SERIAL PRIMARY KEY,
  user_id BIGINT,
  amount DECIMAL(10,2)
);

8. 注意事项

  1. Seata 对 PostgreSQL 的支持: Seata AT 模式原生支持 MySQL,PostgreSQL 需要手动创建 UNDO_LOG 表,并确保驱动兼容性。
  2. 代理数据源: 必须通过 DataSourceProxy 包装原生数据源,否则事务无法拦截。
  3. Nacos 配置: 确保 Seata Server 和 Spring Boot 应用使用相同的 Nacos 配置中心。
  4. 全局事务 ID(XID)传递: 在微服务间调用时,需通过请求头传递 XID(如使用 SeataRestTemplateInterceptor)。

总结

通过以上步骤,Spring Boot 应用可以:

  1. 集成 Seata AT 模式:通过 @GlobalTransactional 注解管理分布式事务。
  2. 兼容 PostgreSQL:手动创建 UNDO_LOG 表,配置数据源代理。
  3. 结合 MyBatis:通过代理数据源实现 SQL 拦截和回滚日志记录。