How to Set Up Seata Distributed Transactions with Spring Boot, Nacos, and MySQL

This guide walks through configuring Seata’s registry and configuration centers with Nacos, setting up MySQL storage, defining service modules, writing transactional code with @GlobalTransactional, and verifying rollback behavior in a Spring Boot 2.3 microservice project.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
How to Set Up Seata Distributed Transactions with Spring Boot, Nacos, and MySQL

Environment: springboot 2.3.11.RELEASE, spring cloud Hoxton.SR8, spring cloud alibaba 2.2.5.RELEASE, seata 1.3.0.

Prerequisite: Nacos service is installed and running.

Seata Registry and Configuration Center Overview

Configuration Center acts like a "wardrobe" that stores configuration files; Seata clients (TM, RM) and servers (TC) read global transaction switches and session storage mode from it. In principle, Seata’s configuration center is the same as Spring Cloud’s, but it only applies to Seata components.
Registry Center is the "address book" of a microservice architecture, recording service‑address mappings. Services register themselves here and discover others, e.g., Seata clients locate Seata servers. Seata’s registry works like Dubbo or Spring Cloud registries, but it is scoped to Seata itself.

Seata Service Configuration

1. Create config.txt under %SEATA_HOME% with the following content:

service.vgroupMapping.dt-group=default
store.mode=db
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.cj.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&serverTimezone=GMT%2B8
store.db.user=root
store.db.password=xxxxxx
store.db.minConn=5
store.db.maxConn=30
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.queryLimit=100
store.db.lockTable=lock_table
store.db.maxWait=5000

2. Execute the script by downloading it from the provided address.

On Windows, run the shell script with Git Bash:

sh nacos-config.sh -h <nacos_ip> -p <nacos_port> -g <nacos_group> -t <namespace> -u <nacos_user> -w <nacos_password>

3. Initialize the database with the following SQL statements:

CREATE TABLE `undo_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(100) NOT NULL,
  `context` varchar(128) NOT NULL,
  `rollback_info` longblob NOT NULL,
  `log_status` int(11) NOT NULL,
  `log_created` datetime NOT NULL,
  `log_modified` datetime NOT NULL,
  `ext` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `global_table` (
  `xid` VARCHAR(128) NOT NULL,
  `transaction_id` BIGINT,
  `status` TINYINT NOT NULL,
  `application_id` VARCHAR(32),
  `transaction_service_group` VARCHAR(32),
  `transaction_name` VARCHAR(128),
  `timeout` INT,
  `begin_time` BIGINT,
  `application_data` VARCHAR(2000),
  `gmt_create` DATETIME,
  `gmt_modified` DATETIME,
  PRIMARY KEY (`xid`),
  KEY `idx_gmt_modified_status` (`gmt_modified`,`status`),
  KEY `idx_transaction_id` (`transaction_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `branch_table` (
  `branch_id` BIGINT NOT NULL,
  `xid` VARCHAR(128) NOT NULL,
  `transaction_id` BIGINT,
  `resource_group_id` VARCHAR(32),
  `resource_id` VARCHAR(256),
  `branch_type` VARCHAR(8),
  `status` TINYINT,
  `client_id` VARCHAR(64),
  `application_data` VARCHAR(2000),
  `gmt_create` DATETIME(6),
  `gmt_modified` DATETIME(6),
  PRIMARY KEY (`branch_id`),
  KEY `idx_xid` (`xid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `lock_table` (
  `row_key` VARCHAR(128) NOT NULL,
  `xid` VARCHAR(96),
  `transaction_id` BIGINT,
  `branch_id` BIGINT NOT NULL,
  `resource_id` VARCHAR(256),
  `table_name` VARCHAR(32),
  `pk` VARCHAR(36),
  `gmt_create` DATETIME,
  `gmt_modified` DATETIME,
  PRIMARY KEY (`row_key`),
  KEY `idx_branch_id` (`branch_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

After completing the steps, check the Nacos configuration:

Project Structure

The project contains two sub‑modules: dt‑account‑service (user module) and dt‑storage‑service (inventory module).

Parent POM Dependencies

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    <exclusions>
      <exclusion>
        <groupId>io.seata</groupId>
        <artifactId>seata-all</artifactId>
      </exclusion>
    </exclusions>
  </dependency>
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
  </dependency>
  <dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-all</artifactId>
    <version>${seata.version}</version>
  </dependency>
</dependencies>
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-alibaba-dependencies</artifactId>
      <version>${spring-cloud-alibaba.version}</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-dependencies</artifactId>
      <version>${spring-cloud.version}</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

Account Module Configuration (application.yml)

spring:
  cloud:
    nacos:
      password: nacos
      username: nacos
      discovery:
        server-addr: 118.24.111.33:8848
        namespace: ""
        group: dt-group
---
seata:
  tx-service-group: dt-group
  registry:
    type: nacos
    nacos:
      application: seata-server
      group: dt-group
      namespace: ""
      server-addr: 118.24.111.33:8848
      username: nacos
      password: nacos
  config:
    type: nacos
    nacos:
      namespace: ""
      group: dt-group
      server-addr: 118.24.111.33:8848
      username: nacos
      password: nacos

AccountService Implementation

@Service
public class AccountService {
    private static final String ERROR_USER_ID = "1002";
    @Resource
    private AccountMapper accountMapper;
    @Resource
    private StorageFeignClient storageFeignClient;

    @Transactional(rollbackFor = Exception.class)
    @GlobalTransactional
    public void debit(String userId, BigDecimal num, String commodityCode, int orderCount) {
        System.out.println(RootContext.getXID());
        accountMapper.updateAccount(num, userId);
        storageFeignClient.deduct(commodityCode, orderCount);
        try {
            TimeUnit.MILLISECONDS.sleep(new Random().nextInt(100));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (ERROR_USER_ID.equals(userId)) {
            throw new RuntimeException("account branch exception");
        }
    }
}

Note: The @GlobalTransactional annotation is required to enable Seata’s distributed transaction management.

Feign Client for Storage Service

@FeignClient(name = "storage-service", url = "127.0.0.1:8802")
public interface StorageFeignClient {
    @GetMapping("/storage/deduct")
    void deduct(@RequestParam("commodityCode") String commodityCode, @RequestParam("count") Integer count);
}

Storage Module (Configuration similar to Account)

StorageService Implementation

@Service
public class StorageService {
    @Resource
    private StorageMapper storageMapper;

    @Transactional
    public void deduct(String commodityCode, int count) {
        System.out.println(RootContext.getXID());
        storageMapper.updateStorage(count, commodityCode);
    }
}

Testing the Distributed Transaction

Start Seata, Nacos, account service, and storage service, then access Nacos to verify registration.

Normal request shows data changes; when userId=1002 is used, an exception triggers a rollback, leaving the database unchanged.

Account and storage service consoles confirm no data persistence after the rollback.

Database tables show no changes, confirming successful transaction rollback.

Setup complete.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

NacosSpring Bootmysqldistributed-transactionSeata
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.