• 分布式事物【RocketMQ事务消息、Docker安装 RocketMQ、实现订单微服务、订单微服务业务层实现】(八)-全面详解(学习总结---从入门到深化)


     

    目录

    可靠消息最终一致性分布式事务实现_RocketMQ事务消息

     可靠消息最终一致性分布式事务实战_案列业务介绍

     数据库表设计

    可靠消息最终一致性分布式事务实战_Docker安装 RocketMQ

     部署RocketMQ的管理工具

    可靠消息最终一致性分布式事务实战_实现订单微服务

    可靠消息最终一致性分布式事务实战_订单微服务业务层实现

    可靠消息最终一致性分布式事务实战_订单微服务监听事务消息

    可靠消息最终一致性分布式事务实战_实现库存微服务


    可靠消息最终一致性分布式事务实现_RocketMQ事务消息

     

     RocketMQ是阿里巴巴开源的一款支持事务消息的消息中间件,于 2012年正式开源,2017年成为Apache基金会的顶级项目。

    实现原理

    RocketMQ 4.3版之后引入了完整的事务消息机制,其内部实现了完 整的本地消息表逻辑,使用RocketMQ实现可靠消息分布式事务就 不用用户再实现本地消息表的逻辑了,极大地减轻了开发工作量。

     

     可靠消息最终一致性分布式事务实战_案列业务介绍

     

     业务介绍

    通过RocketMQ中间件实现可靠消息最终一致性分布式事务,模拟 商城业务中的下单扣减库存场景。订单微服务和库存微服务分别独立开发和部署。

     流程

     架构选型

     数据库表设计

    orders订单数据表

    orders数据表存储于tx-msg-orders订单数据库。

    1. DROP TABLE IF EXISTS `orders`;
    2. CREATE TABLE `order` (
    3. `id` bigint(20) NOT NULL COMMENT '主键',
    4. `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
    5. `order_no` varchar(64) CHARACTER SET utf8
    6. COLLATE utf8_bin NULL DEFAULT NULL COMMENT '订单
    7. 编号',
    8. `product_id` bigint(20) NULL DEFAULT NULL COMMENT '商品id',
    9. `pay_count` int(11) NULL DEFAULT NULL COMMENT '购买数量',
    10. PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE
    11. = utf8_bin ROW_FORMAT = Dynamic;
    12. SET FOREIGN_KEY_CHECKS = 1;
    13. CREATE TABLE `tx_log` (
    14. `tx_no` varchar(64) CHARACTER SET utf8
    15. COLLATE utf8_bin NOT NULL COMMENT '分布式事务全局序列号',
    16. `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
    17. PRIMARY KEY (`tx_no`) USING BTREE
    18. ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;

    stock库存数据表

    1. DROP TABLE IF EXISTS `stock`;
    2. CREATE TABLE `stock` (
    3. `id` bigint(20) NOT NULL COMMENT '主键id',
    4. `product_id` bigint(20) NULL DEFAULT NULL COMMENT '商品id',
    5. `total_count` int(11) NULL DEFAULT NULL COMMENT '商品总库存',
    6. PRIMARY KEY (`id`) USING BTREE
    7. ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;
    8. -- ----------------------------
    9. -- Table structure for tx_log
    10. -- ----------------------------
    11. DROP TABLE IF EXISTS `tx_log`;
    12. CREATE TABLE `tx_log` (
    13. `tx_no` varchar(64) CHARACTER SET utf8
    14. COLLATE utf8_bin NOT NULL COMMENT '分布式事务全局序列号',
    15. `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
    16. PRIMARY KEY (`tx_no`) USING BTREE
    17. ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;
    18. SET FOREIGN_KEY_CHECKS = 1;

    tx_log事务记录表

    可靠消息最终一致性分布式事务实战_Docker安装 RocketMQ

     在安装RocketMQ之前,我们先了解一下RocketMQ的部署架构,了 解一下RocketMQ的组件,然后基于当前主流的Docker安装 RocketMQ,我们这里安装单台RocketMQ,但为了防止单节点故 障、保障高可用,生产环境建议安装RocketMQ集群。

     

     安装NameServer

    拉取镜像

    docker pull rocketmqinc/rocketmq

    创建数据存储目录

    1. mkdir -p /docker/rocketmq/data/namesrv/logs
    2. /docker/rocketmq/data/namesrv/store

    启动NameServer

    1. docker run -d \
    2. --restart=always \
    3. --name rmqnamesrv \
    4. -p 9876:9876 \
    5. -v /docker/rocketmq/data/namesrv/logs:/root/logs \
    6. -v /docker/rocketmq/data/namesrv/store:/root/store \
    7. -e "MAX_POSSIBLE_HEAP=100000000" \
    8. rocketmqinc/rocketmq \
    9. sh mqnamesrv

     安装Broker

    border配置:创建 broker.conf 配置文件

    vim /docker/rocketmq/conf/broker.conf

    1. # 所属集群名称,如果节点较多可以配置多个
    2. brokerClusterName = DefaultCluster
    3. #broker名称,master和slave使用相同的名称,表明他们的
    4. 主从关系
    5. brokerName = broker-a
    6. #0表示Master,大于0表示不同的
    7. slave brokerId = 0
    8. #表示几点做消息删除动作,默认是凌晨4
    9. deleteWhen = 04
    10. #在磁盘上保留消息的时长,单位是小时
    11. fileReservedTime = 48
    12. #有三个值:SYNC_MASTER,ASYNC_MASTER,SLAVE;同步和
    13. 异步表示Master和Slave之间同步数据的机 制;
    14. brokerRole = ASYNC_MASTER
    15. #刷盘策略,取值为:ASYNC_FLUSH,SYNC_FLUSH表示同步刷
    16. 盘和异步刷盘;SYNC_FLUSH消息写入磁盘后 才返回成功状
    17. 态,ASYNC_FLUSH不需要;
    18. flushDiskType = ASYNC_FLUSH
    19. # 设置broker节点所在服务器的ip地址
    20. brokerIP1 = 192.168.66.100
    21. #剩余磁盘比例
    22. diskMaxUsedSpaceRatio=99

    启动broker

    1. docker run -d --restart=always --name rmqbroker
    2. --link rmqnamesrv:namesrv -p 10911:10911 -p
    3. 10909:10909 --privileged=true -v
    4. /docker/rocketmq/data/broker/logs:/root/logs -v
    5. /docker/rocketmq/data/broker/store:/root/store
    6. -v
    7. /docker/rocketmq/conf/broker.conf:/opt/rocketmq
    8. -4.4.0/conf/broker.conf -e
    9. "NAMESRV_ADDR=namesrv:9876" -e
    10. "MAX_POSSIBLE_HEAP=200000000"
    11. rocketmqinc/rocketmq sh mqbroker -c
    12. /opt/rocketmq-4.4.0/conf/broker.conf

     报错:

     

     部署RocketMQ的管理工具

     RocketMQ提供了UI管理工具,名为rocketmq-console,我们选择 docker安装

    1. #创建并启动容器
    2. docker run -d --restart=always --name rmqadmin
    3. -e "JAVA_OPTS=-
    4. Drocketmq.namesrv.addr=192.168.66.100:9876 -
    5. Dcom.rocketmq.sendMessageWithVIPChannel=false"
    6. -p 8080:8080 pangliang/rocketmq-console-ng

    关闭防火墙(或者开放端口)

    1. #关闭防火墙
    2. systemctl stop firewalld.service
    3. #禁止开机启动
    4. systemctl disable firewalld.service

    测试

    访问:http://192.168.66.101:8080/#/ (可以切换中文)

    可靠消息最终一致性分布式事务实战_实现订单微服务

    创建父工程rocketmq-msg

     创建订单微服务子工程

     引入依赖

    1. <dependencies>
    2. <dependency>
    3. <groupId>org.springframework.bootgroupId>
    4. <artifactId>spring-boot-starterwebartifactId>
    5. dependency>
    6. <dependency>
    7. <groupId>mysqlgroupId>
    8. <artifactId>mysql-connectorjavaartifactId>
    9. dependency>
    10. <dependency>
    11. <groupId>org.apache.rocketmqgroupId>
    12. <artifactId>rocketmq-spring-bootstarterartifactId>
    13. <version>2.0.2version>
    14. dependency>
    15. <dependency>
    16. <groupId>com.baomidougroupId>
    17. <artifactId>mybatis-plus-bootstarterartifactId>
    18. dependency>
    19. <dependency>
    20. <groupId>org.projectlombokgroupId>
    21. <artifactId>lombokartifactId>
    22. dependency>
    23. dependencies>

    编写配置文件

    1. server:
    2. port: 9090
    3. spring:
    4. application:
    5. name: tx-msg-stock
    6. datasource:
    7. url: jdbc:mysql://192.168.66.100:3306/txmsg-order?
    8. useUnicode=true&characterEncoding=UTF8&useOldAliasMetadataBehavior=true&autoReconnec
    9. t=true&failOverReadOnly=false&useSSL=false
    10. username: root
    11. password: 123456
    12. driver-class-name: com.mysql.cj.jdbc.Driver
    13. ################ RocketMQ 配置 ##########
    14. rocketmq:
    15. name-server: 192.168.66.100:9876
    16. producer:
    17. group: order-group

    编写主启动类

    1. /**
    2. * 订单微服务启动成功
    3. */
    4. @Slf4j
    5. @MapperScan("com.tong.order.mapper")
    6. @SpringBootApplication
    7. public class OrderMain9090 {
    8. public static void main(String[] args) {
    9. SpringApplication.run(OrderMain9090.class,args);
    10. log.info("************* 订单微服务启动成功*******");
    11. }
    12. }

    代码生成

    1. package com.tong.utils;
    2. import com.baomidou.mybatisplus.generator.FastAutoGenerator;
    3. import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
    4. import java.util.Arrays;
    5. import java.util.List;
    6. public class CodeGenerator {
    7. public static void main(String[] args) {
    8. FastAutoGenerator.create("jdbc:mysql://192.168.66.102:3306/tx-msg-order", "root", "123456")
    9. .globalConfig(builder -> {
    10. builder.author("tong")// 设置作者
    11. .commentDate("MMdd") // 注释日期格式
    12. .outputDir(System.getProperty("user.dir")+"/rocketmq-msg/orders"+ "/src/main/java/")
    13. .fileOverride(); //覆盖文件
    14. })
    15. // 包配置
    16. .packageConfig(builder -> {
    17. builder.parent("com.tong.orders") // 包名前缀
    18. .entity("entity")//实体类包名
    19. .mapper("mapper")//mapper接口包名
    20. .service("service"); //service包名
    21. })
    22. .strategyConfig(builder -> {
    23. // 设置需要生成的表名
    24. builder.addInclude(Arrays.asList("orders","tx_log"))
    25. // 开始实体类配置
    26. .entityBuilder()
    27. // 开启lombok模型
    28. .enableLombok()
    29. //表名下划线转驼峰
    30. .naming(NamingStrategy.underline_to_camel)
    31. //列名下划线转驼峰
    32. .columnNaming(NamingStrategy.underline_to_camel);
    33. })
    34. .execute();
    35. }
    36. }

    创建TxMessage类

    在项目的com.itbaizhan.orders.tx包下创建TxMessage类,主要用 来封装实现分布式事务时,在订单微服务、RocketMQ消息中间件 和库存微服务之间传递的全局事务消息,项目中会通过事务消息实现幂等。

    1. @Data
    2. @NoArgsConstructor
    3. @AllArgsConstructor
    4. public class TxMessage implements Serializable
    5. {
    6. private static final long serialVersionUID = -4704980150056885074L;
    7. /**
    8. * 商品id
    9. */
    10. private Long productId;
    11. /**
    12. * 商品购买数量
    13. */
    14. private Integer payCount;
    15. /**
    16. * 全局事务编号
    17. */
    18. private String txNo;
    19. }

    可靠消息最终一致性分布式事务实战_订单微服务业务层实现

     业务逻辑层主要实现了用户提交订单后的业务逻辑。

    编写OrderService接口

    1. /**
    2. * 添加订单
    3. * @param productId 商品id
    4. * @param payCount 购买数量
    5. */
    6. void save(Long productId,Integer payCount);
    7. /**
    8. * 提交订单同时保存事务信息
    9. */
    10. void submitOrderAndSaveTxNo(TxMessage txMessage);
    11. /**
    12. * 提交订单
    13. * @param productId 商品id
    14. * @param payCount 购买数量
    15. */
    16. void submitOrder(Long productId, Integer payCount);

    编写OrderService接口实现

    1. package com.itbaizhan.order.service.impl;
    2. import com.alibaba.fastjson.JSONObject;
    3. import com.tong.order.entity.Order;
    4. import com.tong.order.entity.TxLog;
    5. import com.tong.order.mapper.OrderMapper;
    6. import com.tong.order.mapper.TxLogMapper;
    7. import com.tong.order.service.IOrderService;
    8. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    9. import com.tong.order.tx.TxMessage;
    10. import lombok.extern.slf4j.Slf4j;
    11. import org.apache.rocketmq.spring.core.RocketMQTemplate;
    12. import org.springframework.beans.factory.annotation.Autowired;
    13. import org.springframework.messaging.Message;
    14. import org.springframework.messaging.support.MessageBuilder;
    15. import org.springframework.stereotype.Service;
    16. import org.springframework.transaction.annotation.Transactional;
    17. import javax.annotation.Resource;
    18. import java.time.LocalDateTime;
    19. import java.util.Date;
    20. import java.util.UUID;
    21. /**
    22. *

    23. * 服务实现类
    24. *

    25. *
    26. * @author tong
    27. * @since 05-20
    28. */
    29. @Slf4j
    30. @Service
    31. public class OrderServiceImpl extends
    32. ServiceImpl implements IOrderService {
    33. @Resource
    34. RocketMQTemplate rocketMQTemplate;
    35. @Resource
    36. private TxLogMapper txLogMapper;
    37. /**
    38. * 添加
    39. * @param productId 商品id
    40. * @param payCount 购买数量
    41. */
    42. @Override
    43. public void save(Long productId, Integer payCount) {
    44. Order order = new Order();
    45. // 订单创建时间
    46. order.setCreateTime(LocalDateTime.now());
    47. // 生产订单编号
    48. order.setOrderNo(UUID.randomUUID().toString().replace("-",""));
    49. // 商品id
    50. order.setProductId(productId);
    51. // 购买数量
    52. order.setPayCount(payCount);
    53. baseMapper.insert(order);
    54. }
    55. @Override
    56. @Transactional(rollbackFor = Exception.class)
    57. public void submitOrderAndSaveTxNo(TxMessage txMessage) {
    58. TxLog txLog = txLogMapper.selectById(txMessage.getTxNo());
    59. if(txLog != null){
    60. log.info("订单微服务已经执行过事务,商品id为:{},事务编号为:{}",txMessage.getProductId(),txMessage.getTxNo());
    61. return;
    62. }
    63. //生成订单
    64. this.save(txMessage.getProductId(),txMessage.getPayCount());
    65. //生成订单
    66. txLog = new TxLog();
    67. txLog.setTxNo(txMessage.getTxNo());
    68. txLog.setCreateTime(LocalDateTime.now());
    69. //添加事务日志
    70. txLogMapper.insert(txLog);
    71. }
    72. /**
    73. * 提交订单
    74. * @param productId 商品id
    75. * @param payCount 购买数量
    76. */
    77. @Override
    78. public void submitOrder(Long productId,Integer payCount) {
    79. //生成全局分布式序列号
    80. String txNo = UUID.randomUUID().toString();
    81. TxMessage txMessage = new TxMessage(productId, payCount, txNo);
    82. JSONObject jsonObject = new JSONObject();
    83. jsonObject.put("txMessage", txMessage);
    84. Message message = MessageBuilder.withPayload(jsonObject.toJSONString()).build();
    85. //发送事务消息 且该消息不允许消费
    86. tx_order_group: 指定版事务消息组
    87. rocketMQTemplate.sendMessageInTransaction("tx_order_group", "topic_txmsg", message, null);
    88. }
    89. }

    可靠消息最终一致性分布式事务实战_订单微服务监听事务消息

     执行本地的业务代码

    1. package com.tong.order.message;
    2. import com.alibaba.fastjson.JSONObject;
    3. import com.tong.order.entity.TxLog;
    4. import com.tong.order.mapper.TxLogMapper;
    5. import com.tong.order.service.IOrderService;
    6. import com.tong.order.service.ITxLogService;
    7. import com.tong.order.tx.TxMessage;
    8. import lombok.extern.slf4j.Slf4j;
    9. import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener;
    10. import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener;
    11. import org.apache.rocketmq.spring.core.RocketMQLocalTransactionState;
    12. import org.springframework.beans.factory.annotation.Autowired;
    13. import org.springframework.messaging.Message;
    14. import org.springframework.stereotype.Component;
    15. import org.springframework.transaction.annotation.Transactional;
    16. import javax.annotation.Resource;
    17. /**
    18. * @author tong
    19. * @version 1.0.0
    20. * @description 监听事务消息
    21. */
    22. @Slf4j
    23. @Component
    24. @RocketMQTransactionListener(txProducerGroup = "tx_order_group")
    25. public class OrderTxMessageListener implements
    26. RocketMQLocalTransactionListener {
    27. @Autowired
    28. private IOrderService orderService;
    29. @Resource
    30. private TxLogMapper txLogMapper;
    31. /**
    32. * RocketMQ的Producer本地事务:先执行本地的业务代码(使用Spring的事件管理),判断是否成功。
    33. * 成功返回: RocketMQLocalTransactionState.COMMIT,
    34. * 失败返回:RocketMQLocalTransactionState.ROLLBACK
    35. */
    36. @Override
    37. @Transactional(rollbackFor = Exception.class)
    38. public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object obj)
    39. {
    40. try {
    41. log.info("订单微服务执行本地事务");
    42. TxMessage txMessage = this.getTxMessage(msg);
    43. //执行本地事务
    44. orderService.submitOrderAndSaveTxNo(txMessage);
    45. //提交事务
    46. log.info("订单微服务提交事务");
    47. // COMMIT:即生产者通知Rocket该消息可以消费
    48. return RocketMQLocalTransactionState.COMMIT;
    49. } catch (Exception e) {
    50. e.printStackTrace();
    51. //异常回滚事务
    52. log.info("订单微服务回滚事务");
    53. // ROLLBACK:即生产者通知Rocket将该消息删除
    54. return RocketMQLocalTransactionState.ROLLBACK;
    55. }
    56. }
    57. private TxMessage getTxMessage(Message msg)
    58. {
    59. String messageString = new String((byte[]) msg.getPayload());
    60. JSONObject jsonObject = JSONObject.parseObject(messageString);
    61. String txStr = jsonObject.getString("txMessage");
    62. return JSONObject.parseObject(txStr,TxMessage.class);
    63. }
    64. }

    网络异常消息处理

    1. /**
    2. * 因为网络异常或其他原因时,RocketMQ的消息状态处于UNKNOWN时,调用该方法检查Producer的本地
    3. * 事务是否已经执行成功,
    4. * 成功返回: RocketMQLocalTransactionState.COMMIT,
    5. * 失败返回:RocketMQLocalTransactionState.ROLLBACK
    6. */
    7. @Override
    8. public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
    9. log.info("订单微服务查询本地事务");
    10. TxMessage txMessage = this.getTxMessage(msg);
    11. // 获取订单的消息
    12. Integer exists = txLogService.isExistsTx(txMessage.getTxNo());
    13. if (exists != null) {
    14. // COMMIT:即生产者通知Rocket该消息可以消费
    15. return RocketMQLocalTransactionState.COMMIT;
    16. }
    17. // UNKNOWN:即生产者通知Rocket继续查询该消息的状态
    18. return RocketMQLocalTransactionState.UNKNOWN;
    19. }
    20. private TxMessage getTxMessage(Message msg)
    21. {
    22. String messageString = new String((byte[]) msg.getPayload());
    23. JSONObject jsonObject = JSONObject.parseObject(messageString);
    24. String txStr = jsonObject.getString("txMessage");
    25. return JSONObject.parseObject(txStr,TxMessage.class);
    26. }

    可靠消息最终一致性分布式事务实战_实现库存微服务

    创建库存微服务tx-msg-stock

     引入依赖

    1. <dependencies>
    2. <dependency>
    3. <groupId>org.springframework.bootgroupId>
    4. <artifactId>spring-boot-starterwebartifactId>
    5. dependency>
    6. <dependency>
    7. <groupId>mysqlgroupId>
    8. <artifactId>mysql-connectorjavaartifactId>
    9. dependency>
    10. <dependency>
    11. <groupId>org.apache.rocketmqgroupId>
    12. <artifactId>rocketmq-spring-bootstarterartifactId>
    13. <version>2.0.1version>
    14. dependency>
    15. <dependency>
    16. <groupId>com.baomidougroupId>
    17. <artifactId>mybatis-plus-bootstarterartifactId>
    18. dependency>
    19. <dependency>
    20. <groupId>org.projectlombokgroupId>
    21. <artifactId>lombokartifactId>
    22. dependency>
    23. dependencies>

     编写配置文件

    1. server:
    2. port: 6060
    3. spring:
    4. application:
    5. name: tx-msg-stock
    6. datasource:
    7. url: jdbc:mysql://192.168.66.100:3306/txmsg-stock?
    8. useUnicode=true&characterEncoding=UTF8&useOldAliasMetadataBehavior=true&autoReconnec
    9. t=true&failOverReadOnly=false&useSSL=false
    10. username: root
    11. password01: 123456
    12. driver-class-name: com.mysql.cj.jdbc.Driver
    13. ################ RocketMQ 配置 ##########
    14. rocketmq:
    15. name-server: 192.168.66.100:9876

    编写主启动类

    1. package com.tong.stock;
    2. import lombok.extern.slf4j.Slf4j;
    3. import org.mybatis.spring.annotation.MapperScan;
    4. import org.springframework.boot.SpringApplication;
    5. import org.springframework.boot.autoconfigure.SpringBootApplication;
    6. /**
    7. * @author tong
    8. * @version 1.0.0
    9. * @description 库存微服务启动类
    10. */
    11. @MapperScan("com.tong.stock.mapper")
    12. @Slf4j
    13. @SpringBootApplication
    14. public class StockServerStarter {
    15. public static void main(String[] args) {
    16. SpringApplication.run(StockServerStarter.class, args);
    17. log.info("**************** 库存服务启动成功 ***********");
    18. }
    19. }

    代码生成

    1. package com.tong.utils;
    2. import com.baomidou.mybatisplus.generator.FastAutoGenerator;
    3. import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
    4. import java.util.Arrays;
    5. import java.util.List;
    6. public class CodeGenerator {
    7. public static void main(String[] args) {
    8. FastAutoGenerator.create("jdbc:mysql://192.168.66.102:3306/tx-msg-stock", "root", "123456")
    9. .globalConfig(builder -> { builder.author("tong")// 设置作者
    10. .commentDate("MMdd") // 注释日期格式
    11. .outputDir(System.getProperty("user.dir") +"/rocketmq-msg/stock"+ "/src/main/java/")
    12. .fileOverride(); //覆盖文件
    13. })
    14. // 包配置
    15. .packageConfig(builder -> {
    16. builder.parent("com.tong.stock") // 包名前缀
    17. .entity("entity")//实体类包名
    18. .mapper("mapper")//mapper接口包名
    19. .service("service"); //service包名
    20. })
    21. .strategyConfig(builder -> {
    22. // 设置需要生成的表名
    23. builder.addInclude(Arrays.asList("stock","tx_log"))
    24. // 开始实体类配置
    25. .entityBuilder()
    26. // 开启lombok模型
    27. .enableLombok() //表名下划线转驼峰
    28. .naming(NamingStrategy.underline_to_camel)
    29. //列名下划线转驼峰
    30. .columnNaming(NamingStrategy.underline_to_camel);
    31. })
    32. .execute();
    33. }
    34. }

    编写库存接口

    1. public interface StockService {
    2. /**
    3. * 根据id查询库存
    4. * @param id
    5. * @return
    6. */
    7. Stock getStockById(Long id);
    8. /**
    9. * 扣减库存
    10. */
    11. void decreaseStock(TxMessage txMessage);
    12. }

  • 相关阅读:
    【Python机器学习】零基础掌握GaussianProcessRegressor高斯过程
    yolov8 模型部署--TensorRT部署-c++服务化部署
    Codeforces Round #814 (Div. 2) A.B.C
    记一次排查线上MySQL死锁过程,不能只会curd,还要知道加锁原理
    夜莺n9ev5配置grafana9.1.1
    UOS统信操作系统QIcon::fromTheme详解
    智能边缘小站 CloudPond(低延迟、高带宽和更好的数据隐私保护)
    springboot使用切面记录接口访问日志
    【python】基于随机森林和决策树的鸢尾花分类
    动态规划- 背包问题总结(一)
  • 原文地址:https://blog.csdn.net/m0_58719994/article/details/131691489