Seata分布式事物案例及详解
spring cloud alibaba Seata分布式事物 详解
安装seata
官网: https://seata.apache.org/zh-cn/download/seata-server
下载后直接解压 进到bin目录 输入cmd
启动命令:seata-server.bat
进入localhost:7091 默认账户跟密码都为seata
seate分布式事务案例
- 创建数据库
CREATE DATABASE IF NOT EXISTS `storage_db`;
USE `storage_db`;
DROP TABLE IF EXISTS `storage_tbl`;
CREATE TABLE `storage_tbl` (`id` int(11) NOT NULL AUTO_INCREMENT,`commodity_code` varchar(255) DEFAULT NULL,`count` int(11) DEFAULT 0,PRIMARY KEY (`id`),UNIQUE KEY (`commodity_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO storage_tbl (commodity_code, count) VALUES ('P0001', 100);
INSERT INTO storage_tbl (commodity_code, count) VALUES ('B1234', 10);
DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE `undo_log` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`branch_id` bigint(20) NOT NULL,`xid` varchar(100) NOT NULL,`context` varchar(128) NOT NULL,`rollback_info` longblob NOT NULL,`log_status` int(11) NOT NULL,`log_created` datetime NOT NULL,`log_modified` datetime NOT NULL,`ext` varchar(100) DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;CREATE DATABASE IF NOT EXISTS `order_db`;
USE `order_db`;
DROP TABLE IF EXISTS `order_tbl`;
CREATE TABLE `order_tbl` (`id` int(11) NOT NULL AUTO_INCREMENT,`user_id` varchar(255) DEFAULT NULL,`commodity_code` varchar(255) DEFAULT NULL,`count` int(11) DEFAULT 0,`money` int(11) DEFAULT 0,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE `undo_log` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`branch_id` bigint(20) NOT NULL,`xid` varchar(100) NOT NULL,`context` varchar(128) NOT NULL,`rollback_info` longblob NOT NULL,`log_status` int(11) NOT NULL,`log_created` datetime NOT NULL,`log_modified` datetime NOT NULL,`ext` varchar(100) DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;CREATE DATABASE IF NOT EXISTS `account_db`;
USE `account_db`;
DROP TABLE IF EXISTS `account_tbl`;
CREATE TABLE `account_tbl` (`id` int(11) NOT NULL AUTO_INCREMENT,`user_id` varchar(255) DEFAULT NULL,`money` int(11) DEFAULT 0,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO account_tbl (user_id, money) VALUES ('1', 10000);
DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE `undo_log` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`branch_id` bigint(20) NOT NULL,`xid` varchar(100) NOT NULL,`context` varchar(128) NOT NULL,`rollback_info` longblob NOT NULL,`log_status` int(11) NOT NULL,`log_created` datetime NOT NULL,`log_modified` datetime NOT NULL,`ext` varchar(100) DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
- 导入依赖
<dependencies><dependency><groupId>com.nie</groupId><artifactId>model</artifactId><version>0.0.1-SNAPSHOT</version></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId><version>2023.0.3.2</version></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></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>org.projectlombok</groupId><artifactId>lombok</artifactId><scope>annotationProcessor</scope></dependency></dependencies>
- 创建四个微服务项目
seata-account
启动类
package com.nie.account;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.transaction.annotation.EnableTransactionManagement;@EnableTransactionManagement
@MapperScan("com.nie.account.mapper")
@EnableDiscoveryClient
@SpringBootApplication
public class SeataAccountMainApplication {public static void main(String[] args) {SpringApplication.run(SeataAccountMainApplication.class, args);}
}
实体类
package com.nie.account.bean;import lombok.Data;import java.io.Serializable;/*** * @TableName account_tbl*/
@Data
public class AccountTbl implements Serializable {private Integer id;private String userId;private Integer money;}
控制层
package com.nie.account.controller;import com.nie.account.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
public class AccountRestController {@AutowiredAccountService accountService;/*** 扣减账户余额* @return*/@GetMapping("/debit")public String debit(@RequestParam("userId") String userId,@RequestParam("money") int money){accountService.debit(userId, money);return "account debit success";}
}
持久层
package com.nie.account.mapper;import com.nie.account.bean.AccountTbl;public interface AccountTblMapper {int deleteByPrimaryKey(Long id);int insert(AccountTbl record);int insertSelective(AccountTbl record);AccountTbl selectByPrimaryKey(Long id);int updateByPrimaryKeySelective(AccountTbl record);int updateByPrimaryKey(AccountTbl record);void debit(String userId, int money);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.nie.account.mapper.AccountTblMapper"><resultMap id="BaseResultMap" type="com.nie.account.bean.AccountTbl"><id property="id" column="id" jdbcType="INTEGER"/><result property="userId" column="user_id" jdbcType="VARCHAR"/><result property="money" column="money" jdbcType="INTEGER"/></resultMap><sql id="Base_Column_List">id,user_id,money</sql><select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">select<include refid="Base_Column_List" />from account_tblwhere id = #{id,jdbcType=INTEGER} </select><delete id="deleteByPrimaryKey" parameterType="java.lang.Long">delete from account_tblwhere id = #{id,jdbcType=INTEGER} </delete><insert id="insert" keyColumn="id" keyProperty="id" parameterType="com.nie.account.bean.AccountTbl" useGeneratedKeys="true">insert into account_tbl( id,user_id,money)values (#{id,jdbcType=INTEGER},#{userId,jdbcType=VARCHAR},#{money,jdbcType=INTEGER})</insert><insert id="insertSelective" keyColumn="id" keyProperty="id" parameterType="com.nie.account.bean.AccountTbl" useGeneratedKeys="true">insert into account_tbl<trim prefix="(" suffix=")" suffixOverrides=","><if test="id != null">id,</if><if test="userId != null">user_id,</if><if test="money != null">money,</if></trim><trim prefix="values (" suffix=")" suffixOverrides=","><if test="id != null">#{id,jdbcType=INTEGER},</if><if test="userId != null">#{userId,jdbcType=VARCHAR},</if><if test="money != null">#{money,jdbcType=INTEGER},</if></trim></insert><update id="updateByPrimaryKeySelective" parameterType="com.nie.account.bean.AccountTbl">update account_tbl<set><if test="userId != null">user_id = #{userId,jdbcType=VARCHAR},</if><if test="money != null">money = #{money,jdbcType=INTEGER},</if></set>where id = #{id,jdbcType=INTEGER} </update><update id="updateByPrimaryKey" parameterType="com.nie.account.bean.AccountTbl">update account_tblset user_id = #{userId,jdbcType=VARCHAR},money = #{money,jdbcType=INTEGER}where id = #{id,jdbcType=INTEGER} </update><update id="debit">update account_tblset money = money - #{money,jdbcType=INTEGER}where user_id = #{userId,jdbcType=VARCHAR}</update>
</mapper>
业务层
package com.nie.account.service;public interface AccountService {/*** 从用户账户中扣减* @param userId 用户id* @param money 扣减金额*/void debit(String userId, int money);
}
package com.nie.account.service.impl;import com.nie.account.mapper.AccountTblMapper;
import com.nie.account.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;@Service
public class AccountServiceImpl implements AccountService {@AutowiredAccountTblMapper accountTblMapper;@Transactional //本地事务@Overridepublic void debit(String userId, int money) {// 扣减账户余额accountTblMapper.debit(userId,money);}
}
配置application.yml
spring:application:name: seata-accountdatasource:url: jdbc:mysql://localhost:3306/account_db?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=trueusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Drivercloud:nacos:server-addr: 127.0.0.1:8848config:import-check:enabled: false
server:port: 10010mybatis:mapper-locations: classpath:mapper/*.xml
#seata:
# data-source-proxy-mode: XA
配置seata地址
service {#transaction service group mappingvgroupMapping.default_tx_group = "default"#only support when registry.type=file, please don't set multiple addressesdefault.grouplist = "127.0.0.1:8091"#degrade, current not supportenableDegrade = false#disable seatadisableGlobalTransaction = false}
seata-business
启动类
package com.nie.business;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;@EnableFeignClients(basePackages = "com.nie.business.feign")
@EnableDiscoveryClient
@SpringBootApplication
public class SeataBusinessMainApplication {public static void main(String[] args) {SpringApplication.run(SeataBusinessMainApplication.class, args);}
}
控制层
package com.nie.business.controller;import com.nie.business.service.BusinessService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
public class PurchaseRestController {@AutowiredBusinessService businessService;/*** 购买* @param userId 用户ID* @param commodityCode 商品编码* @param orderCount 数量* @return*/@GetMapping("/purchase")public String purchase(@RequestParam("userId") String userId,@RequestParam("commodityCode") String commodityCode,@RequestParam("count") int orderCount){businessService.purchase(userId, commodityCode, orderCount);return "business purchase success";}
}
远程调用
package com.nie.business.feign;import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;@FeignClient(value = "seata-order")
public interface OrderFeignClient {/*** 创建订单* @param userId* @param commodityCode* @param orderCount* @return*/@GetMapping("/create")String create(@RequestParam("userId") String userId,@RequestParam("commodityCode") String commodityCode,@RequestParam("count") int orderCount);
}
package com.nie.business.feign;import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;@FeignClient(value = "seata-storage")
public interface StorageFeignClient {/*** 扣减库存* @param commodityCode* @param count* @return*/@GetMapping("/deduct")String deduct(@RequestParam("commodityCode") String commodityCode,@RequestParam("count") Integer count);
}
业务层
package com.nie.business.service;public interface BusinessService {/*** 采购* @param userId 用户id* @param commodityCode 商品编号* @param orderCount 购买数量*/void purchase(String userId, String commodityCode, int orderCount);
}
package com.nie.business.service.impl;import com.nie.business.feign.OrderFeignClient;
import com.nie.business.feign.StorageFeignClient;
import com.nie.business.service.BusinessService;
import org.apache.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class BusinessServiceImpl implements BusinessService {@AutowiredStorageFeignClient storageFeignClient;@AutowiredOrderFeignClient orderFeignClient;@GlobalTransactional@Overridepublic void purchase(String userId, String commodityCode, int orderCount) {//1. 扣减库存storageFeignClient.deduct(commodityCode, orderCount);//2. 创建订单orderFeignClient.create(userId, commodityCode, orderCount);}
}
配置application.yml
spring:application:name: seata-businesscloud:nacos:server-addr: 127.0.0.1:8848config:import-check:enabled: false
server:port: 11000
配置seata地址
service {#transaction service group mappingvgroupMapping.default_tx_group = "default"#only support when registry.type=file, please don't set multiple addressesdefault.grouplist = "127.0.0.1:8091"#degrade, current not supportenableDegrade = false#disable seatadisableGlobalTransaction = false}
seata-order
启动类
package com.nie.order;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.transaction.annotation.EnableTransactionManagement;@EnableFeignClients(basePackages = "com.nie.order.feign")
@EnableTransactionManagement
@MapperScan("com.nie.order.mapper")
@EnableDiscoveryClient
@SpringBootApplication
public class SeataOrderMainApplication {public static void main(String[] args) {SpringApplication.run(SeataOrderMainApplication.class, args);}}
实体类
package com.nie.order.bean;import java.io.Serializable;
import lombok.Data;/*** @TableName order_tbl*/
@Data
public class OrderTbl implements Serializable {private Integer id;private String userId;private String commodityCode;private Integer count;private Integer money;private static final long serialVersionUID = 1L;
}
控制层
package com.nie.order.controller;import com.nie.order.bean.OrderTbl;
import com.nie.order.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
public class OrderRestController {@AutowiredOrderService orderService;/*** 创建订单* @param userId* @param commodityCode* @param orderCount* @return*/@GetMapping("/create")public String create(@RequestParam("userId") String userId,@RequestParam("commodityCode") String commodityCode,@RequestParam("count") int orderCount){OrderTbl tbl = orderService.create(userId, commodityCode, orderCount);return "order create success = 订单id:【"+tbl.getId()+"】";}}
业务层
package com.nie.order.service;import com.nie.order.bean.OrderTbl;public interface OrderService {/*** 创建订单* @param userId 用户id* @param commodityCode 商品编码* @param orderCount 商品数量*/OrderTbl create(String userId, String commodityCode, int orderCount);
}
package com.nie.order.service.impl;import com.nie.order.bean.OrderTbl;
import com.nie.order.feign.AccountFeignClient;
import com.nie.order.mapper.OrderTblMapper;
import com.nie.order.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;@Service
public class OrderServiceImpl implements OrderService {@AutowiredOrderTblMapper orderTblMapper;@AutowiredAccountFeignClient accountFeignClient;@Transactional@Overridepublic OrderTbl create(String userId, String commodityCode, int orderCount) {//1、计算订单价格int orderMoney = calculate(commodityCode, orderCount);//2、扣减账户余额accountFeignClient.debit(userId, orderMoney);//3、保存订单OrderTbl orderTbl = new OrderTbl();orderTbl.setUserId(userId);orderTbl.setCommodityCode(commodityCode);orderTbl.setCount(orderCount);orderTbl.setMoney(orderMoney);//3、保存订单orderTblMapper.insert(orderTbl);return orderTbl;}// 计算价格private int calculate(String commodityCode, int orderCount) {return 9*orderCount;}
}
持久层
package com.nie.order.mapper;import com.nie.order.bean.OrderTbl;/**
* @author lfy
* @description 针对表【order_tbl】的数据库操作Mapper
* @createDate 2025-01-08 18:34:18
* @Entity com.atguigu.order.bean.OrderTbl
*/
public interface OrderTblMapper {int deleteByPrimaryKey(Long id);int insert(OrderTbl record);int insertSelective(OrderTbl record);OrderTbl selectByPrimaryKey(Long id);int updateByPrimaryKeySelective(OrderTbl record);int updateByPrimaryKey(OrderTbl record);}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.nie.order.mapper.OrderTblMapper"><resultMap id="BaseResultMap" type="com.nie.order.bean.OrderTbl"><id property="id" column="id" jdbcType="INTEGER"/><result property="userId" column="user_id" jdbcType="VARCHAR"/><result property="commodityCode" column="commodity_code" jdbcType="VARCHAR"/><result property="count" column="count" jdbcType="INTEGER"/><result property="money" column="money" jdbcType="INTEGER"/></resultMap><sql id="Base_Column_List">id,user_id,commodity_code,count,money</sql><select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">select<include refid="Base_Column_List" />from order_tblwhere id = #{id,jdbcType=INTEGER} </select><delete id="deleteByPrimaryKey" parameterType="java.lang.Long">delete from order_tblwhere id = #{id,jdbcType=INTEGER} </delete><insert id="insert" keyColumn="id" keyProperty="id" parameterType="com.nie.order.bean.OrderTbl"useGeneratedKeys="true">insert into order_tbl( id,user_id,commodity_code,count,money)values (#{id,jdbcType=INTEGER},#{userId,jdbcType=VARCHAR},#{commodityCode,jdbcType=VARCHAR},#{count,jdbcType=INTEGER},#{money,jdbcType=INTEGER})</insert><insert id="insertSelective" keyColumn="id" keyProperty="id" parameterType="com.nie.order.bean.OrderTbl" useGeneratedKeys="true">insert into order_tbl<trim prefix="(" suffix=")" suffixOverrides=","><if test="id != null">id,</if><if test="userId != null">user_id,</if><if test="commodityCode != null">commodity_code,</if><if test="count != null">count,</if><if test="money != null">money,</if></trim><trim prefix="values (" suffix=")" suffixOverrides=","><if test="id != null">#{id,jdbcType=INTEGER},</if><if test="userId != null">#{userId,jdbcType=VARCHAR},</if><if test="commodityCode != null">#{commodityCode,jdbcType=VARCHAR},</if><if test="count != null">#{count,jdbcType=INTEGER},</if><if test="money != null">#{money,jdbcType=INTEGER},</if></trim></insert><update id="updateByPrimaryKeySelective" parameterType="com.nie.order.bean.OrderTbl">update order_tbl<set><if test="userId != null">user_id = #{userId,jdbcType=VARCHAR},</if><if test="commodityCode != null">commodity_code = #{commodityCode,jdbcType=VARCHAR},</if><if test="count != null">count = #{count,jdbcType=INTEGER},</if><if test="money != null">money = #{money,jdbcType=INTEGER},</if></set>where id = #{id,jdbcType=INTEGER} </update><update id="updateByPrimaryKey" parameterType="com.nie.order.bean.OrderTbl">update order_tblset user_id = #{userId,jdbcType=VARCHAR},commodity_code = #{commodityCode,jdbcType=VARCHAR},count = #{count,jdbcType=INTEGER},money = #{money,jdbcType=INTEGER}where id = #{id,jdbcType=INTEGER} </update>
</mapper>
配置application.yml
spring:application:name: seata-orderdatasource:url: jdbc:mysql://localhost:3306/order_db?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=trueusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Drivercloud:nacos:server-addr: 127.0.0.1:8848config:import-check:enabled: falseserver:port: 12000
mybatis:mapper-locations: classpath:mapper/*.xml
配置seata地址
service {#transaction service group mappingvgroupMapping.default_tx_group = "default"#only support when registry.type=file, please don't set multiple addressesdefault.grouplist = "127.0.0.1:8091"#degrade, current not supportenableDegrade = false#disable seatadisableGlobalTransaction = false}
seata-storage
启动类
package com.nie.storage;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.transaction.annotation.EnableTransactionManagement;@EnableTransactionManagement
@MapperScan("com.nie.storage.mapper")
@EnableDiscoveryClient
@SpringBootApplication
public class SeataStorageMainApplication {public static void main(String[] args) {SpringApplication.run(SeataStorageMainApplication.class, args);}
}
实体类
package com.nie.storage.bean;import java.io.Serializable;
import lombok.Data;/*** @TableName storage_tbl*/
@Data
public class StorageTbl implements Serializable {private Integer id;private String commodityCode;private Integer count;private static final long serialVersionUID = 1L;
}
控制层
package com.nie.storage.controller;import com.nie.storage.service.StorageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
public class StorageRestController {@AutowiredStorageService storageService;@GetMapping("/deduct")public String deduct(@RequestParam("commodityCode") String commodityCode,@RequestParam("count") Integer count) {storageService.deduct(commodityCode, count);return "storage deduct success";}
}
业务层
package com.nie.storage.service;public interface StorageService {/*** 扣除存储数量* @param commodityCode 商品编码* @param count 数量*/void deduct(String commodityCode, int count);
}
package com.nie.storage.service.impl;import com.nie.storage.mapper.StorageTblMapper;
import com.nie.storage.service.StorageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;@Service
public class StorageServiceImpl implements StorageService {@AutowiredStorageTblMapper storageTblMapper;@Transactional@Overridepublic void deduct(String commodityCode, int count) {storageTblMapper.deduct(commodityCode, count);if (count == 5) {throw new RuntimeException("库存不足");}}
}
持久层
package com.nie.storage.mapper;import com.nie.storage.bean.StorageTbl;/**
* @author lfy
* @description 针对表【storage_tbl】的数据库操作Mapper
* @Entity com.atguigu.storage.bean.StorageTbl
*/
public interface StorageTblMapper {int deleteByPrimaryKey(Long id);int insert(StorageTbl record);int insertSelective(StorageTbl record);StorageTbl selectByPrimaryKey(Long id);int updateByPrimaryKeySelective(StorageTbl record);int updateByPrimaryKey(StorageTbl record);void deduct(String commodityCode, int count);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.nie.storage.mapper.StorageTblMapper"><resultMap id="BaseResultMap" type="com.nie.storage.bean.StorageTbl"><id property="id" column="id" jdbcType="INTEGER"/><result property="commodityCode" column="commodity_code" jdbcType="VARCHAR"/><result property="count" column="count" jdbcType="INTEGER"/></resultMap><sql id="Base_Column_List">id,commodity_code,count</sql><select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">select<include refid="Base_Column_List" />from storage_tblwhere id = #{id,jdbcType=INTEGER} </select><delete id="deleteByPrimaryKey" parameterType="java.lang.Long">delete from storage_tblwhere id = #{id,jdbcType=INTEGER} </delete><insert id="insert" keyColumn="id" keyProperty="id" parameterType="com.nie.storage.bean.StorageTbl" useGeneratedKeys="true">insert into storage_tbl( id,commodity_code,count)values (#{id,jdbcType=INTEGER},#{commodityCode,jdbcType=VARCHAR},#{count,jdbcType=INTEGER})</insert><insert id="insertSelective" keyColumn="id" keyProperty="id" parameterType="com.nie.storage.bean.StorageTbl" useGeneratedKeys="true">insert into storage_tbl<trim prefix="(" suffix=")" suffixOverrides=","><if test="id != null">id,</if><if test="commodityCode != null">commodity_code,</if><if test="count != null">count,</if></trim><trim prefix="values (" suffix=")" suffixOverrides=","><if test="id != null">#{id,jdbcType=INTEGER},</if><if test="commodityCode != null">#{commodityCode,jdbcType=VARCHAR},</if><if test="count != null">#{count,jdbcType=INTEGER},</if></trim></insert><update id="updateByPrimaryKeySelective" parameterType="com.nie.storage.bean.StorageTbl">update storage_tbl<set><if test="commodityCode != null">commodity_code = #{commodityCode,jdbcType=VARCHAR},</if><if test="count != null">count = #{count,jdbcType=INTEGER},</if></set>where id = #{id,jdbcType=INTEGER} </update><update id="updateByPrimaryKey" parameterType="com.nie.storage.bean.StorageTbl">update storage_tblset commodity_code = #{commodityCode,jdbcType=VARCHAR},count = #{count,jdbcType=INTEGER}where id = #{id,jdbcType=INTEGER} </update><update id="deduct">update storage_tblset count = count - #{count}where commodity_code = #{commodityCode}</update>
</mapper>
配置application.yml
spring:application:name: seata-storagedatasource:url: jdbc:mysql://localhost:3306/storage_db?useUnicode=true&characterEncoding=utf-8&useSSL=falseusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Drivercloud:nacos:server-addr: 127.0.0.1:8848config:import-check:enabled: false
server:port: 13000mybatis:mapper-locations: classpath:mapper/*.xml
配置seata地址
service {#transaction service group mappingvgroupMapping.default_tx_group = "default"#only support when registry.type=file, please don't set multiple addressesdefault.grouplist = "127.0.0.1:8091"#degrade, current not supportenableDegrade = false#disable seatadisableGlobalTransaction = false}
二阶提交协议
- 一阶:本地事物提交
(业务数据+undo_log)
会添加一个全局锁(数据级别) - 二阶事物:成功或者失败:
成功:所有微服务删除undo_log
失败:所有人拿到自己的前镜像(数据没做更改之前的数据),恢复到之前的数据,删除undo_log
当我们发送一个全局事物的时候
我们可以看到他添加了全局锁
我们的数据库发生了改变
我们的undo_log也有记录
但是当某一个事物出错时 那么发生回滚 数据恢复到之前没有修改的时候
undo_log被删除
四种事物模式
- AT模式
AT模式是Seata框架的默认事务模式,它通过记录SQL执行前后的数据快照(undo log),在需要回滚时,根据这些数据快照进行反向补偿,从而实现事务的原子性和一致性。 - XA模式
XA模式基于X/Open XA规范实现,依赖于数据库对XA协议的支持。它通过两阶段提交(2PC)来保证分布式事务的原子性和一致性。 - TCC模式
TCC模式是一种基于业务逻辑的分布式事务解决方案,它将事务分为三个阶段:尝试(Try)、确认(Confirm)和取消(Cancel)。 - Sage模式
SAGA模式是一种长事务解决方案,它将一个长事务拆分为多个本地事务,并通过事件驱动的方式进行协调。