`
zhangdaiscott
  • 浏览: 427494 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
8fb25857-16b4-3681-ab5e-e319f45c42a8
Jeecg快速开发平台
浏览量:0
文章分类
社区版块
存档分类

jeecgboot集成seata实战

阅读更多

1. 环境描述

JeecgBoot 3.0

seata版本 : 1.3.0

2.数据库搭建

先创建3个数据库,加上jeecg-boot自有的数据库,一共4个数据库

在这里插入图片描述在这里插入图片描述

首先在四个数据库中引入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;

在jeecg-account中,创建表并插入数据

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;
 
INSERT INTO `account` (`id`, `user_id`, `balance`, `update_time`) VALUES ('1', '1', '200', '2021-01-15 00:02:17');
 
在jeecg-order库中,创建表
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;

 

在jeecg-product中,创建表并插入数据

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;
 
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');

 

3. 坐标引入

 

<!-- seata-spring-boot-starter -->
<dependency>
 <groupId>io.seata</groupId>
 <artifactId>seata-spring-boot-starter</artifactId>
 <version>1.3.0</version>
</dependency>

 

4. yml配置文件

 

seata:
  config:
    type: file
  application-id: springboot-seata
  #  enable-auto-data-source-proxy: false
  registry:
    type: file
  service:
    grouplist:
      default: 127.0.0.1:8091
    vgroup-mapping:
      springboot-seata-group: default
  # seata 事务组编号 用于TC集群名
  tx-service-group: springboot-seata-group
spring:
  datasource:
    dynamic:
      datasource:
        master:
          url: jdbc:mysql://127.0.0.1:3306/jeecg-boot?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
          username: root
          password: root
          driver-class-name: com.mysql.cj.jdbc.Driver
        # 设置 账号数据源配置
        account-ds:
          driver-class-name: com.mysql.cj.jdbc.Driver
          password: root
          url: jdbc:mysql://127.0.0.1:3306/jeecg-account?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false
          username: root
          # 设置 订单数据源配置
        order-ds:
          driver-class-name: com.mysql.cj.jdbc.Driver
          password: root
          url: jdbc:mysql://127.0.0.1:3306/jeecg-order?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false
          username: root
          # 设置商品 数据源配置
        product-ds:
          driver-class-name: com.mysql.cj.jdbc.Driver
          password: root
          url: jdbc:mysql://127.0.0.1:3306/jeecg-product?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false
          username: root
          
      # 设置默认数据源或者数据源组 默认值即为master
      primary: master   # 默认指定一个数据源
      # 开启对 seata的支持
      seata: true

 

5. Seata启动

采用jeecg-boot单体模式测试,使用默认的文件进行seata配置,不需要做额外的配置,直接启动seata-server.bat即可。

6. 代码编写

项目结构

在这里插入图片描述

其中三个实体类对应如下

 

package org.jeecg.modules.seata.entity;
 
import lombok.Data;
 
import java.math.BigDecimal;
import java.util.Date;
 
@Data
public class Orders {
 
    private Integer id;
 
    private Integer userId;
 
    private Integer productId;
 
    private BigDecimal payAmount;
 
    private Date addTime;
 
    private Date updateTime;
 
}
 

 

package org.jeecg.modules.seata.entity;
 
import lombok.Data;
 
import java.math.BigDecimal;
import java.util.Date;
 
@Data
public class Product {
 
    private Integer id;
 
    private String name;
 
    private BigDecimal price;
 
    private Integer stock;
 
    private Date addTime;
 
    private Date updateTime;
}

 

package org.jeecg.modules.seata.entity;
 
import lombok.Data;
 
import java.math.BigDecimal;
import java.util.Date;
 
@Data
public class Account {
 
    private Integer id;
 
    private Integer userId;
 
    private BigDecimal balance;
 
    private Date updateTime;
}

 

Mapper对应代码如下

package org.jeecg.modules.seata.mapper;
 
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.jeecg.modules.seata.entity.Product;
 
@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);
}

 

package org.jeecg.modules.seata.mapper;
 
import org.apache.ibatis.annotations.Mapper;
import org.jeecg.modules.seata.entity.Orders;
 
@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);
}

 

package org.jeecg.modules.seata.mapper;
 
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.jeecg.modules.seata.entity.Account;
 
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);
}

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.jeecg.modules.seata.mapper.ProductMapper">
    <resultMap id="BaseResultMap" type="org.jeecg.modules.seata.entity.Product">
        <id column="id" jdbcType="INTEGER" property="id"/>
        <result column="name" jdbcType="VARCHAR" property="name"/>
        <result column="price" jdbcType="DECIMAL" property="price"/>
        <result column="stock" jdbcType="INTEGER" property="stock"/>
        <result column="add_time" jdbcType="TIMESTAMP" property="addTime"/>
        <result column="update_time" jdbcType="TIMESTAMP" property="updateTime"/>
    </resultMap>
    <sql id="Base_Column_List">
    id, name, price, stock, add_time, update_time
  </sql>
    <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from product
        where id = #{id,jdbcType=INTEGER}
    </select>
    <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
    delete from product
    where id = #{id,jdbcType=INTEGER}
  </delete>
    <insert id="insert" parameterType="org.jeecg.modules.seata.entity.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}
      )
  </insert>
    <insert id="insertSelective" parameterType="org.jeecg.modules.seata.entity.Product">
        insert into product
        <trim prefix="(" 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>
        </trim>
        <trim prefix="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>
        </trim>
    </insert>
    <update id="updateByPrimaryKeySelective" parameterType="org.jeecg.modules.seata.entity.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}
    </update>
    <update id="updateByPrimaryKey" parameterType="org.jeecg.modules.seata.entity.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}
  </update>
 
    <!--减库存-->
    <update id="reduceStock">
    update product SET stock = stock - #{amount, jdbcType=INTEGER}
    WHERE id = #{productId, jdbcType=INTEGER} AND stock >= #{amount, jdbcType=INTEGER}
  </update>
 
</mapper>

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.jeecg.modules.seata.mapper.OrdersMapper">
  <resultMap id="BaseResultMap" type="org.jeecg.modules.seata.entity.Orders">
    <id column="id" jdbcType="INTEGER" property="id" />
    <result column="user_id" jdbcType="INTEGER" property="userId" />
    <result column="product_id" jdbcType="INTEGER" property="productId" />
    <result column="pay_amount" jdbcType="DECIMAL" property="payAmount" />
    <result column="add_time" jdbcType="TIMESTAMP" property="addTime" />
    <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
  </resultMap>
  <sql id="Base_Column_List">
    id, user_id, product_id, pay_amount, add_time, update_time
  </sql>
  <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List" />
    from orders
    where id = #{id,jdbcType=INTEGER}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
    delete from orders
    where id = #{id,jdbcType=INTEGER}
  </delete>
  <insert id="insert" parameterType="org.jeecg.modules.seata.entity.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}
      )
  </insert>
 
  <insert id="insertSelective" parameterType="org.jeecg.modules.seata.entity.Orders">
    insert into orders
    <trim prefix="(" 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>
    </trim>
    <trim prefix="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>
    </trim>
  </insert>
 
  <update id="updateByPrimaryKeySelective" parameterType="org.jeecg.modules.seata.entity.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}
  </update>
  <update id="updateByPrimaryKey" parameterType="org.jeecg.modules.seata.entity.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}
  </update>
</mapper>

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.jeecg.modules.seata.mapper.AccountMapper">
 
    <resultMap id="BaseResultMap" type="org.jeecg.modules.seata.entity.Account">
        <id column="id" jdbcType="INTEGER" property="id"/>
        <result column="user_id" jdbcType="INTEGER" property="userId"/>
        <result column="balance" jdbcType="DECIMAL" property="balance"/>
        <result column="update_time" jdbcType="TIMESTAMP" property="updateTime"/>
    </resultMap>
 
    <sql id="Base_Column_List">
    id, user_id, balance, update_time
  </sql>
 
    <!--根据userId-->
    <select id="selectAccountByUserId" parameterType="java.lang.Integer" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from account
        where user_id = #{userId, jdbcType=INTEGER}
    </select>
 
    <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from account
        where id = #{id,jdbcType=INTEGER}
    </select>
 
    <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
    delete from account
    where id = #{id,jdbcType=INTEGER}
  </delete>
 
    <insert id="insert" parameterType="org.jeecg.modules.seata.entity.Account">
    insert into account (id, user_id, balance,
      update_time)
    values (#{id,jdbcType=INTEGER}, #{userId,jdbcType=INTEGER}, #{balance,jdbcType=DOUBLE},
      #{updateTime,jdbcType=TIMESTAMP})
  </insert>
 
    <insert id="insertSelective" parameterType="org.jeecg.modules.seata.entity.Account">
        insert into account
        <trim prefix="(" 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>
        </trim>
        <trim prefix="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>
        </trim>
    </insert>
 
    <update id="updateByPrimaryKeySelective" parameterType="org.jeecg.modules.seata.entity.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}
    </update>
 
   <update id="updateByPrimaryKey" parameterType="org.jeecg.modules.seata.entity.Account">
    update account
    set user_id = #{userId,jdbcType=INTEGER},
      balance = #{balance,jdbcType=DOUBLE},
      update_time = #{updateTime,jdbcType=TIMESTAMP}
    where id = #{id,jdbcType=INTEGER}
  </update>
 
    <!--减余额-->
    <update id="reduceBalance">
    update account
        SET balance = balance - #{money}
    WHERE user_id = #{userId, jdbcType=INTEGER}
        AND balance >= ${money}
  </update>
</mapper>

 

Service对应的代码如下

package org.jeecg.modules.seata.service;

import org.jeecg.modules.seata.entity.Product;
 
public interface ProductService {
 
    /**
     * 减库存
     *
     * @param productId 商品 ID
     * @param amount    扣减数量
     * @throws Exception 扣减失败时抛出异常
     */
    Product reduceStock(Integer productId, Integer amount) throws Exception;
 
}

 

package org.jeecg.modules.seata.service;
 
public interface OrderService {
 
    /**
     * 下订单
     *
     * @param userId 用户id
     * @param productId 产品id
     * @return 订单id
     * @throws Exception 创建订单失败,抛出异常
     */
    Integer createOrder(Integer userId, Integer productId) throws Exception;
 
}

 

package org.jeecg.modules.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 org.jeecg.modules.seata.service.impl;
 
import com.baomidou.dynamic.datasource.annotation.DS;
import io.seata.core.context.RootContext;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.modules.seata.entity.Product;
import org.jeecg.modules.seata.mapper.ProductMapper;
import org.jeecg.modules.seata.service.ProductService;
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() < amount) {
            throw new Exception("库存不足");
        }
 
        // 扣减库存
        int updateCount = productMapper.reduceStock(productId, amount);
        // 扣除成功
        if (updateCount == 0) {
            throw new Exception("库存不足");
        }
 
        // 扣除成功
        log.info("扣除 {} 库存成功", productId);
        return product;
    }
}

 

package org.jeecg.modules.seata.service.impl;
 
import com.baomidou.dynamic.datasource.annotation.DS;
import io.seata.core.context.RootContext;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.modules.seata.entity.Orders;
import org.jeecg.modules.seata.entity.Product;
import org.jeecg.modules.seata.mapper.OrdersMapper;
import org.jeecg.modules.seata.service.AccountService;
import org.jeecg.modules.seata.service.OrderService;
import org.jeecg.modules.seata.service.ProductService;
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; // 购买数量暂时设置为 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());
 
 
        //int a = 1/0;
        // 返回订单编号
        return order.getId();
    }
}

 

package org.jeecg.modules.seata.service.impl;
 
import com.baomidou.dynamic.datasource.annotation.DS;
import io.seata.core.context.RootContext;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.modules.seata.entity.Account;
import org.jeecg.modules.seata.mapper.AccountMapper;
import org.jeecg.modules.seata.service.AccountService;
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() < money.doubleValue()) {
            throw new Exception("余额不足");
        }
 
        // 扣除余额
        int updateCount = accountMapper.reduceBalance(userId, money);
        // 扣除成功
        if (updateCount == 0) {
            throw new Exception("余额不足");
        }
        log.info("扣除用户 {} 余额成功", userId);
    }
}

 

controller对应的代码如下

package org.jeecg.modules.seata.controller;
 
import lombok.extern.slf4j.Slf4j;
import org.jeecg.modules.seata.service.OrderService;
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);
    }
}

7. 测试结果

在浏览器请求

http://localhost:8080/jeecg-boot/order?userId=1&productId=1在这里插入图片描述

正常提交,数据库数据都是正常的。

http://localhost:8080/jeecg-boot/order?userId=2&productId=1在这里插入图片描述

更新异常,数据回滚。

分享到:
评论

相关推荐

    spring cloud 集成seata集成nacos集成feign的demo

    本项目“spring cloud 集成seata集成nacos集成feign的demo”旨在展示如何在Spring Cloud环境中整合Seata分布式事务管理、Nacos服务注册与配置中心以及Feign客户端调用,以实现高效且一致性的数据处理。 首先,Seata...

    springboot 集成seata MongoDB xxxl-job

    通过以上步骤,你可以构建一个集成了Spring Boot、MongoDB、Seata和Xxxl-job的系统,该系统具备强大的数据存储能力、分布式事务处理能力和任务调度功能。在实际应用中,这样的组合可以应用于电商平台、金融系统等对...

    项目中集成seata(分布式事务解决方案)

    项目中集成seata(分布式事务解决方案)

    seata-server-2.0.0.zip

    - 集成Seata客户端(Seata Client)到应用程序,配置TM和RM。 5. **监控与调试**: - Seata提供了Web管理界面,用于监控全局事务状态,查看异常信息,方便问题排查。 - 支持 Prometheus 和 Grafana 集成,进行...

    集成seata 分布式事务配置、sentienl 限流、熔断、降级、gateway 网关、jwt 、rocketmq

    ----------------------------rocketmq 消息队列 ---------------------------- brokerIP1=192.168.1.144 客户端控制台启动:java -jar rocketmq-console-ng-1.0.1.jar --rocketmq.config.namesrvAddr=192.168.1.144...

    分布式事务之Seata XA AT实战代码

    分布式事务在现代高并发、大数据量的互联网应用中扮演着至关重要的角色,它确保...通过与`zookeeper`的集成,以及对云原生架构的支持,Seata能够很好地适应现代企业的需求,为大规模分布式系统提供可靠的事务解决方案。

    seata-spring-boot-starter-1.3.0-API文档-中文版.zip

    赠送jar包:seata-spring-boot-starter-1.3.0.jar; 赠送原API文档:seata-spring-boot-starter-1.3.0-javadoc.jar; 赠送源代码:seata-spring-boot-starter-1.3.0-sources.jar; 赠送Maven依赖信息文件:seata-...

    springcloud集成分布式事务seata中的AT模式

    4. **开启AT模式**:在Spring Boot的主配置类上添加`@EnableSeata`注解,启动Seata的自动代理功能。 5. **数据库准备**:AT模式需要在数据库中创建Seata的全局事务表和元数据表,执行Seata提供的SQL脚本完成初始化...

    Spring Cloud与Seata 2.0集成:零基础安装到AT模式实战的完整教程.zip

    Spring Cloud与Seata 2.0的集成是一个涉及微服务架构和分布式事务管理的复杂过程。本教程将从零基础开始,指导你如何安装并配置Spring Cloud环境,以及如何集成Seata 2.0的AT模式。首先,你需要准备相应的开发环境,...

    【seata】基于RuoYi-Cloud集成分布式事务Seata.zip

    【seata】基于RuoYi-Cloud集成分布式事务Seata.zip 【seata】基于RuoYi-Cloud集成分布式事务Seata.zip 【seata】基于RuoYi-Cloud集成分布式事务Seata.zip 【seata】基于RuoYi-Cloud集成分布式事务Seata.zip

    seata-2.0.0

    Seata可以无缝集成到Spring Boot、Dubbo、MyBatis等主流框架中。通过简单的配置和注解,开发者可以快速地将分布式事务能力添加到现有的业务逻辑中。 6. 案例分析: 以电商平台为例,用户下单并支付成功后,涉及订单...

    seata集成springcloud详细步骤

    Seata集成SpringCloud详细步骤 Seata是一款开源的分布式事务解决方案,旨在提供高性能和高可用的分布式事务服务。SpringCloud是一款基于Java的微服务架构解决方案,旨在简化分布式系统的开发和维护。将Seata集成到...

    seata-1.7.0安装包

    6. 集成友好:Seata 通常与其他开源框架和中间件如Spring Boot、MyBatis等有良好的集成,1.7.0 版本可能增强了这方面的兼容性和用户体验。 7. 安装和部署:对于“seata-1.7.0”安装包,用户可以通过下载并解压压缩...

    seata 1.7 基于springcloud 和nacos搭建的客户端

    2. **集成 Seata:** 在你的项目中引入 Seata 的依赖,并配置 Seata Server 的地址和 Nacos 的配置。 3. **配置 Seata:** 在 Nacos 中创建 Seata 相关的配置,如 TC、TM、RM 的配置信息。 4. **开启 Seata 客户端:...

    seata-server-1.3.0.zip

    在使用Seata时,开发者还需要在应用中集成Seata的客户端SDK,如Java的`seata-spring-boot-starter`,并在业务代码中使用Seata提供的注解或API来开启分布式事务。 总的来说,Seata Server 1.3.0是解决分布式事务问题...

    Linux安装Seata教程

    以上步骤完成后,你的Linux服务器就已经成功安装并配置好了Seata,它可以作为一个分布式事务服务,支持与其他系统集成,提供全局事务的协调能力。在实际部署过程中,确保网络环境通畅,配置文件正确无误,以保证...

    seata server1.6.1资源双版本

    Seata 提供了对各种资源的适配器,如 MyBatis、JDBC、RocketMQ 等,以便于与不同的服务环境集成。 Seata Server 1.6.1 版本的发布,可能包含了以下改进: - 性能优化:可能提升了处理并发事务的能力,降低了延迟,...

    seata-server-0.7.0.zip

    在实际应用中,结合Spring Boot或Dubbo等框架,可以轻松地集成Seata,解决分布式环境下的事务一致性问题。同时,Seata提供了丰富的监控指标和日志,方便开发者调试和排查问题。在0.7.0版本之后,Seata还持续更新,...

    seata-server-1.4.0-all

    此外,由于Seata集成了Spring Boot,所以如果你的应用也基于Spring Boot,那么集成Seata将会更加顺畅。 总的来说,Seata-server-1.4.0-all提供了一套完整的分布式事务解决方案,结合Zookeeper和MySQL,可以有效地...

Global site tag (gtag.js) - Google Analytics