在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实现服务之间的调用,所以你必须具备以下基础知识:
- 在Spring Cloud中使用Nacos作为服务注册、发现中心的配置及使用。
- 在Spring Cloud中通过OpenFeign实现服务之间的调用。
- MyBatis的使用。
场景描述
本文参考尚硅谷阳哥的视频教程,使用最新的Nacos(2.0.2)及Seata(1.4.2),演示用户下单的业务场景,基本流程如下
其实业务很简单,用户下单,扣减商品库存表数据然后减去账户余额,如果不是微服务架构,我们一般使用一个系统,基本上一个@Transactional
注解就解决了。
基于微服务就不同了,我们提供三个微服务,分别为订单服务、库存服务、账户服务,三个服务对应三个数据库,分别为seata_order、seata_storage、seata_account,现在不同业务分布在不同的数据库中,那么我们在进行数据回滚就不太好处理了。
Seata的分布式交易解决方案
有了Seata就简单了,我们只需要使用一个@GlobalTransactional
注解在业务方法上就可以了。
Spring Cloud使用Seata
创建数据库
创建三个数据库,分别为seata_order
、seata_storage
、seata_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测试
可以看到系统抛出了异常,然后我们查看以下数据库,可以看到数据没有发生任何变化。
评论 (0)