1. SpringBoot分布式事務整合Seata

        共 12783字,需瀏覽 26分鐘

         ·

        2021-01-21 09:27

        點擊上方藍色字體,選擇“標星公眾號”

        優(yōu)質(zhì)文章,第一時間送達

        76套java從入門到精通實戰(zhàn)課程分享

        1. 適用場景

        1. 一個SpringBoot的單體項目整合多個關(guān)系型數(shù)據(jù)庫。多數(shù)據(jù)源。

        2. Seata 地址

        http://seata.io/zh-cn/docs/overview/what-is-seata.html

        3.案例說明

        模擬一個用戶下訂單場景。

        1. 創(chuàng)建三個數(shù)據(jù)庫:用戶庫、商品庫、訂單庫。

        2. SpringBoot 項目配置三個數(shù)據(jù)庫。

        3. 訂單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分布式事務

        ????
        ????????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
        ????????????????
        ????????????????????**/*.*
        ????????????????

        ????????????

        ????????

        ????



        1. 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?事務組編號?用于TC集群名
        ??tx-service-group:?springboot-seata-group

        spring:
        ??application:
        ????name:?seata-distributed-transaction
        ??datasource:
        ????dynamic:
        ??????datasource:
        ????????#?設置?賬號數(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ù)據(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ù)據(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ù)據(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
        ??
        ????"selectByPrimaryKey"?parameterType="java.lang.Integer"?resultMap="BaseResultMap">
        ????????select
        ????????"Base_Column_List"/>
        ????????from?product
        ????????where?id?=?#{id,jdbcType=INTEGER}
        ????
        ????"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
        ??
        ??"selectByPrimaryKey"?parameterType="java.lang.Integer"?resultMap="BaseResultMap">
        ????select?
        ????"Base_Column_List"?/>
        ????from?orders
        ????where?id?=?#{id,jdbcType=INTEGER}
        ??
        ??"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
        ??

        ????
        ????"selectAccountByUserId"?parameterType="java.lang.Integer"?resultMap="BaseResultMap">
        ????????select
        ????????"Base_Column_List"/>
        ????????from?account
        ????????where?user_id?=?#{userId,?jdbcType=INTEGER}
        ????

        ????"selectByPrimaryKey"?parameterType="java.lang.Integer"?resultMap="BaseResultMap">
        ????????select
        ????????"Base_Column_List"/>
        ????????from?account
        ????????where?id?=?#{id,jdbcType=INTEGER}
        ????

        ????"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全局事務注解
        ????public?Integer?createOrder(Integer?userId,?Integer?productId)?throws?Exception?{
        ????????Integer?amount?=?1;?//?購買數(shù)量暫時設置為?1

        ????????log.info("當前?XID:?{}",?RootContext.getXID());

        ????????//?減庫存?-?遠程服務
        ????????Product?product?=?productService.reduceStock(productId,?amount);

        ????????//?減余額?-?遠程服務
        ????????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從入門到入土學習路線圖

        ??????

        ??長按上方微信二維碼?2 秒


        感謝點贊支持下哈?

        瀏覽 91
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
          
          

            1. 美女撒尿私密视频 | 北条麻妃99 | chinese麻豆videostube | 久久久久久久久久一区二区三区 | 欧美三级片在线观看 |