上篇文章我们讲述了如何启动seata的本地服务,并且注册到nacos使用,这篇文章将在SpringCloud中整合Seata框架
上篇文章传送门:https://blog.csdn.net/Syals/article/details/130102851?spm=1001.2014.3001.5501
@GlobalTransactional是Seata框架提供的注解,用于开启一个全局事务。当一个方法被标记为@GlobalTransactional时,Seata框架会自动创建一个全局事务,并将该方法的执行视为整个事务的一个参与者。
在使用@GlobalTransactional注解时,需要在Seata Server中配置好相应的事务组,并使用相同的事务组ID和事务模式。同时,所有参与该全局事务的服务都需要使用相同的事务组ID和事务模式,并在业务代码中使用@Transactional注解开启本地事务。
当全局事务中的任何一个本地事务发生异常时,Seata框架会回滚整个全局事务,保证数据的一致性。同时,Seata框架还提供了一系列的扩展点和机制,可以自定义全局事务的创建、提交和回滚过程,以满足不同场景的需求。
使用@GlobalTransactional注解可以简化分布式事务的管理和操作,提高开发效率和数据一致性。
项 | 版本号 |
---|---|
jdk | 1.8 |
SpringBoot | 2.3.12.RELEASE |
SpringCloud | 2.2.7.RELEASE |
SpringCloudVersion | Hoxton.SR12 |
创建一个新的数据库seata-test
,创建数据库表it_order
、it_stock
、undo_log
其中it_order
是我们的订单表,it_stock
为库存表,undo_log
则为seata框架要求的事务日志表,在你每个要操作事务的数据库中都要有一个undo_log表
这里的话数据表随便也可以,我现在就按演示的数据表来进行操作,但是undo_log表是固定的
undo_log表:
-- ----------------------------
-- Table structure for undo_log
-- ----------------------------
DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE `undo_log` (
`id` bigint(0) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(0) NOT NULL,
`xid` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
`context` varchar(128) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(0) NOT NULL,
`log_created` datetime(0) NOT NULL,
`log_modified` datetime(0) NOT NULL,
`ext` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
订单和库存表:
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for it_order
-- ----------------------------
DROP TABLE IF EXISTS `it_order`;
CREATE TABLE `it_order` (
`id` bigint(0) NOT NULL AUTO_INCREMENT,
`order_name` varchar(64) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
`order_status` char(1) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for it_stock
-- ----------------------------
DROP TABLE IF EXISTS `it_stock`;
CREATE TABLE `it_stock` (
`id` bigint(0) NOT NULL AUTO_INCREMENT,
`order_id` bigint(0) NULL DEFAULT NULL,
`repertory` int(0) NULL DEFAULT NULL COMMENT '库存',
`sales` int(0) NULL DEFAULT NULL COMMENT '销量',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = Dynamic;
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<java.version>1.8</java.version>
<spring.cloud.alibaba.version>2.2.7.RELEASE</spring.cloud.alibaba.version>
<spring.boot.version>2.3.12.RELEASE</spring.boot.version>
<spring.cloud.version>Hoxton.SR12</spring.cloud.version>
</properties>
<dependencies>
<!--SpringBoot基本场景启动-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--SpringBoot 测试的场景启动-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<!--Spring Cloud alibaba的版本管理, 通过dependency完成继承-->
<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>
<!--SpringBoot的版本管理-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--Spring Cloud的版本管理-->
<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>
一些常用的依赖项,例如mysql的驱动、web启动器、Druid、Mybaits框架等等。
order和stock的pom文件都是这三个
<dependencies>
<!--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>
<!--1. openfeign依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
stock服务的配置文件我们就不在展示了,将下面配置文件的端口号改为8222,application.name改为seata-stock即可,其他的都是相同的。
server:
port: 8111
spring:
#===============数据库连接=============
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/seata-test?characterEncoding=utf8&useSSL=false&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
application:
name: seata-order#order的服务名称
cloud:
#==============nacos的地址===============
nacos:
discovery:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
seata:
#==============seata的nacos配置=================
registry:
type: nacos#这里的type等同于registry.conf文件的mode = "nacos"
nacos:
server-addr: 127.0.0.1:8848 #seata所在的nacos服务地址
username: nacos
password: nacos
config:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
#这里要注意,这是我们在配置config.txt文件中讲述的service.vgroupMapping.后面的自定义名称
#要跟你在上传配置文件中的组名称一致
tx-service-group: my_tx_group
mybatis:
mapper-locations: classpath:mapper/*.xml
typeAliasesPackage: com.it.entity
configuration:
mapUnderscoreToCamelCase: true
1 启动类上加上@EnableFeignClients注解
2 创建fegin包,并创建一个新的接口,StockService
//value的值是要调用的服务名称,path是接口前缀路径
@FeignClient(value = "seata-stock", path = "/stock/")
public interface StockService {
//这个方法是修改库存方法
@RequestMapping("updateStockByOrderId")
public String updateStockByOrderId(@RequestParam("orderId") Long orderId);
}
@RestController
@RequestMapping("/order/")
public class SeataOrderController {
@Resource
private ItOrderService orderService;
@RequestMapping("saveOrder")
public String saveOrder() {
return orderService.saveOrder();
}
}
@Service
public class ItOrderServiceImpl extends ServiceImpl<ItOrderMapper, ItOrder> implements ItOrderService {
@Resource
private ItOrderMapper orderMapper;
@Resource
private StockService stockService;
@GlobalTransactional
@Override
public String saveOrder() {
ItOrder order = new ItOrder();
order.setOrderName("技嘉主板");
order.setOrderStatus("2");
orderMapper.insert(order);
order.setId(1L);
stockService.updateStockByOrderId(order.getId());
int i = 1 / 0;//故意报错
return "执行完毕!";
}
}
@RestController
@RequestMapping("/stock/")
public class SeataStockController {
@Resource
private ItStockService stockService;
@RequestMapping("updateStockByOrderId")
public void updateStockByOrderId(Long orderId) {
stockService.updateStockByOrderId(orderId);
}
}
@Service
public class ItStockServiceImpl extends ServiceImpl<ItStockMapper, ItStock> implements ItStockService {
@Resource
private ItStockMapper stockMapper;
@Override
public void updateStockByOrderId(Long orderId) {
ItStock stock = stockMapper.selectOne(new QueryWrapper<ItStock>().eq("order_id", orderId));
stock.setRepertory(stock.getRepertory() - 1);
stock.setSales(stock.getSales() + 1);
stockMapper.updateById(stock);
}
}
我们可以看到在order服务中,我们在方法上标记了@GlobalTransactional注解,我们执行完插入语句然后远程调用修改库存服务,调用玩库存服务后故意抛出异常来测试两个服务之间是否都可以正常回滚操作
最后的运行结果我就不在这里演示了,如果出现了@GlobalTransactional注解失效的问题那么有可能是以下原因:
seata最难的地方是在于如何配置它,当搞清楚他的配置文件,使用它只需要一个注解即可,跟Spring提供的本地事务注解一样。
今天就分享到这里,拜拜啦!