Seata基础教程下-Spring Cloud Alibaba使用Seata

Seata基础教程下-Spring Cloud Alibaba使用Seata

Laughing
2021-07-26 / 0 评论 / 1,031 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2024年03月21日,已超过303天没有更新,若内容或图片失效,请留言反馈。

seata基础教程上-seata 1.4.2的安装及基于Nacos的配置中,我们基于Nacos完成了Seata的安装及配置,这篇文章我们介绍下在Spring Cloud Alibaba中的使用。

环境准备

学习本文之前,你应该准备好以下环境。

  • Nacos 2.0.2版本并运行
  • Seata Server 1.4.2版本,配置好Nacos并运行

前置知识

本文通过Spring Cloud Alibaba,使用Nacos作为服务中心,通过OpenFeign实现服务之间的调用,所以你必须具备以下基础知识:

  1. 在Spring Cloud中使用Nacos作为服务注册、发现中心的配置及使用。
  2. 在Spring Cloud中通过OpenFeign实现服务之间的调用。
  3. MyBatis的使用。

场景描述

本文参考尚硅谷阳哥的视频教程,使用最新的Nacos(2.0.2)及Seata(1.4.2),演示用户下单的业务场景,基本流程如下
其实业务很简单,用户下单,扣减商品库存表数据然后减去账户余额,如果不是微服务架构,我们一般使用一个系统,基本上一个@Transactional注解就解决了。
基于微服务就不同了,我们提供三个微服务,分别为订单服务、库存服务、账户服务,三个服务对应三个数据库,分别为seata_order、seata_storage、seata_account,现在不同业务分布在不同的数据库中,那么我们在进行数据回滚就不太好处理了。

Seata的分布式交易解决方案

有了Seata就简单了,我们只需要使用一个@GlobalTransactional注解在业务方法上就可以了。

Spring Cloud使用Seata

创建数据库

创建三个数据库,分别为seata_orderseata_storageseata_account

订单微服务表结构

create table t_order
(
    id bigint auto_increment comment '主键',
    user_id bigint null comment '用户Id',
    product_id bigint null,
    count int null,
    money decimal(11) null,
    status int(1) null,
    constraint t_order_id_uindex
        unique (id)
);

alter table t_order
    add primary key (id);

create table undo_log
(
    id bigint auto_increment
        primary key,
    branch_id bigint not null,
    xid varchar(100) 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,
    constraint ux_undo_log
        unique (xid, branch_id)
)
charset=utf8;

账户微服务表结构

create table t_account
(
    id bigint auto_increment,
    user_id bigint null,
    total decimal null,
    used decimal null,
    residue decimal null,
    constraint t_account_id_uindex
        unique (id)
);

alter table t_account
    add primary key (id);

create table undo_log
(
    id bigint auto_increment
        primary key,
    branch_id bigint not null,
    xid varchar(100) 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,
    constraint ux_undo_log
        unique (xid, branch_id)
)
charset=utf8;
INSERT INTO seata_account.t_account (id, user_id, total, used, residue) VALUES (1, 1, 1000, 0, 1000);

库存微服务表结构

create table t_storage
(
    id bigint auto_increment,
    product_id bigint null,
    total int null,
    used int null,
    residue int null comment '剩余库存',
    constraint t_storage_id_uindex
        unique (id)
);

alter table t_storage
    add primary key (id);

create table undo_log
(
    id bigint auto_increment
        primary key,
    branch_id bigint not null,
    xid varchar(100) 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,
    constraint ux_undo_log
        unique (xid, branch_id)
)
charset=utf8;

INSERT INTO seata_storage.t_storage (id, product_id, total, used, residue) VALUES (2, 1, 100, 0, 100);

创建工程

工程结构,如下图,请忽略其他模块。

[alt type="warning"]
一定要注意依赖里面的版本号,不然会出现各种错误。
[/alt]

创建父工程

父工程用于维护依赖管理,没有具体代码。

增加依赖

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>net.xiangcaowuyu</groupId>
    <artifactId>Cloud2020</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>

    <modules>
        <module>cloud-provider-payment8001</module>
        <module>cloud-consumer-order80</module>
        <module>cloud-api-common</module>
        <module>cloud-provider-payment8004</module>
        <module>cloud-consumerzk-order80</module>
        <module>cloud-consumer-feign-order80</module>
        <module>cloud-provider-hystrix-payment8001</module>
        <module>cloud-consumer-feign-hystrix-order80</module>
        <module>cloud-consumer-hystrix-dashboard9001</module>
        <module>cloud-gateway-gateway9527</module>
        <module>cloud-config-center-3344</module>
        <module>cloud-config-client-3355</module>
        <module>cloud-config-client-3366</module>
        <module>cloud-stream-rabbitmq-provider8801</module>
        <module>cloud-stream-rabbitmq-consumer8802</module>
        <module>cloud-stream-rabbitmq-consumer8803</module>
        <module>cloud-stream-rabbitmq-consumer8802</module>
        <module>cloud-stream-rabbitmq-consumer8803</module>
        <module>cloud-stream-rabbitmq-consumer8803</module>
        <module>cloudalibaba-provider-payment9001</module>
        <module>cloudalibaba-provider-payment9002</module>
        <module>cloudalibaba-consumer-order80</module>
        <module>cloudalibaba-config-nacos-client3377</module>
        <module>cloudalibaba-sentinel-service8401</module>
        <module>seata-order-service2001</module>
        <module>seata-storage-service2002</module>
        <module>seata-account-service2003</module>
    </modules>

    <!--    统一管理jar包版本-->
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <junit.version>4.12</junit.version>
        <log4j.version>1.2.17</log4j.version>
        <lombok.version>1.16.18</lombok.version>
        <mysql.version>5.1.47</mysql.version>
        <druid.version>1.1.16</druid.version>
        <mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.3.12.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR12</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
<!--                <version>2.1.0.RELEASE</version>-->
                <version>2.2.1.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>${druid.version}</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>${mybatis.spring.boot.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
            <plugins>
                <plugin>
                    <artifactId>maven-clean-plugin</artifactId>
                    <version>3.1.0</version>
                </plugin>
                <plugin>
                    <artifactId>maven-site-plugin</artifactId>
                    <version>3.7.1</version>
                </plugin>
                <plugin>
                    <artifactId>maven-project-info-reports-plugin</artifactId>
                    <version>3.0.0</version>
                </plugin>
            </plugins>
        </pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-site-plugin</artifactId>
                <configuration>
                    <locales>en,fr</locales>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <reporting>
        <plugins>
            <plugin>
                <artifactId>maven-project-info-reports-plugin</artifactId>
            </plugin>
        </plugins>
    </reporting>
</project>

创建通用模块

通用模块这里维护了一个CommonResult用于返回Json对象。

增加依赖

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>Cloud2020</artifactId>
        <groupId>net.xiangcaowuyu</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-api-common</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.5</version>
        </dependency>
    </dependencies>

</project>

增加实体

package net.xiangcaowuyu.springcloud.entites;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 博客:https://www.xiangcaowuyu.net
 * Description:
 *
 * @Author: 香草物语
 * DateTime: 2021-07-24 11:25
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T> {

    private Integer code;

    private String message;

    private T data;

    public CommonResult(Integer code, String message) {
        this(code, message, null);
    }
}

创建订单微服务模块

订单模块维护订单微服务,通过Feign调用库存及账户微服务。

增加依赖

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>Cloud2020</artifactId>
        <groupId>net.xiangcaowuyu</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>seata-order-service2001</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.5</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.seata</groupId>
                    <artifactId>seata-spring-boot-starter</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-spring-boot-starter</artifactId>
            <version>1.4.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>net.xiangcaowuyu</groupId>
            <artifactId>cloud-api-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>

修改配置文件

配置文件一定要注意,tx-service-group一定要与我们Seata服务端的配置保持一致。

server:
  port: 2001
spring:
  application:
    name: seata-order-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:1111
    loadbalancer:
      retry:
        enabled: false
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/seata_order?useSSL=false
    username: root
    password: root
feign:
  hystrix:
    enabled: false
mybatis:
  type-aliases-package: net.xiangcaowuyu.springcloud.alibaba.seata.domain
  mapperLocations: classpath:/mapper/*.xml
seata:
  tx-service-group: my_tx_group #与config.txt 中的service.vgroupMapping.my_tx_group=default一致
  service:
    vgroup-mapping:
      my_tx_group: default #与config.txt 中的service.vgroupMapping.my_tx_group=default一致
  config:
    type: nacos
    nacos:
      server-addr: localhost:1111
      group: "SEATA_GROUP"
      namespace: ""
      username: "nacos"
      password: "nacos"

增加实体

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {

    /**
     * 主键
     */
    private Long id;

    /**
     * 用户Id
     */
    private Long userId;

    private Long productId;

    private Integer count;

    private BigDecimal money;

    private Integer status;
}

增加Mapper

public interface OrderDao {

    int create(Order record);

    int update(@Param("userId") Long userId, @Param("status") Integer status);

数据库操作

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="net.xiangcaowuyu.springcloud.alibaba.seata.dao.OrderDao">
    <resultMap id="BaseResultMap" type="net.xiangcaowuyu.springcloud.alibaba.seata.domain.Order">
        <!--@Table t_order-->
        <result column="id" jdbcType="BIGINT" property="id"/>
        <result column="user_id" jdbcType="BIGINT" property="userId"/>
        <result column="product_id" jdbcType="BIGINT" property="productId"/>
        <result column="count" jdbcType="INTEGER" property="count"/>
        <result column="money" jdbcType="DECIMAL" property="money"/>
        <result column="status" jdbcType="INTEGER" property="status"/>
    </resultMap>
    <sql id="Base_Column_List">
        <!--@mbg.generated-->
        id, user_id, product_id, `count`, money, `status`
    </sql>
    <insert id="create" parameterType="net.xiangcaowuyu.springcloud.alibaba.seata.domain.Order">
        <!--@mbg.generated-->
        insert into t_order (user_id, product_id,
                             `count`, money, `status`)
        values (#{userId,jdbcType=BIGINT}, #{productId,jdbcType=BIGINT},
                #{count,jdbcType=INTEGER}, #{money,jdbcType=DECIMAL}, #{status,jdbcType=INTEGER})
    </insert>

    <update id="update">
        update t_order
        set status = 1
        where user_id = #{userId}
          and status = #{status}
    </update>
</mapper>

增加服务接口

增加账户微服务Feign调用接口
@FeignClient(value = "seata-account-service")
public interface IAccountService {

    @PostMapping("/account/decrease")
    public CommonResult accountDecrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money);

}
增加库存微服务Feign调用接口
@FeignClient(value = "seata-storage-service")
public interface IStorageService {

    @PostMapping("/storage/decrease")
    public CommonResult storageDecrease(@RequestParam("productId") Long productId,@RequestParam("count") Integer count);

}
增加订单接口
public interface IOrderService {

    int create(Order record);

}

增加订单服务实现

@Service
@Slf4j
public class OrderServiceImpl implements IOrderService {

    @Resource
    private OrderDao orderDao;

    @Resource
    private IStorageService storageService;

    @Resource
    private IAccountService accountService;

    @Override
    @GlobalTransactional(name = "fsp-create-order")
//    @Transactional
    public int create(Order order) {
        log.info("--------->开始新建订单");
        orderDao.create(order);
        log.info("--------->订单微服务开始调用库存,做扣减Count");
        storageService.storageDecrease(order.getProductId(), order.getCount());
        log.info("--------->订单微服务开始调用库存,做扣减End");
        log.info("--------->订单微服务开始调用账户,做扣减Money");
        accountService.accountDecrease(order.getUserId(), order.getMoney());
        log.info("--------->订单微服务开始调用账户,做扣减End");
        log.info("--------->修改订单状态开始");
        orderDao.update(order.getUserId(), 0);
        log.info("--------->修改订单状态End");
        return 1;
    }
}

增加接口controller

@RestController
@Slf4j
@RequestMapping("/order")
public class OrderController {

    @Resource
    private IOrderService orderService;

    @PostMapping("/create")
    public CommonResult create(Order order) {
        orderService.create(order);
        return new CommonResult(200, "订单创建成功");
    }

}

增加DataSource,让Seata代理数据库

@Configuration
public class DataSourceProxyConfig {

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druidDataSource(){
        return new DruidDataSource();
    }

}

修改启动类

取消数据源自动配置,让Seata能够代理数据源。

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableDiscoveryClient
@EnableFeignClients
@MapperScan(basePackages = {"net.xiangcaowuyu.springcloud.alibaba.seata.dao"})
public class SeataOrderServiceMain2001 {

    public static void main(String[] args) {
        SpringApplication.run(SeataOrderServiceMain2001.class, args);
    }

}

创建库存微服务模块

库存微服务用于提供扣减库存的微服务接口。

增加依赖

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>Cloud2020</artifactId>
        <groupId>net.xiangcaowuyu</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>seata-storage-service2002</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.5</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.seata</groupId>
                    <artifactId>seata-spring-boot-starter</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-spring-boot-starter</artifactId>
            <version>1.4.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>net.xiangcaowuyu</groupId>
            <artifactId>cloud-api-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>

修改配置文件

server:
  port: 2002
spring:
  application:
    name: seata-storage-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:1111
    loadbalancer:
      retry:
        enabled: false
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/seata_storage?useSSL=false
    username: root
    password: root
feign:
  hystrix:
    enabled: false
mybatis:
  type-aliases-package: net.xiangcaowuyu.springcloud.alibaba.seata.domain
  mapperLocations: classpath:/mapper/*.xml
seata:
  tx-service-group: my_tx_group #与config.txt 中的service.vgroupMapping.my_tx_group=default一致
  service:
    vgroup-mapping:
      my_tx_group: default #与config.txt 中的service.vgroupMapping.my_tx_group=default一致
  config:
    type: nacos
    nacos:
      server-addr: localhost:1111
      group: "SEATA_GROUP"
      namespace: ""
      username: "nacos"
      password: "nacos"

增加实体

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Storage {
    private Long id;

    private Long productId;

    private Integer total;

    private Integer used;

    private Integer residue;
}

增加Mapper

public interface StorageDao {

    int decrease(@Param("productId") Long productId, @Param("count") Integer count);

}

数据库操作

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="net.xiangcaowuyu.springcloud.alibaba.seata.dao.StorageDao">
    <resultMap id="BaseResultMap" type="net.xiangcaowuyu.springcloud.alibaba.seata.domain.Storage">
        <!--@Table t_storage-->
        <result column="id" jdbcType="BIGINT" property="id"/>
        <result column="product_id" jdbcType="BIGINT" property="productId"/>
        <result column="total" jdbcType="INTEGER" property="total"/>
        <result column="used" jdbcType="INTEGER" property="used"/>
        <result column="residue" jdbcType="INTEGER" property="residue"/>
    </resultMap>
    <sql id="Base_Column_List">
        id,
        product_id,
        total,
        used,
        residue
    </sql>

    <update id="decrease">
        update t_storage
        set used    = used + #{count},
            residue = residue - #{count}
        where product_id = #{productId}
    </update>
</mapper>

增加服务接口

public interface IStorageService {

    int decrease(Long productId,  Integer count);

}

增加服务实现

@Service
@Slf4j
public class StorageServiceImpl implements IStorageService {

    @Resource
    private StorageDao storageDao;

    @Override
//    @Transactional
    public int decrease(Long productId, Integer count) {
        log.info("--------->storage-------decrease获取xid:" + RootContext.getXID());
        return storageDao.decrease(productId, count);
    }
}

增加接口controller

@RestController
@RequestMapping("/storage")
@Slf4j
public class StorageController {

    @Resource
    private IStorageService storageService;

    @PostMapping("/decrease")
    public CommonResult decrease(@RequestParam("productId") Long productId, @RequestParam("count") Integer count) {
        storageService.decrease(productId, count);
        return new CommonResult(200, "扣减库存成功");
    }

}

增加DataSource,让Seata代理数据源

@Configuration
public class DataSourceProxyConfig {

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druidDataSource(){
        return new DruidDataSource();
    }

}

修改启动类

取消数据源自动配置,让Seata能够代理数据源。

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableDiscoveryClient
@EnableFeignClients
@MapperScan(basePackages = {"net.xiangcaowuyu.springcloud.alibaba.seata.dao"})
public class SeataStorageServiceMain2002 {

    public static void main(String[] args) {
        SpringApplication.run(SeataStorageServiceMain2002.class, args);
    }

}

创建账户微服务模块

账户微服务用于提供扣减账户金额的微服务接口。

增加依赖

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>Cloud2020</artifactId>
        <groupId>net.xiangcaowuyu</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>seata-account-service2003</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.5</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.seata</groupId>
                    <artifactId>seata-spring-boot-starter</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-spring-boot-starter</artifactId>
            <version>1.4.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>net.xiangcaowuyu</groupId>
            <artifactId>cloud-api-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>

修改配置文件

server:
  port: 2003
spring:
  application:
    name: seata-account-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:1111
    loadbalancer:
      retry:
        enabled: false
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/seata_account?useSSL=false
    username: root
    password: root
feign:
  hystrix:
    enabled: false
mybatis:
  type-aliases-package: net.xiangcaowuyu.springcloud.alibaba.seata.domain
  mapperLocations: classpath:/mapper/*.xml
seata:
  tx-service-group: my_tx_group #与config.txt 中的service.vgroupMapping.my_tx_group=default一致
  service:
    vgroup-mapping:
      my_tx_group: default #与config.txt 中的service.vgroupMapping.my_tx_group=default一致
  config:
    type: nacos
    nacos:
      server-addr: localhost:1111
      group: "SEATA_GROUP"
      namespace: ""
      username: "nacos"
      password: "nacos"

增加实体

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Account {
    private Long id;

    private Long userId;

    private Long total;

    private Long used;

    private Long residue;
}

增加Mapper

public interface AccountDao {

    int decrease(@Param("userId") Long userId, @Param("money") BigDecimal money);

}

数据库操作

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="net.xiangcaowuyu.springcloud.alibaba.seata.dao.AccountDao">
    <resultMap id="BaseResultMap" type="net.xiangcaowuyu.springcloud.alibaba.seata.domain.Account">
        <!--@Table t_account-->
        <result column="id" jdbcType="BIGINT" property="id"/>
        <result column="user_id" jdbcType="BIGINT" property="userId"/>
        <result column="total" jdbcType="DECIMAL" property="total"/>
        <result column="used" jdbcType="DECIMAL" property="used"/>
        <result column="residue" jdbcType="DECIMAL" property="residue"/>
    </resultMap>
    <sql id="Base_Column_List">
        id,
        user_id,
        total,
        used,
        residue
    </sql>
    <update id="decrease">
        update t_account
        set used   = used + #{money},
            residue=residue - #{money}
        where user_id = #{userId}
    </update>
</mapper>

增加服务接口

public interface IAccountService {

    int decrease( Long userId, BigDecimal money);

}

增加服务实现

这里注意以下,我们模拟了报错信息

@Service
@Slf4j
public class AccountServiceImpl implements IAccountService {

    @Resource
    private AccountDao accountDao;

    @Override
//    @Transactional
    public int decrease(Long userId, BigDecimal money) {
        log.info("--------->storage-------decrease获取xid:" + RootContext.getXID());
        //模拟报错
        int age = 10 / 0;
        return accountDao.decrease(userId, money);
    }
}

增加接口controller

@RestController
@Slf4j
@RequestMapping("/account")
public class AccountController {

    @Resource
    private IAccountService accountService;

    @PostMapping("/decrease")
    public CommonResult decrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money) {
        accountService.decrease(userId, money);
        return new CommonResult(200, "账户扣减成功");
    }

}

增加DataSource,让Seata代理数据源

@Configuration
public class DataSourceProxyConfig {

    @Value("${mybatis.mapperLocations}")
    private String mapperLocations;

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druidDataSource(){
        return new DruidDataSource();
    }

}

修改启动类

取消数据源自动配置,让Seata能够代理数据源。

@EnableDiscoveryClient
@EnableFeignClients
@MapperScan(basePackages = {"net.xiangcaowuyu.springcloud.alibaba.seata.dao"})
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class SeataAccountServiceMain2003 {

    public static void main(String[] args) {
        SpringApplication.run(SeataAccountServiceMain2003.class, args);
    }

}

测试

通过postman测试
可以看到系统抛出了异常,然后我们查看以下数据库,可以看到数据没有发生任何变化。

1

评论 (0)

取消