• Springcloud结合mybatis-plus与nacos实现分布式事务seata


    简介

    Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。

    项目示例

    用户购买商品的业务逻辑。整个业务逻辑由3个微服务提供支持:
    库存服务:对给定的商品扣除仓储数量。
    订单服务:根据采购需求创建订单。
    帐户服务:从用户帐户中扣除余额。
    下单流程

    seata配置启动

    #初始化seata 的nacos配置
    cd conf
    sh nacos-config.sh 127.0.0.1
    
    #启动seata-server
    cd bin
    seata-server.bat -p 8091 -h 127.0.0.1 -m db
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    启动成功
    配置可以参考官方的配置。

    账户模块

    mapper
    /**
     * 

    * Mapper 接口 *

    * * @author elite * @since 2022-09-11 */
    @Mapper public interface AccountMapper extends BaseMapper<Account> { /** * 更新账户余额 * @param user_id * @param amt * @return */ @Update("UPDATE seata_account.account SET acc_money= acc_money - #{amt} WHERE user_id = #{user_id} and (acc_money - #{amt}) >= 0") boolean deductAcct(Integer user_id, BigDecimal amt); }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    启动账户

    启动成功

    库存模块

    mapper
    /**
     * 

    * 库存表 Mapper 接口 *

    * * @author elite * @since 2022-09-11 */
    @Mapper public interface StockMapper extends BaseMapper<Stock> { @Update("UPDATE seata_stock.stock set stock_num = stock_num - #{product_num} WHERE product_id = #{product_id} and (stock_num - #{product_num}) >= 0") boolean deuctStock(Integer product_id, Integer product_num); }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    启动账户模块

    账户

    订单模块
    依赖
     
        <dependency>
            <groupId>com.alibaba.cloudgroupId>
            <artifactId>spring-cloud-alibaba-seataartifactId>
            <version>2.2.0.RELEASEversion>
        dependency>
        <dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    yml配置
    server:
      port: 8087
    spring:
      application:
        name: springcloud-seata-order
      ##nacos服务地址注册
      cloud:
        nacos:
          discovery:
            server-addr: 192.168.5.130:8848
            enabled: true
        alibaba:
          seata:
            tx-service-group: springcloud-seata-order
      #配置数据库
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:mysql://192.168.5.130:3306/seata_order
        driver-class-name: com.mysql.jdbc.Driver
        username: root
        password: 123456
        druid:
          # 初始化大小,最小,最大
          initialSize: 5
          minIdle: 5
          maxActive: 20
          # 配置获取连接等待超时的时间(毫秒)
          maxWait: 60000
          # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
          timeBetweenEvictionRunsMillis: 60000
          # 配置有一个连接在连接池中的最小生存时间,单位是毫秒
          minEvictableIdleTimeMillis: 300000
          validationQuery: SELECT 1 FROM DUAL
          testWhileIdle: true
          testOnBorrow: false
          testOnReturn: false
          # 打开PSCache,指定每个连接上PSCache的大小
          poolPreparedStatements: true
          maxPoolPreparedStatementPerConnectionSize: 20
          # 配置监控统计拦截的filters,去掉后监控界面sql将无法统计,'wall'用于防火墙
          filters: stat, wall, log4j
          # 通过connectProperties属性来打开mergeSql功能,慢SQL记录
          connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
    
    #mybatis日志
    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
      mapper-locations: classpath:mapper/*.xml
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    controller类
    /**
     * 

    * 订单表 前端控制器 *

    * * @author elite * @since 2022-09-11 */
    @RestController @RequestMapping("/springcloud/order") public class OrderController { @Autowired IOrderService orderService; /** * 传入用户id 商品id下单 * @param product_id * @param user_id * @return */ @GetMapping("/createOrder/{product_id}/{user_id}/{product_num}") public R createOrder(@PathVariable("")Integer product_id, @PathVariable("user_id") Integer user_id, @PathVariable("product_num")Integer product_num){ R r = orderService.createOrder(product_id,user_id,product_num); return r; } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    服务类
    
    /**
     * 

    * 订单表 服务实现类 *

    * * @author elite * @since 2022-09-06 */
    @Service @Slf4j public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService { @Autowired OrderMapper orderMapper; //库存服务 @Autowired StockService stockService; //账户服务 @Autowired AcctService acctService; /** * 下单服务 * @param product_id * @param user_id * @return */ @GlobalTransactional @Override public R createOrder(Integer product_id, Integer user_id,Integer product_num) { //库存服务:对给定的商品扣除仓储数量。 Product product = stockService.getProductById(product_id); if (product == null){ return R.fail(400,"商品信息为空,商品ID不能为空"); } boolean deductSuccess = stockService.deductStock(product_id, product_num); if (!deductSuccess){ return R.fail(400,"扣减库存失败!"); } //验证用户 User user = acctService.getUserByUseId(user_id); if (user == null){ return R.fail(400,"传入的用户ID不存在"); } //订单服务:根据采购需求创建订单。 BigDecimal amt = (product.getProductPrice()).multiply(new BigDecimal(product_num )); //订单信息 Order order = new Order(); order.setOrderNo(10); order.setProductId(product_id); order.setUserId(user.getUserId()); order.setOrderNum(product_num); order.setOrderAmt(amt); //单价*数量 order.setOrderStatus("下单"); order.setPayStatus("支付成功"); order.setCreateBy("牛奶糖"); order.setUpdateBy("牛奶糖"); orderMapper.insert(order); //模拟异常 //int i = 1/0; //帐户服务:从用户帐户中扣除余额。 boolean deductAcctFlag = acctService.deductAcct(user_id, amt); if (!deductAcctFlag){ return R.fail(400,"扣减账户失败!"); } return R.ok(200,"下单成功!",order); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    测试库存模块

    获取商品

    获取商品

    扣减库存

    扣减库存
    执行的sql
    ==> Preparing: UPDATE seata_stock.stock set stock_num = stock_num - ? WHERE product_id = ? and (stock_num - ?) >= 0
    更新数量大于库存数量失败

    测试账户模块

    获取用户

    获取用户

    扣减账户

    扣减账户

    测试订单

    不加事务模拟下单

    下单成功
    订单
    库存
    账户

    不加事务模拟异常

    异常模拟库存减少
    账户未扣

    加事务模拟异常

    @GlobalTransactional 进行模拟异常
    事务
    下单订单
    账户
    数据库脚本和完整代码比较多,这里就不全部贴出来。

  • 相关阅读:
    AI:06-基于OpenCV的二维码识别技术的研究
    vulnhub靶场之ICA: 1
    【ESP32之旅】ESP32-S2 MicroPython环境搭建
    Qt学习08 启航!第一个应用实例
    硬件新问答
    波卡 DOT 完成性质转变 已不再是证券
    基于ZigBee设计的物联网LED控制系统
    中间件环境搭建配置过程解读
    QT 6.5下载安装及配置教程
    配置Swagger2生成API接口文档
  • 原文地址:https://blog.csdn.net/qq_37400096/article/details/126801955