1.前提
已经完成了seata服务端启动的环境部署,并已经启动seata服务端
参考:https://www.yuque.com/u27809381/ca4o9w/avy27g
2.案例
如下,假设当前存在一个存在一个订单确认的功能
该功能的对外暴露接口存在于订单服务,接口的功能包括3步
- 订单状态改为已确认
- 物品扣减库存
- 账户扣除花费
3.创建物品服务
建库建表
-- 创建物品数据库CREATE DATABASE `item`;-- 使用物品数据库USE `item`;-- 创建物品表DROP TABLE IF EXISTS `t_item`;CREATE TABLE `t_item` (`id` BIGINT NOT NULL AUTO_INCREMENT,`count` INT DEFAULT NULL COMMENT '库存',PRIMARY KEY (`id`)) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;-- 创建回滚日志表DROP TABLE IF EXISTS `undo_log`;CREATE TABLE `undo_log`(`branch_id` BIGINT NOT NULL COMMENT 'branch transaction id',`xid` VARCHAR(128) NOT NULL COMMENT 'global transaction id',`context` VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',`rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info',`log_status` INT(11) NOT NULL COMMENT '0:normal status,1:defense status',`log_created` DATETIME(6) NOT NULL COMMENT 'create datetime',`log_modified` DATETIME(6) NOT NULL COMMENT 'modify datetime',UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table';-- 插入测试数据INSERT INTO t_item (id,COUNT)VALUES(1,5),(2,15),(3,20),(4,25);
项目引入依赖
<!--web服务依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--Nacos 服务发现依赖--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!--引入 seata 依赖--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId></dependency><!--第三方工具类库--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.18</version></dependency><!--代码生成器依赖--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.5.0</version></dependency><dependency><groupId>org.apache.velocity</groupId><artifactId>velocity-engine-core</artifactId><version>2.3</version></dependency><!-- ...................多数据源连接数据库相关依赖 start................... --><!--动态数据源--><dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>3.5.0</version></dependency><!--Mybatis连接依赖--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.23</version></dependency><!--mybatis-plus依赖--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.0</version></dependency><!--druid连接池--><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.8</version></dependency><!--log4j--><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency><!-- ...................多数据源连接数据库相关依赖 end................... --><!--Lombok依赖--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!--swagger依赖--><dependency><groupId>com.spring4all</groupId><artifactId>swagger-spring-boot-starter</artifactId><version>1.8.0.RELEASE</version></dependency><!--单元测试--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
编写bootstrap.yml文件
#端点配置management:endpoints:web:exposure:include: '*'#spring配置spring:#服务名称application:name: itemcloud:#注册中心配置nacos:discovery:server-addr: 8.142.132.135:8848 #nacos地址#事务分组配置alibaba:seata:tx-service-group: service-item-group
编写application.yml文件
#服务器配置server:port: 8082spring:#配置数据库数据源datasource:dynamic:type: com.alibaba.druid.pool.DruidDataSourceprimary: item#配置连接池druid:initialSize: 10minIdle: 1maxActive: 20maxWait: 60000timeBetweenEvictionRunsMillis: 60000minEvictableIdleTimeMillis: 300000validationQuery: select 'x' FROM DUALtestWhileIdle: truetestOnBorrow: falsetestOnReturn: falsepoolPreparedStatements: falsemaxOpenPreparedStatements: -1filters: stat,log4j,wallconnectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000useGlobalDataSourceStat: true#配置数据源datasource:item:url: jdbc:mysql://10.128.41.42:3306/item?characterEncoding=UTF-8&autoReconnect=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=convertToNull&useUnicode=true&zeroDateTimeBehavior=CONVERT_TO_NULL&useSSL=true&rewriteBatchedStatements=trueusername: rootpassword: sMviwqONsqW&Ag$5type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driver#seata配置seata:#配置中心配置config:type: nacosnacos:server-addr: 8.142.132.135:8848group: "SEATA_GROUP"namespace: ""username: "nacos"password: "nacos"#注册中心配置registry:type: nacosnacos:application: seata-serverserver-addr: 8.142.132.135:8848group: "SEATA_GROUP"namespace: ""username: "nacos"password: "nacos"
启动类添加注解
import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication@EnableDiscoveryClientpublic class ItemApplication {public static void main(String[] args) {SpringApplication.run(ItemApplication.class, args);}}
编写扣减库存接口
mapper
import com.example.item.entity.TItemPo;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import org.apache.ibatis.annotations.Mapper;/*** <p>* Mapper 接口* </p>** @author 冯铁城 [fengtiecheng@cyou-inc.com]* @since 2022-06-27*/@Mapperpublic interface TItemMapper extends BaseMapper<TItemPo> {}
service
import cn.hutool.json.JSONObject;import com.baomidou.mybatisplus.extension.service.IService;import com.example.item.entity.TItemPo;/*** <p>* 服务类* </p>** @author 冯铁城 [fengtiecheng@cyou-inc.com]* @since 2022-06-27*/public interface TItemService extends IService<TItemPo> {/*** 扣减库存** @param id 主键ID* @return 扣减后对象*/JSONObject delCount(Integer id);}
service.impl
import cn.hutool.core.util.ObjectUtil;import cn.hutool.json.JSONObject;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;import com.example.item.entity.TItemPo;import com.example.item.mapper.TItemMapper;import com.example.item.service.TItemService;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;/*** <p>* 服务实现类* </p>** @author 冯铁城 [fengtiecheng@cyou-inc.com]* @since 2022-06-27*/@Service@Transactional(rollbackFor = Exception.class)public class TItemServiceImpl extends ServiceImpl<TItemMapper, TItemPo> implements TItemService {@Overridepublic JSONObject delCount(Integer id) {//1.id判定if (id <= 0) {throw new RuntimeException("id异常");}//2.查询库存TItemPo itemPo = getById(id);if (ObjectUtil.isNull(itemPo)) {throw new RuntimeException("未找到资源");}//3.修改库存,默认-10,测试使用itemPo.setCount(itemPo.getCount() - 10);if (itemPo.getCount() < 0) {throw new RuntimeException("库存数不足");}//4.数据库更新boolean isSuccess = updateById(itemPo);if (!isSuccess) {throw new RuntimeException("数据库更新失败");}//5.封装返回结果JSONObject result = new JSONObject(true);result.set("status", true);result.set("data", itemPo);//6.返回return result;}}
entity
import com.baomidou.mybatisplus.annotation.TableName;import com.baomidou.mybatisplus.annotation.IdType;import com.baomidou.mybatisplus.extension.activerecord.Model;import com.baomidou.mybatisplus.annotation.TableId;import java.io.Serializable;import io.swagger.annotations.ApiModel;import io.swagger.annotations.ApiModelProperty;import lombok.Data;import lombok.EqualsAndHashCode;/*** <p>** </p>** @author 冯铁城 [fengtiecheng@cyou-inc.com]* @since 2022-06-27*/@Data@EqualsAndHashCode(callSuper = false)@TableName("t_item")@ApiModel(value="TItemPo对象", description="")public class TItemPo extends Model<TItemPo> {private static final long serialVersionUID = 1L;@TableId(value = "id", type = IdType.AUTO)private Long id;@ApiModelProperty(value = "库存")private Integer count;@Overridepublic Serializable pkVal() {return this.id;}}
controller
import cn.hutool.json.JSONObject;import com.example.item.service.TItemService;import lombok.RequiredArgsConstructor;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.PutMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;/*** <p>* 前端控制器* </p>** @author 冯铁城 [fengtiecheng@cyou-inc.com]* @since 2022-06-27*/@RestController@RequestMapping("/api/v1/items")@RequiredArgsConstructorpublic class TItemController {private final TItemService itemService;@PutMapping("{id}")public JSONObject delCount(@PathVariable Integer id) {return itemService.delCount(id);}}
4. 创建账户服务
建库建表
-- 创建账户数据库CREATE DATABASE `account`;-- 使用账户数据库USE `account`;-- 创建账户表DROP TABLE IF EXISTS `t_account`;CREATE TABLE `t_account` (`id` BIGINT NOT NULL AUTO_INCREMENT,`count` INT DEFAULT NULL COMMENT '余额',PRIMARY KEY (`id`)) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;-- 创建回滚日志表DROP TABLE IF EXISTS `undo_log`;CREATE TABLE `undo_log`(`branch_id` BIGINT NOT NULL COMMENT 'branch transaction id',`xid` VARCHAR(128) NOT NULL COMMENT 'global transaction id',`context` VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',`rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info',`log_status` INT(11) NOT NULL COMMENT '0:normal status,1:defense status',`log_created` DATETIME(6) NOT NULL COMMENT 'create datetime',`log_modified` DATETIME(6) NOT NULL COMMENT 'modify datetime',UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table';-- 插入测试数据INSERT INTO t_account (id,COUNT)VALUES(1,100),(2,150),(3,200),(4,250);
项目引入依赖
<!--web服务依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--Nacos 服务发现依赖--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!--引入 seata 依赖--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId></dependency><!--第三方工具类库--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.18</version></dependency><!--代码生成器依赖--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.5.0</version></dependency><dependency><groupId>org.apache.velocity</groupId><artifactId>velocity-engine-core</artifactId><version>2.3</version></dependency><!-- ...................多数据源连接数据库相关依赖 start................... --><!--动态数据源--><dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>3.5.0</version></dependency><!--Mybatis连接依赖--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.23</version></dependency><!--mybatis-plus依赖--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.0</version></dependency><!--druid连接池--><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.8</version></dependency><!--log4j--><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency><!-- ...................多数据源连接数据库相关依赖 end................... --><!--Lombok依赖--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!--swagger依赖--><dependency><groupId>com.spring4all</groupId><artifactId>swagger-spring-boot-starter</artifactId><version>1.8.0.RELEASE</version></dependency><!--单元测试--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
编写bootstrap.yml文件
#端点配置management:endpoints:web:exposure:include: '*'#spring配置spring:#服务名称application:name: accountcloud:#注册中心配置nacos:discovery:server-addr: 8.142.132.135:8848 #nacos地址#事务分组配置alibaba:seata:tx-service-group: service-account-group
编写application.yml文件
#服务器配置server:port: 8083spring:#配置数据库数据源datasource:dynamic:type: com.alibaba.druid.pool.DruidDataSourceprimary: account#配置连接池druid:initialSize: 10minIdle: 1maxActive: 20maxWait: 60000timeBetweenEvictionRunsMillis: 60000minEvictableIdleTimeMillis: 300000validationQuery: select 'x' FROM DUALtestWhileIdle: truetestOnBorrow: falsetestOnReturn: falsepoolPreparedStatements: falsemaxOpenPreparedStatements: -1filters: stat,log4j,wallconnectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000useGlobalDataSourceStat: true#配置数据源datasource:account:url: jdbc:mysql://10.128.41.42:3306/account?characterEncoding=UTF-8&autoReconnect=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=convertToNull&useUnicode=true&zeroDateTimeBehavior=CONVERT_TO_NULL&useSSL=true&rewriteBatchedStatements=trueusername: rootpassword: sMviwqONsqW&Ag$5type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driver#seata配置seata:#配置中心配置config:type: nacosnacos:server-addr: 8.142.132.135:8848group: "SEATA_GROUP"namespace: ""username: "nacos"password: "nacos"#注册中心配置registry:type: nacosnacos:application: seata-serverserver-addr: 8.142.132.135:8848group: "SEATA_GROUP"namespace: ""username: "nacos"password: "nacos"
启动类添加注解
import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication@EnableDiscoveryClientpublic class AccountApplication {public static void main(String[] args) {SpringApplication.run(AccountApplication.class, args);}}
编写扣减账户余额接口
mapper
import com.example.account.entity.TAccountPo;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import org.apache.ibatis.annotations.Mapper;/*** <p>* Mapper 接口* </p>** @author 冯铁城 [fengtiecheng@cyou-inc.com]* @since 2022-06-27*/@Mapperpublic interface TAccountMapper extends BaseMapper<TAccountPo> {}
service
import cn.hutool.json.JSONObject;import com.baomidou.mybatisplus.extension.service.IService;import com.example.account.entity.TAccountPo;/*** <p>* 服务类* </p>** @author 冯铁城 [fengtiecheng@cyou-inc.com]* @since 2022-06-27*/public interface TAccountService extends IService<TAccountPo> {/*** 扣减余额** @param id 主键ID* @return 扣减后对象*/JSONObject delCount(Integer id);}
service.impl
import cn.hutool.core.util.ObjectUtil;import cn.hutool.json.JSONObject;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;import com.example.account.entity.TAccountPo;import com.example.account.mapper.TAccountMapper;import com.example.account.service.TAccountService;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;/*** <p>* 服务实现类* </p>** @author 冯铁城 [fengtiecheng@cyou-inc.com]* @since 2022-06-27*/@Service@Transactional(rollbackFor = Exception.class)public class TAccountServiceImpl extends ServiceImpl<TAccountMapper, TAccountPo> implements TAccountService {@Overridepublic JSONObject delCount(Integer id) {//1.id判定if (id <= 0) {throw new RuntimeException("id异常");}//2.查询库存TAccountPo accountPo = getById(id);if (ObjectUtil.isNull(accountPo)) {throw new RuntimeException("未找到资源");}//3.修改余额,默认-100,测试使用accountPo.setCount(accountPo.getCount() - 100);if (accountPo.getCount() < 0) {throw new RuntimeException("余额不足");}//4.数据库更新boolean isSuccess = updateById(accountPo);if (!isSuccess) {throw new RuntimeException("数据库更新失败");}//5.封装返回结果JSONObject result = new JSONObject(true);result.set("status", true);result.set("data", accountPo);//6.返回return result;}}
entity
import com.baomidou.mybatisplus.annotation.IdType;import com.baomidou.mybatisplus.annotation.TableId;import com.baomidou.mybatisplus.annotation.TableName;import com.baomidou.mybatisplus.extension.activerecord.Model;import io.swagger.annotations.ApiModel;import io.swagger.annotations.ApiModelProperty;import lombok.Data;import lombok.EqualsAndHashCode;import java.io.Serializable;/*** <p>** </p>** @author 冯铁城 [fengtiecheng@cyou-inc.com]* @since 2022-06-27*/@Data@EqualsAndHashCode(callSuper = false)@TableName("t_account")@ApiModel(value = "TAccountPo对象", description = "")public class TAccountPo extends Model<TAccountPo> {private static final long serialVersionUID = 1L;@TableId(value = "id", type = IdType.AUTO)private Long id;@ApiModelProperty(value = "余额")private Integer count;@Overridepublic Serializable pkVal() {return this.id;}}
controller
import cn.hutool.json.JSONObject;import com.example.account.service.TAccountService;import lombok.RequiredArgsConstructor;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.PutMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;/*** <p>* 前端控制器* </p>** @author 冯铁城 [fengtiecheng@cyou-inc.com]* @since 2022-06-27*/@RestController@RequestMapping("/api/v1/accounts")@RequiredArgsConstructorpublic class TAccountController {private final TAccountService accountService;@PutMapping("{id}")public JSONObject delCount(@PathVariable Integer id) {return accountService.delCount(id);}}
5.创建订单服务
建库建表
-- 创建订单数据库CREATE DATABASE `order`;-- 使用订单数据库USE `order`;-- 创建订单表DROP TABLE IF EXISTS `t_order`;CREATE TABLE `t_order` (`id` BIGINT NOT NULL AUTO_INCREMENT,`status` INT DEFAULT NULL COMMENT '订单状态:0:未完成;1:已完结',PRIMARY KEY (`id`)) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;-- 创建回滚日志表DROP TABLE IF EXISTS `undo_log`;CREATE TABLE `undo_log`(`branch_id` BIGINT NOT NULL COMMENT 'branch transaction id',`xid` VARCHAR(128) NOT NULL COMMENT 'global transaction id',`context` VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',`rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info',`log_status` INT(11) NOT NULL COMMENT '0:normal status,1:defense status',`log_created` DATETIME(6) NOT NULL COMMENT 'create datetime',`log_modified` DATETIME(6) NOT NULL COMMENT 'modify datetime',UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table';-- 插入测试数据INSERT INTO t_order (id,STATUS)VALUES(1,0),(2,0),(3,0),(4,0);
项目引入依赖
<!--web服务依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--Nacos 服务发现依赖--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!--引入 seata 依赖--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId></dependency><!-- openfeign依赖 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!-- openfeign优化请求连接池依赖 --><dependency><groupId>io.github.openfeign</groupId><artifactId>feign-httpclient</artifactId></dependency><!--第三方工具类库--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.18</version></dependency><!-- ...................多数据源连接数据库相关依赖 start................... --><!--动态数据源--><dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>3.5.0</version></dependency><!--Mybatis连接依赖--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.23</version></dependency><!--mybatis-plus依赖--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.0</version></dependency><!--druid连接池--><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.8</version></dependency><!--log4j--><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency><!-- ...................多数据源连接数据库相关依赖 end................... --><!--Lombok依赖--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!--swagger依赖--><dependency><groupId>com.spring4all</groupId><artifactId>swagger-spring-boot-starter</artifactId><version>1.8.0.RELEASE</version></dependency><!--单元测试--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
编写bootstramp.yml文件
#端点配置management:endpoints:web:exposure:include: '*'#spring配置spring:#服务名称application:name: ordercloud:#注册中心配置nacos:discovery:server-addr: 8.142.132.135:8848 #nacos地址#事务分组配置alibaba:seata:tx-service-group: service-order-group
编写application.yml文件
#服务器配置server:port: 8081spring:#配置数据库数据源datasource:dynamic:type: com.alibaba.druid.pool.DruidDataSourceprimary: order#配置连接池druid:initialSize: 10minIdle: 1maxActive: 20maxWait: 60000timeBetweenEvictionRunsMillis: 60000minEvictableIdleTimeMillis: 300000validationQuery: select 'x' FROM DUALtestWhileIdle: truetestOnBorrow: falsetestOnReturn: falsepoolPreparedStatements: falsemaxOpenPreparedStatements: -1filters: stat,log4j,wallconnectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000useGlobalDataSourceStat: true#配置数据源datasource:order:url: jdbc:mysql://10.128.41.42:3306/order?characterEncoding=UTF-8&autoReconnect=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=convertToNull&useUnicode=true&zeroDateTimeBehavior=CONVERT_TO_NULL&useSSL=true&rewriteBatchedStatements=trueusername: rootpassword: sMviwqONsqW&Ag$5type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driver#seata配置seata:#配置中心配置config:type: nacosnacos:server-addr: 8.142.132.135:8848group: "SEATA_GROUP"namespace: ""username: "nacos"password: "nacos"#注册中心配置registry:type: nacosnacos:application: seata-serverserver-addr: 8.142.132.135:8848group: "SEATA_GROUP"namespace: ""username: "nacos"password: "nacos"#ribbon配置ribbon:ReadTimeout: 6000ConnectionTimeout: 6000#feign配置feign:client:httpclient:enabled: truecompression:request:enabled: truemime-types: text/xml,application/xml, application/jsonmin-request-size: 1024response:enabled: true
启动类添加注解
import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;import org.springframework.cloud.openfeign.EnableFeignClients;@SpringBootApplication@EnableDiscoveryClient@EnableFeignClientspublic class OrderApplication {public static void main(String[] args) {SpringApplication.run(OrderApplication.class, args);}}
编写订单确认接口
mapper
import com.example.order.entity.TOrderPo;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import org.apache.ibatis.annotations.Mapper;/*** <p>* Mapper 接口* </p>** @author 冯铁城 [fengtiecheng@cyou-inc.com]* @since 2022-06-27*/@Mapperpublic interface TOrderMapper extends BaseMapper<TOrderPo> {}
service
import cn.hutool.json.JSONObject;import com.baomidou.mybatisplus.extension.service.IService;import com.example.order.entity.TOrderPo;/*** <p>* 服务类* </p>** @author 冯铁城 [fengtiecheng@cyou-inc.com]* @since 2022-06-27*/public interface TOrderService extends IService<TOrderPo> {/*** 修改订单状态** @param id 订单ID* @return 修改后的订单*/JSONObject updateOrder(Integer id);}
service.impl
import cn.hutool.core.util.ObjectUtil;import cn.hutool.json.JSONObject;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;import com.example.order.entity.TOrderPo;import com.example.order.mapper.TOrderMapper;import com.example.order.service.TOrderService;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;/*** <p>* 服务实现类* </p>** @author 冯铁城 [fengtiecheng@cyou-inc.com]* @since 2022-06-27*/@Service@Transactional(rollbackFor = Exception.class)public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrderPo> implements TOrderService {@Overridepublic JSONObject updateOrder(Integer id) {//1.id判定if (id <= 0) {throw new RuntimeException("id异常");}//2.查询账户TOrderPo orderPo = getById(id);if (ObjectUtil.isNull(orderPo)) {throw new RuntimeException("未找到资源");}//3.修改订单状态orderPo.setStatus(1);//4.数据库更新boolean isSuccess = updateById(orderPo);if (!isSuccess) {throw new RuntimeException("数据库更新失败");}//5.封装返回结果JSONObject result = new JSONObject(true);result.set("status", true);result.set("data", orderPo);//6.返回return result;}}
entity
import com.baomidou.mybatisplus.annotation.TableName;import com.baomidou.mybatisplus.annotation.IdType;import com.baomidou.mybatisplus.extension.activerecord.Model;import com.baomidou.mybatisplus.annotation.TableId;import java.io.Serializable;import io.swagger.annotations.ApiModel;import io.swagger.annotations.ApiModelProperty;import lombok.Data;import lombok.EqualsAndHashCode;/*** <p>** </p>** @author 冯铁城 [fengtiecheng@cyou-inc.com]* @since 2022-06-27*/@Data@EqualsAndHashCode(callSuper = false)@TableName("t_order")@ApiModel(value="TOrderPo对象", description="")public class TOrderPo extends Model<TOrderPo> {private static final long serialVersionUID = 1L;@TableId(value = "id", type = IdType.AUTO)private Long id;@ApiModelProperty(value = "订单状态:0:未完成;1:已完结")private Integer status;@Overridepublic Serializable pkVal() {return this.id;}}
ItemFeign
import cn.hutool.json.JSONObject;import org.springframework.cloud.openfeign.FeignClient;import org.springframework.stereotype.Component;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.PutMapping;/*** @author 冯铁城 [fengtiecheng@cyou-inc.com]* @date 2022-06-27 18:24:50* @describe:*/@Component@FeignClient(value = "item")public interface ItemFeign {@PutMapping("/api/v1/items/{id}")JSONObject delCount(@PathVariable Integer id);}
AccountFeign
import cn.hutool.json.JSONObject;import org.springframework.cloud.openfeign.FeignClient;import org.springframework.stereotype.Component;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.PutMapping;/*** @author 冯铁城 [fengtiecheng@cyou-inc.com]* @date 2022-06-27 18:24:50* @describe:*/@Component@FeignClient(value = "account")public interface AccountFeign {@PutMapping("/api/v1/accounts/{id}")JSONObject delCount(@PathVariable Integer id);}
controller
import cn.hutool.json.JSONObject;import cn.hutool.log.StaticLog;import com.example.order.feign.AccountFeign;import com.example.order.feign.ItemFeign;import com.example.order.service.TOrderService;import lombok.RequiredArgsConstructor;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.PutMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;/*** <p>* 前端控制器* </p>** @author 冯铁城 [fengtiecheng@cyou-inc.com]* @since 2022-06-27*/@RestController@RequestMapping("/api/v1/orders")@RequiredArgsConstructorpublic class TOrderController {private final TOrderService orderService;private final ItemFeign itemFeign;private final AccountFeign accountFeign;@PutMapping("{id}")public JSONObject updateOrder(@PathVariable Integer id) {//1.修改订单状态JSONObject result = orderService.updateOrder(id);//2.调用物品服务扣减库存JSONObject itemResult = itemFeign.delCount(id);StaticLog.info("物品服务扣减库存响应:[{}]", itemResult);//3.调用账户服务扣减余额JSONObject accountResult = accountFeign.delCount(id);StaticLog.info("账户服务扣减余额响应:[{}]", accountResult);//4.返回return result;}}
6.未引入seata触发修改订单状态接口
因为item服务每次会扣除10库存,而id=1的库存=5,当库存不足时,item服务会抛出异常,因此调用item服务的order服务也会异常
如图:响应异常
异常原因出现在item服务
但是此时查看order数据库,id为1的订单状态,发现状态已经变更了,此时就出现了数据不一致问题
7.@GlobalTransactional注解引入
注解说明
seata提供了@GlobalTransactional标签来控制全局事务的开启、管理以及控制
@GlobalTransactional注解最常用的属性
- name:声明全局事务的名称
- rollbackFor:出现什么异常时全局回滚
@GlobalTransactional注解的作用域
- 在方法上使用时,全局事务的范围就是该方法以及它所涉及的所有服务。
- 在类上使用时,全局事务的范围就是这个类中的所有方法以及这些方法涉及的服务。
order服务改造
```java import cn.hutool.json.JSONObject; import cn.hutool.log.StaticLog; import com.example.order.feign.AccountFeign; import com.example.order.feign.ItemFeign; import com.example.order.service.TOrderService; import io.seata.spring.annotation.GlobalTransactional; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
/**
- 前端控制器
- *
- @author 冯铁城 [fengtiecheng@cyou-inc.com]
@since 2022-06-27 */ @RestController @RequestMapping(“/api/v1/orders”) @RequiredArgsConstructor public class TOrderController {
private final TOrderService orderService;
private final ItemFeign itemFeign;
private final AccountFeign accountFeign;
@PutMapping(“{id}”) @GlobalTransactional(name = “order-update-order”, rollbackFor = Exception.class) public JSONObject updateOrder(@PathVariable Integer id) {
//1.修改订单状态JSONObject result = orderService.updateOrder(id);//2.调用物品服务扣减库存JSONObject itemResult = itemFeign.delCount(id);StaticLog.info("物品服务扣减库存响应:[{}]", itemResult);//3.调用账户服务扣减余额JSONObject accountResult = accountFeign.delCount(id);StaticLog.info("账户服务扣减余额响应:[{}]", accountResult);//4.返回return result;
改造后具体逻辑步骤


