seata—TCC事务

有侵入的事务方案

TCC–两个阶段的三个事务操作

在这里插入图片描述

  • Try–预留资源,冻结

  • Confirm–确认资源,使用第一阶段冻结的数据来完成业务数据处理,commit
  • Cancel–取消预留的资源,把第一阶段冻结的数据,恢复回去,rollback

1.seata依赖

2.三个配置文件

  • application.yml–更改组名
  • registry.conf–注册中心地址
  • file.conf–组对应使用的协调器

3.修改Mapper,添加新的数据操作

orderMapper:

package cn.tedu.mapper;

import cn.tedu.entity.Order;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

public interface OrderMapper extends BaseMapper<Order> {
    void create(Order order);
    //插入冻结订单
    void insertFrozen(Order order);
    //修改订单状态
    void updateStatus(Long orderId,Integer status);
    //删除订单
}

修改映射文件,添加sql语句

	<insert id="insertFrozen">
        INSERT INTO `order` (`id`,`user_id`,`product_id`,`count`,`money`,`status`)
        VALUES(#{id}, #{userId}, #{productId}, #{count}, #{money},0);
    </insert>
    <update id="updateStatus">
        update `order` set status=#{status} where id=#{orderId}
    </update>
    <delete id="deleteById">
        delete from `order` where identity =#{orderId}
    </delete>

4.按照seata的规则,添加TccAction接口和实现,使用Mapper完成TCC数据库操作

接口

package cn.tedu.tcc;

import io.seata.rm.tcc.api.LocalTCC;
import io.seata.rm.tcc.api.TwoPhaseBusinessAction;

/*
 * tcc操作接口
* 定义三个操作的方法
* */
@LocalTCC
public interface OrderTccActrion {
    @TwoPhaseBusinessAction(
            name = "OrderTccActrion",
            commitMethod = "commit",
            rollbackMethod = "rollback")//T方法注解,名字随意起
    boolean prepare(BusinessActionContext ctx,
                    @BusinessActionContextParameter(paramName = "orderId") Long orderId,//将此属性放入ctx中
                    Long userId,
                    Long productId,
                    Integer count,
                    BigDecimal money);//为了避开seata的一个bug,不传order封装对象,而是把数据打散,一个个的单独传递
    boolean commit(BusinessActionContext ctx);
    boolean rollback(BusinessActionContext ctx);
}

实现类

@Component
public class OrderTccActionImpl implements OrderTccAction {
    @Autowired
    private OrderMapper orderMapper;

    @Transactional
    @Override
    public boolean prepare(BusinessActionContext ctx, Long orderId, Long userId, Long productId, Integer count, BigDecimal money) {
        orderMapper.insertFrozen(
                new Order(orderId,userId,productId,count,money,0)
        );
        return true;
    }

    @Transactional
    @Override
    public boolean commit(BusinessActionContext ctx) {
        Long orderId = Long.valueOf(ctx.getActionContext("orderId").toString());//orderId类型消失,需要重新变成Long
        orderMapper.updateStatus(orderId,1);
        return true;
    }

    @Transactional
    @Override
    public boolean rollback(BusinessActionContext ctx) {
        Long orderId = Long.valueOf(ctx.getActionContext("orderId").toString());//orderId类型消失,需要重新变成Long
        orderMapper.updateStatus(orderId,1)
        return true;
    }
}

5.修改业务方法,调用TccAction的第一阶段方法来冻结数据

OrderServiceImpl:

//        orderMapper.create(order);
        //手动调用第一阶段,冻结订单
        //第二阶段的两个方法,是由seata的rm自动调用
        tcc.prepare(null,
                order.getId(),
                order.getUserId(),
                order.getProductId(),
                order.getCount(),
                order.getMoney());

6.在第一个模块上添加:@GlobalTransactional 启动全局事务

    @GlobalTransactional
    @Override
    public void create(Order order) {
        // TODO: 从全局唯一id发号器获得id
        String s = easyIdClient.getId("order_business");
        Long orderId = Long.valueOf(s);
        order.setId(orderId);

//        orderMapper.create(order);
        //手动调用第一阶段,冻结订单
        //第二阶段的两个方法,是由seata的rm自动调用
        tcc.prepare(null,
                order.getId(),
                order.getUserId(),
                order.getProductId(),
                order.getCount(),
                order.getMoney());

子模块

storage模块

1.Mapper映射文件
 	<select id="selectByProductId" resultMap="BaseResultMap">
        select * from storage where product_id = #{productId}
    </select>

    <update id="updateResidueToFrozen">
        update storage
        set Residue=Residue-#{count},Frozen=Frozen+#{count}
        where product_id = #{productId}
    </update>

    <update id="updateFrozenToUsed">
        update storage
        set Frozen = Frozen - #{count},Used = Used+ #{count}
        where product_id = #{productId}
    </update>

    <update id="updateFrozenToResidue">
        update storage
        set Frozen = Frozen - #{count},Residue = Residue+ #{count}
        where product_id = #{productId}
    </update>
2.Mapper:
 // 查询库存
    Storage selectByProductId(Long productId);
    // 可用 ---> 冻结
    void updateResidueToFrozen(Long productId, Integer count);
    // 冻结 ---> 已使用
    void updateFrozenToUsed(Long productId, Integer count);
    // 冻结 ---> 可用
    void updateFrozenToResidue(Long productId, Integer count);
3.Service实现类
@Service
public class StorageServiceImpl implements StorageService {
    @Autowired
    private StorageTccAction tcc;

    @Override
    public void decrease(Long productId, Integer count) {
        // 不直接直接减库存的业务,而是冻结库存
        tcc.prepare(null, productId, count);
    }
}
4.TCC

接口:

@LocalTCC
public interface StorageTccAction {
    @TwoPhaseBusinessAction(name = "StorageTccAction")
    boolean prepare(BusinessActionContext ctx,
                    @BusinessActionContextParameter(paramName = "productId") Long productId,
                    @BusinessActionContextParameter(paramName = "count") Integer count);

    boolean commit(BusinessActionContext ctx);
    boolean rollback(BusinessActionContext ctx);
}

实现类

@Component
public class StorageTccActionImpl implements StorageTccAction {
    @Autowired
    private StorageMapper storageMapper;
    @Transactional
    @Override
    public boolean prepare(BusinessActionContext ctx, Long productId, Integer count) {
        // 查询库存判断有没有足够的库存
        Storage storage = storageMapper.selectByProductId(productId);
        if (storage.getResidue() < count) {
            throw new RuntimeException("库存不足");
        }

        // 冻结库存
        storageMapper.updateResidueToFrozen(productId, count);
        return true;
    }
    @Transactional
    @Override
    public boolean commit(BusinessActionContext ctx) {
        Long productId = Long.valueOf(ctx.getActionContext("productId").toString());
        Integer count = Integer.valueOf(ctx.getActionContext("count").toString());

        storageMapper.updateFrozenToUsed(productId, count);
        return true;
    }
    @Transactional
    @Override
    public boolean rollback(BusinessActionContext ctx) {
        Long productId = Long.valueOf(ctx.getActionContext("productId").toString());
        Integer count = Integer.valueOf(ctx.getActionContext("count").toString());

        storageMapper.updateFrozenToResidue(productId, count);
        return true;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值