SpringBoot分布式事務(wù)整合Seata
點擊上方藍色字體,選擇“標星公眾號”
優(yōu)質(zhì)文章,第一時間送達
1. 適用場景
一個SpringBoot的單體項目整合多個關(guān)系型數(shù)據(jù)庫。多數(shù)據(jù)源。

2. Seata 地址
http://seata.io/zh-cn/docs/overview/what-is-seata.html
3.案例說明
模擬一個用戶下訂單場景。
創(chuàng)建三個數(shù)據(jù)庫:用戶庫、商品庫、訂單庫。
SpringBoot 項目配置三個數(shù)據(jù)庫。
訂單controller–>訂單service(調(diào)用商品service、用戶service),各自service在調(diào)用各自的dao層
4.項目結(jié)構(gòu)展示

5. 代碼實現(xiàn)
1.pom.xml 文件
"1.0"?encoding="UTF-8"?>
"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?https://maven.apache.org/xsd/maven-4.0.0.xsd">
????4.0.0
????com.zlw
????seata-distributed-transaction
????0.0.1-SNAPSHOT
????seata-distributed-transaction
????springboot項目測試seata分布式事務(wù)
????
????????1.8
????????UTF-8
????????UTF-8
????????2.3.0.RELEASE
????????2.2.1.RELEASE
????
????
????????
????????????org.springframework.boot
????????????spring-boot-starter-web
????????
????????
????????????org.springframework.boot
????????????spring-boot-starter-actuator
????????
????????
????????????org.springframework.boot
????????????spring-boot-starter-test
????????????test
????????????
????????????????
????????????????????org.junit.vintage
????????????????????junit-vintage-engine
????????????????
????????????
????????
????????
????????????org.projectlombok
????????????lombok
????????
????????
????????
????????????mysql
????????????mysql-connector-java
????????
????????
????????
????????????org.mybatis.spring.boot
????????????mybatis-spring-boot-starter
????????????2.1.3
????????
????????
????????
????????????io.seata
????????????seata-spring-boot-starter
????????????1.3.0
????????
????????
????????
????????
????????????com.baomidou
????????????dynamic-datasource-spring-boot-starter
????????????3.2.0
????????
????????
????????
????????????com.alibaba.nacos
????????????nacos-client
????????????1.3.1
????????
????
????
????????
????????????
????????????????org.springframework.boot
????????????????spring-boot-dependencies
????????????????${spring-boot.version}
????????????????<type>pomtype>
????????????????import
????????????
????????????
????????????????com.alibaba.cloud
????????????????spring-cloud-alibaba-dependencies
????????????????${spring-cloud-alibaba.version}
????????????????<type>pomtype>
????????????????import
????????????
????????
????
????
????
????????
????????????
????????????????org.springframework.boot
????????????????spring-boot-maven-plugin
????????????????
????????????????????
????????????????????????
????????????????????????????org.projectlombok
????????????????????????????lombok
????????????????????????
????????????????????
????????????????
????????????
????????
????????
????????
????????????
????????????????src/main/java
????????????????
????????????????????**/*.xml
????????????????
????????????
????????????
????????????????src/main/resources
????????????????
????????????????????**/*.*
????????????????
????????????
????????
????
yml 配置文件
#?springboot?整合單體?TC?server?配置。
server:
??port:?18080
seata:
??config:
????type:?file
??application-id:?springboot-seata
??#??enable-auto-data-source-proxy:?false
??registry:
????type:?file
??#????nacos:
??#??????application:?seata-server
??#??????cluster:?default
??#??????group:?SEATA_GROUP
??#??????server-addr:?127.0.0.1:8801,127.0.0.1:8802,127.0.0.1:8803?#192.168.172.128:8848
??#??????namespace:?le_zi_jie
??#????type:?nacos
??#??service:
??#????vgroup-mapping:
??#??????springboot-seata-group:?default
??service:
????grouplist:
??????default:?127.0.0.1:8091
????vgroup-mapping:
??????springboot-seata-group:?default
??#?seata?事務(wù)組編號?用于TC集群名
??tx-service-group:?springboot-seata-group
spring:
??application:
????name:?seata-distributed-transaction
??datasource:
????dynamic:
??????datasource:
????????#?設(shè)置?賬號數(shù)據(jù)源配置
????????account-ds:
??????????driver-class-name:?com.mysql.cj.jdbc.Driver
??????????password:?123456
??????????url:?jdbc:mysql://127.0.0.1:3306/accountdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false
??????????username:?root
??????????#?設(shè)置?訂單數(shù)據(jù)源配置
????????order-ds:
??????????driver-class-name:?com.mysql.cj.jdbc.Driver
??????????password:?123456
??????????url:?jdbc:mysql://127.0.0.1:3306/orderdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false
??????????username:?root
??????????#?設(shè)置商品?數(shù)據(jù)源配置
????????product-ds:
??????????driver-class-name:?com.mysql.cj.jdbc.Driver
??????????password:?123456
??????????url:?jdbc:mysql://127.0.0.1:3306/productdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false
??????????username:?root
??????#?設(shè)置默認數(shù)據(jù)源或者數(shù)據(jù)源組?默認值即為master
??????primary:?order-ds???#?默認指定一個數(shù)據(jù)源
??????#?開啟對?seata的支持
??????seata:?true
3.mapper 文件
3.1 product mapper
package?com.zlw.seata.mapper;
import?com.zlw.seata.mode.Product;
import?org.apache.ibatis.annotations.Mapper;
import?org.apache.ibatis.annotations.Param;
@Mapper
public?interface?ProductMapper?{
????int?deleteByPrimaryKey(Integer?id);
????int?insert(Product?record);
????int?insertSelective(Product?record);
????Product?selectByPrimaryKey(Integer?id);
????int?updateByPrimaryKeySelective(Product?record);
????int?updateByPrimaryKey(Product?record);
????int?reduceStock(@Param("productId")?Integer?productId,?@Param("amount")?Integer?amount);
}
"1.0"?encoding="UTF-8"?>
"-//mybatis.org//DTD?Mapper?3.0//EN"?"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
"com.zlw.seata.mapper.ProductMapper">
????"BaseResultMap"?type="com.zlw.seata.mode.Product">
????????"id"?jdbcType="INTEGER"?property="id"/>
????????"name"?jdbcType="VARCHAR"?property="name"/>
????????"price"?jdbcType="DECIMAL"?property="price"/>
????????"stock"?jdbcType="INTEGER"?property="stock"/>
????????"add_time"?jdbcType="TIMESTAMP"?property="addTime"/>
????????"update_time"?jdbcType="TIMESTAMP"?property="updateTime"/>
????
????"Base_Column_List">
????id,?name,?price,?stock,?add_time,?update_time
??
????
????"deleteByPrimaryKey"?parameterType="java.lang.Integer">
????delete?from?product
????where?id?=?#{id,jdbcType=INTEGER}
??
????"insert"?parameterType="com.zlw.seata.mode.Product">
????insert?into?product?(id,?name,?price,?
??????stock,?add_time,?update_time
??????)
????values?(#{id,jdbcType=INTEGER},?#{name,jdbcType=VARCHAR},?#{price,jdbcType=DECIMAL},?
??????#{stock,jdbcType=INTEGER},?#{addTime,jdbcType=TIMESTAMP},?#{updateTime,jdbcType=TIMESTAMP}
??????)
??
????"insertSelective"?parameterType="com.zlw.seata.mode.Product">
????????insert?into?product
????????"("?suffix=")"?suffixOverrides=",">
????????????<if?test="id?!=?null">
????????????????id,
????????????if>
????????????<if?test="name?!=?null">
????????????????name,
????????????if>
????????????<if?test="price?!=?null">
????????????????price,
????????????if>
????????????<if?test="stock?!=?null">
????????????????stock,
????????????if>
????????????<if?test="addTime?!=?null">
????????????????add_time,
????????????if>
????????????<if?test="updateTime?!=?null">
????????????????update_time,
????????????if>
????????
????????"values?("?suffix=")"?suffixOverrides=",">
????????????<if?test="id?!=?null">
????????????????#{id,jdbcType=INTEGER},
????????????if>
????????????<if?test="name?!=?null">
????????????????#{name,jdbcType=VARCHAR},
????????????if>
????????????<if?test="price?!=?null">
????????????????#{price,jdbcType=DECIMAL},
????????????if>
????????????<if?test="stock?!=?null">
????????????????#{stock,jdbcType=INTEGER},
????????????if>
????????????<if?test="addTime?!=?null">
????????????????#{addTime,jdbcType=TIMESTAMP},
????????????if>
????????????<if?test="updateTime?!=?null">
????????????????#{updateTime,jdbcType=TIMESTAMP},
????????????if>
????????
????
????"updateByPrimaryKeySelective"?parameterType="com.zlw.seata.mode.Product">
????????update?product
????????<set>
????????????<if?test="name?!=?null">
????????????????name?=?#{name,jdbcType=VARCHAR},
????????????if>
????????????<if?test="price?!=?null">
????????????????price?=?#{price,jdbcType=DECIMAL},
????????????if>
????????????<if?test="stock?!=?null">
????????????????stock?=?#{stock,jdbcType=INTEGER},
????????????if>
????????????<if?test="addTime?!=?null">
????????????????add_time?=?#{addTime,jdbcType=TIMESTAMP},
????????????if>
????????????<if?test="updateTime?!=?null">
????????????????update_time?=?#{updateTime,jdbcType=TIMESTAMP},
????????????if>
????????set>
????????where?id?=?#{id,jdbcType=INTEGER}
????
????"updateByPrimaryKey"?parameterType="com.zlw.seata.mode.Product">
????update?product
????set?name?=?#{name,jdbcType=VARCHAR},
??????price?=?#{price,jdbcType=DECIMAL},
??????stock?=?#{stock,jdbcType=INTEGER},
??????add_time?=?#{addTime,jdbcType=TIMESTAMP},
??????update_time?=?#{updateTime,jdbcType=TIMESTAMP}
????where?id?=?#{id,jdbcType=INTEGER}
??
????
????"reduceStock">
????update?product?SET?stock?=?stock?-?#{amount,?jdbcType=INTEGER}
????WHERE?id?=?#{productId,?jdbcType=INTEGER}?AND?stock?>=?#{amount,?jdbcType=INTEGER}
??
3.2 OrdersMapper
package?com.zlw.seata.mapper;
import?com.zlw.seata.mode.Orders;
import?org.apache.ibatis.annotations.Mapper;
@Mapper
public?interface?OrdersMapper?{
????int?deleteByPrimaryKey(Integer?id);
????int?insert(Orders?record);
????int?insertSelective(Orders?record);
????Orders?selectByPrimaryKey(Integer?id);
????int?updateByPrimaryKeySelective(Orders?record);
????int?updateByPrimaryKey(Orders?record);
}
"1.0"?encoding="UTF-8"?>
"-//mybatis.org//DTD?Mapper?3.0//EN"?"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
"com.zlw.seata.mapper.OrdersMapper">
??"BaseResultMap"?type="com.zlw.seata.mode.Orders">
????"id"?jdbcType="INTEGER"?property="id"?/>
????"user_id"?jdbcType="INTEGER"?property="userId"?/>
????"product_id"?jdbcType="INTEGER"?property="productId"?/>
????"pay_amount"?jdbcType="DECIMAL"?property="payAmount"?/>
????"add_time"?jdbcType="TIMESTAMP"?property="addTime"?/>
????"update_time"?jdbcType="TIMESTAMP"?property="updateTime"?/>
??
??"Base_Column_List">
????id,?user_id,?product_id,?pay_amount,?add_time,?update_time
??
??
??"deleteByPrimaryKey"?parameterType="java.lang.Integer">
????delete?from?orders
????where?id?=?#{id,jdbcType=INTEGER}
??
??"insert"?parameterType="com.zlw.seata.mode.Orders">
????insert?into?orders?(id,?user_id,?product_id,?
??????pay_amount,?add_time,?update_time
??????)
????values?(#{id,jdbcType=INTEGER},?#{userId,jdbcType=INTEGER},?#{productId,jdbcType=INTEGER},?
??????#{payAmount,jdbcType=DECIMAL},?#{addTime,jdbcType=TIMESTAMP},?#{updateTime,jdbcType=TIMESTAMP}
??????)
??
??"insertSelective"?parameterType="com.zlw.seata.mode.Orders">
????insert?into?orders
????"("?suffix=")"?suffixOverrides=",">
??????<if?test="id?!=?null">
????????id,
??????if>
??????<if?test="userId?!=?null">
????????user_id,
??????if>
??????<if?test="productId?!=?null">
????????product_id,
??????if>
??????<if?test="payAmount?!=?null">
????????pay_amount,
??????if>
??????<if?test="addTime?!=?null">
????????add_time,
??????if>
??????<if?test="updateTime?!=?null">
????????update_time,
??????if>
????
????"values?("?suffix=")"?suffixOverrides=",">
??????<if?test="id?!=?null">
????????#{id,jdbcType=INTEGER},
??????if>
??????<if?test="userId?!=?null">
????????#{userId,jdbcType=INTEGER},
??????if>
??????<if?test="productId?!=?null">
????????#{productId,jdbcType=INTEGER},
??????if>
??????<if?test="payAmount?!=?null">
????????#{payAmount,jdbcType=DECIMAL},
??????if>
??????<if?test="addTime?!=?null">
????????#{addTime,jdbcType=TIMESTAMP},
??????if>
??????<if?test="updateTime?!=?null">
????????#{updateTime,jdbcType=TIMESTAMP},
??????if>
????
??
??"updateByPrimaryKeySelective"?parameterType="com.zlw.seata.mode.Orders">
????update?orders
????<set>
??????<if?test="userId?!=?null">
????????user_id?=?#{userId,jdbcType=INTEGER},
??????if>
??????<if?test="productId?!=?null">
????????product_id?=?#{productId,jdbcType=INTEGER},
??????if>
??????<if?test="payAmount?!=?null">
????????pay_amount?=?#{payAmount,jdbcType=DECIMAL},
??????if>
??????<if?test="addTime?!=?null">
????????add_time?=?#{addTime,jdbcType=TIMESTAMP},
??????if>
??????<if?test="updateTime?!=?null">
????????update_time?=?#{updateTime,jdbcType=TIMESTAMP},
??????if>
????set>
????where?id?=?#{id,jdbcType=INTEGER}
??
??"updateByPrimaryKey"?parameterType="com.zlw.seata.mode.Orders">
????update?orders
????set?user_id?=?#{userId,jdbcType=INTEGER},
??????product_id?=?#{productId,jdbcType=INTEGER},
??????pay_amount?=?#{payAmount,jdbcType=DECIMAL},
??????add_time?=?#{addTime,jdbcType=TIMESTAMP},
??????update_time?=?#{updateTime,jdbcType=TIMESTAMP}
????where?id?=?#{id,jdbcType=INTEGER}
??
3.3 AccountMapper
package?com.zlw.seata.mapper;
import?com.zlw.seata.mode.Account;
import?org.apache.ibatis.annotations.Mapper;
import?org.apache.ibatis.annotations.Param;
import?java.math.BigDecimal;
@Mapper
public?interface?AccountMapper?{
????int?deleteByPrimaryKey(Integer?id);
????int?insert(Account?record);
????int?insertSelective(Account?record);
????Account?selectByPrimaryKey(Integer?id);
????Account?selectAccountByUserId(Integer?userId);
????int?updateByPrimaryKeySelective(Account?record);
????int?updateByPrimaryKey(Account?record);
????int?reduceBalance(@Param("userId")?Integer?userId,?@Param("money")?BigDecimal?money);
}
"1.0"?encoding="UTF-8"?>
"-//mybatis.org//DTD?Mapper?3.0//EN"?"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
"com.zlw.seata.mapper.AccountMapper">
????"BaseResultMap"?type="com.zlw.seata.mode.Account">
????????"id"?jdbcType="INTEGER"?property="id"/>
????????"user_id"?jdbcType="INTEGER"?property="userId"/>
????????"balance"?jdbcType="DECIMAL"?property="balance"/>
????????"update_time"?jdbcType="TIMESTAMP"?property="updateTime"/>
????
????"Base_Column_List">
????id,?user_id,?balance,?update_time
??
????
????
????
????"deleteByPrimaryKey"?parameterType="java.lang.Integer">
????delete?from?account
????where?id?=?#{id,jdbcType=INTEGER}
??
????"insert"?parameterType="com.zlw.seata.mode.Account">
????insert?into?account?(id,?user_id,?balance,?
??????update_time)
????values?(#{id,jdbcType=INTEGER},?#{userId,jdbcType=INTEGER},?#{balance,jdbcType=DOUBLE},?
??????#{updateTime,jdbcType=TIMESTAMP})
??
????"insertSelective"?parameterType="com.zlw.seata.mode.Account">
????????insert?into?account
????????"("?suffix=")"?suffixOverrides=",">
????????????<if?test="id?!=?null">
????????????????id,
????????????if>
????????????<if?test="userId?!=?null">
????????????????user_id,
????????????if>
????????????<if?test="balance?!=?null">
????????????????balance,
????????????if>
????????????<if?test="updateTime?!=?null">
????????????????update_time,
????????????if>
????????
????????"values?("?suffix=")"?suffixOverrides=",">
????????????<if?test="id?!=?null">
????????????????#{id,jdbcType=INTEGER},
????????????if>
????????????<if?test="userId?!=?null">
????????????????#{userId,jdbcType=INTEGER},
????????????if>
????????????<if?test="balance?!=?null">
????????????????#{balance,jdbcType=DOUBLE},
????????????if>
????????????<if?test="updateTime?!=?null">
????????????????#{updateTime,jdbcType=TIMESTAMP},
????????????if>
????????
????
????"updateByPrimaryKeySelective"?parameterType="com.zlw.seata.mode.Account">
????????update?account
????????<set>
????????????<if?test="userId?!=?null">
????????????????user_id?=?#{userId,jdbcType=INTEGER},
????????????if>
????????????<if?test="balance?!=?null">
????????????????balance?=?#{balance,jdbcType=DOUBLE},
????????????if>
????????????<if?test="updateTime?!=?null">
????????????????update_time?=?#{updateTime,jdbcType=TIMESTAMP},
????????????if>
????????set>
????????where?id?=?#{id,jdbcType=INTEGER}
????
???"updateByPrimaryKey"?parameterType="com.zlw.seata.mode.Account">
????update?account
????set?user_id?=?#{userId,jdbcType=INTEGER},
??????balance?=?#{balance,jdbcType=DOUBLE},
??????update_time?=?#{updateTime,jdbcType=TIMESTAMP}
????where?id?=?#{id,jdbcType=INTEGER}
??
????
????"reduceBalance">
????update?account
????????SET?balance?=?balance?-?#{money}
????WHERE?user_id?=?#{userId,?jdbcType=INTEGER}
????????AND?balance?>=?${money}
??
4.service
4.1 ProductService
package?com.zlw.seata.service;
import?com.zlw.seata.mode.Product;
public?interface?ProductService?{
????/**
?????*?減庫存
?????*
?????*?@param?productId?商品?ID
?????*?@param?amount????扣減數(shù)量
?????*?@throws?Exception?扣減失敗時拋出異常
?????*/
????Product?reduceStock(Integer?productId,?Integer?amount)?throws?Exception;
}
package?com.zlw.seata.service.impl;
import?com.baomidou.dynamic.datasource.annotation.DS;
import?com.zlw.seata.mapper.ProductMapper;
import?com.zlw.seata.mode.Product;
import?com.zlw.seata.service.ProductService;
import?io.seata.core.context.RootContext;
import?lombok.extern.slf4j.Slf4j;
import?org.springframework.beans.factory.annotation.Autowired;
import?org.springframework.stereotype.Service;
@Slf4j
@Service
public?class?ProductServiceImpl?implements?ProductService?{
????@Autowired
????private?ProductMapper?productMapper;
????@Override
????@DS(value?=?"product-ds")
????public?Product?reduceStock(Integer?productId,?Integer?amount)?throws?Exception?{
????????log.info("當前?XID:?{}",?RootContext.getXID());
????????//?檢查庫存
????????Product?product?=?productMapper.selectByPrimaryKey(productId);
????????if?(product.getStock()?????????????throw?new?Exception("庫存不足");
????????}
????????//?扣減庫存
????????int?updateCount?=?productMapper.reduceStock(productId,?amount);
????????//?扣除成功
????????if?(updateCount?==?0)?{
????????????throw?new?Exception("庫存不足");
????????}
????????//?扣除成功
????????log.info("扣除?{}?庫存成功",?productId);
????????return?product;
????}
}
4.2 OrderService
package?com.zlw.seata.service;
public?interface?OrderService?{
????/**
?????*?下訂單
?????*
?????*?@param?userId?用戶id
?????*?@param?productId?產(chǎn)品id
?????*?@return?訂單id
?????*?@throws?Exception?創(chuàng)建訂單失敗,拋出異常
?????*/
????Integer?createOrder(Integer?userId,?Integer?productId)?throws?Exception;
}
package?com.zlw.seata.service.impl;
import?com.baomidou.dynamic.datasource.annotation.DS;
import?com.zlw.seata.mapper.OrdersMapper;
import?com.zlw.seata.mode.Orders;
import?com.zlw.seata.mode.Product;
import?com.zlw.seata.service.AccountService;
import?com.zlw.seata.service.OrderService;
import?com.zlw.seata.service.ProductService;
import?io.seata.core.context.RootContext;
import?io.seata.spring.annotation.GlobalTransactional;
import?lombok.extern.slf4j.Slf4j;
import?org.springframework.beans.factory.annotation.Autowired;
import?org.springframework.stereotype.Service;
import?java.math.BigDecimal;
@Slf4j
@Service
public?class?OrderServiceImpl?implements?OrderService?{
????@Autowired
????private?OrdersMapper?ordersMapper;
????@Autowired
????private?AccountService?accountService;
????@Autowired
????private?ProductService?productService;
????@Override
????@DS(value?=?"order-ds")
????@GlobalTransactional?//seata全局事務(wù)注解
????public?Integer?createOrder(Integer?userId,?Integer?productId)?throws?Exception?{
????????Integer?amount?=?1;?//?購買數(shù)量暫時設(shè)置為?1
????????log.info("當前?XID:?{}",?RootContext.getXID());
????????//?減庫存?-?遠程服務(wù)
????????Product?product?=?productService.reduceStock(productId,?amount);
????????//?減余額?-?遠程服務(wù)
????????accountService.reduceBalance(userId,?product.getPrice());
????????//?下訂單?-?本地下訂單
????????Orders?order?=?new?Orders();
????????order.setUserId(userId);
????????order.setProductId(productId);
????????order.setPayAmount(product.getPrice().multiply(new?BigDecimal(amount)));
????????ordersMapper.insertSelective(order);
????????log.info("下訂單:?{}",?order.getId());
????????//?返回訂單編號
????????return?order.getId();
????}
}
4.3 AccountService
package?com.zlw.seata.service;
import?java.math.BigDecimal;
public?interface?AccountService?{
????/**
?????*?減余額
?????*
?????*?@param?userId?用戶id
?????*?@param?money??扣減金額
?????*?@throws?Exception?失敗時拋出異常
?????*/
????void?reduceBalance(Integer?userId,?BigDecimal?money)?throws?Exception;
}
package?com.zlw.seata.service.impl;
import?com.baomidou.dynamic.datasource.annotation.DS;
import?com.zlw.seata.mapper.AccountMapper;
import?com.zlw.seata.mode.Account;
import?com.zlw.seata.service.AccountService;
import?io.seata.core.context.RootContext;
import?lombok.extern.slf4j.Slf4j;
import?org.springframework.beans.factory.annotation.Autowired;
import?org.springframework.stereotype.Service;
import?java.math.BigDecimal;
@Slf4j
@Service
public?class?AccountServiceImpl?implements?AccountService?{
????@Autowired
????private?AccountMapper?accountMapper;
????@Override
????@DS(value?=?"account-ds")
????public?void?reduceBalance(Integer?userId,?BigDecimal?money)?throws?Exception?{
????????log.info("當前?XID:?{}",?RootContext.getXID());
????????//?檢查余額
????????Account?account?=?accountMapper.selectAccountByUserId(userId);
????????if?(account.getBalance().doubleValue()?????????????throw?new?Exception("余額不足");
????????}
????????//?扣除余額
????????int?updateCount?=?accountMapper.reduceBalance(userId,?money);
????????//?扣除成功
????????if?(updateCount?==?0)?{
????????????throw?new?Exception("余額不足");
????????}
????????log.info("扣除用戶?{}?余額成功",?userId);
????}
}
5 controller 調(diào)用
package?com.zlw.seata.contorller;
import?com.zlw.seata.service.OrderService;
import?lombok.extern.slf4j.Slf4j;
import?org.springframework.beans.factory.annotation.Autowired;
import?org.springframework.web.bind.annotation.RequestMapping;
import?org.springframework.web.bind.annotation.RequestParam;
import?org.springframework.web.bind.annotation.RestController;
@Slf4j?//lombok
@RestController
public?class?OrderController?{
????@Autowired
????private?OrderService?orderService;
????@RequestMapping("/order")
????public?Integer?createOrder(@RequestParam("userId")?Integer?userId,
???????????????????????????????@RequestParam("productId")?Integer?productId)?throws?Exception?{
????????log.info("請求下單,?用戶:{},?商品:{}",?userId,?productId);
????????return?orderService.createOrder(userId,?productId);
????}
}
6.表數(shù)據(jù)
CREATE?TABLE?`account`?(
??`id`?int(11)?NOT?NULL?AUTO_INCREMENT,
??`user_id`?int(11)?DEFAULT?NULL,
??`balance`?int(11)?DEFAULT?NULL,
??`update_time`?datetime?DEFAULT?NULL,
??PRIMARY?KEY?(`id`)
)?ENGINE=InnoDB?AUTO_INCREMENT=2?DEFAULT?CHARSET=utf8mb4;
CREATE?TABLE?`orders`?(
??`id`?int(11)?NOT?NULL?AUTO_INCREMENT,
??`user_id`?int(11)?DEFAULT?NULL,
??`product_id`?int(11)?DEFAULT?NULL,
??`pay_amount`?int(11)?DEFAULT?NULL,
??`add_time`?datetime?DEFAULT?NULL,
??`update_time`?datetime?DEFAULT?NULL,
??PRIMARY?KEY?(`id`)
)?ENGINE=InnoDB?AUTO_INCREMENT=50?DEFAULT?CHARSET=utf8mb4;
CREATE?TABLE?`product`?(
??`id`?int(11)?NOT?NULL?AUTO_INCREMENT,
??`name`?varchar(255)?DEFAULT?NULL,
??`price`?int(11)?DEFAULT?NULL,
??`stock`?int(11)?DEFAULT?NULL,
??`add_time`?datetime?DEFAULT?NULL,
??`update_time`?datetime?DEFAULT?NULL,
??PRIMARY?KEY?(`id`)
)?ENGINE=InnoDB?AUTO_INCREMENT=2?DEFAULT?CHARSET=utf8mb4;
account?表:
INSERT?INTO?`account`?(`id`,?`user_id`,?`balance`,?`update_time`)?VALUES?('1',?'1',?'200',?'2021-01-15?00:02:17');
product?表:
INSERT?INTO?`product`?(`id`,?`name`,?`price`,?`stock`,?`add_time`,?`update_time`)?VALUES?('1',?'電池',?'10',?'67',?'2021-01-15?00:00:32',?'2021-01-15?00:00:35');
每個庫都需要有一個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,
??PRIMARY?KEY?(`id`),
??UNIQUE?KEY?`ux_undo_log`?(`xid`,`branch_id`)
)?ENGINE=InnoDB?AUTO_INCREMENT=137?DEFAULT?CHARSET=utf8;
7.seata的配置
單體SpringBoot使用默認的文件進行seata TCServer的日志記錄(集群的時候使用MySQL),此處也可以使用MySQL。
##?transaction?log?store,?only?used?in?seata-server
store?{
??##?store?mode:?file、db、redis
??mode?=?"file"
??##?file?store?property
??file?{
????##?store?location?dir
????dir?=?"sessionStore"
????#?branch?session?size?,?if?exceeded?first?try?compress?lockkey,?still?exceeded?throws?exceptions
????maxBranchSessionSize?=?16384
????#?globe?session?size?,?if?exceeded?throws?exceptions
????maxGlobalSessionSize?=?512
????#?file?buffer?size?,?if?exceeded?allocate?new?buffer
????fileWriteBufferCacheSize?=?16384
????#?when?recover?batch?read?size
????sessionReloadReadSize?=?100
????#?async,?sync
????flushDiskMode?=?async
??}
}
6. 調(diào)用測試
6.1 啟動seata,seata默認端口是8091
win 使用cmd 運行seata-server.bat 文件
6.2 啟動項目
可以看到8091 說明項目連接seata TCServer正常,項目啟動正常。
瀏覽器發(fā)送請求:
http://localhost:18080/order?userId=1&productId=1
請求之前數(shù)據(jù):


正常請求:



模擬異常:
二階段回滾

版權(quán)聲明:本文為博主原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接和本聲明。
本文鏈接:
https://blog.csdn.net/qq_32691791/article/details/112727500
粉絲福利:Java從入門到入土學(xué)習(xí)路線圖
??????

??長按上方微信二維碼?2 秒
感謝點贊支持下哈?
