Springboot+nacos+seata实现简单分布式事务经验分享:二

Springboot+nacos+seata实现简单的分布式事务

上一篇文章把三个服务都注册进nacos中了,这次就开始写业务代码

首先先创建三个数据库,每个数据库都需要有一张记录表,一张回滚表

order表

CREATE TABLE `t_order` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `user_id` bigint DEFAULT NULL ,
  `product_id` bigint DEFAULT NULL ,
  `count` int DEFAULT NULL ,
  `money` decimal(11,0) DEFAULT NULL ,
  `status` int DEFAULT NULL ,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8;


CREATE TABLE `undo_log` (
  `branch_id` bigint NOT NULL COMMENT 'branch transaction id',
  `xid` varchar(128) NOT NULL COMMENT 'global transaction id',
  `context` varchar(128) NOT NULL COMMENT 'undo_log context,such as serialization',
  `rollback_info` longblob NOT NULL COMMENT 'rollback info',
  `log_status` int NOT NULL COMMENT '0:normal status,1:defense status',
  `log_created` datetime(6) NOT NULL COMMENT 'create datetime',
  `log_modified` datetime(6) NOT NULL COMMENT 'modify datetime',
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;

storage表

CREATE TABLE `t_storage` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `product_id` bigint DEFAULT NULL,
  `total` int DEFAULT NULL,
  `used` int DEFAULT NULL,
  `residue` int DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

--在表中插入一条数据
INSERT INTO `t_storage` VALUES ('1', '1', '100', '0', '100');

CREATE TABLE `undo_log` (
  `branch_id` bigint NOT NULL COMMENT 'branch transaction id',
  `xid` varchar(128) NOT NULL COMMENT 'global transaction id',
  `context` varchar(128) NOT NULL COMMENT 'undo_log context,such as serialization',
  `rollback_info` longblob NOT NULL COMMENT 'rollback info',
  `log_status` int NOT NULL COMMENT '0:normal status,1:defense status',
  `log_created` datetime(6) NOT NULL COMMENT 'create datetime',
  `log_modified` datetime(6) NOT NULL COMMENT 'modify datetime',
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='AT transaction mode undo table';

account表

CREATE TABLE `t_account` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `user_id` bigint DEFAULT NULL,
  `total` decimal(10,0) DEFAULT NULL,
  `used` decimal(10,0) DEFAULT NULL,
  `residue` decimal(10,0) DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

--插入一条数据
INSERT INTO `t_account` VALUES ('1', '1', '1000', '0', '1000');

CREATE TABLE `undo_log` (
  `branch_id` bigint NOT NULL COMMENT 'branch transaction id',
  `xid` varchar(128) NOT NULL COMMENT 'global transaction id',
  `context` varchar(128) NOT NULL COMMENT 'undo_log context,such as serialization',
  `rollback_info` longblob NOT NULL COMMENT 'rollback info',
  `log_status` int NOT NULL COMMENT '0:normal status,1:defense status',
  `log_created` datetime(6) NOT NULL COMMENT 'create datetime',
  `log_modified` datetime(6) NOT NULL COMMENT 'modify datetime',
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

到此,三个数据库六张表建好了下面写代码

order服务

新建order实体

public class Order {
    private int id;

    private int userId;

    private int productId;

    private int count;

    private int money;

    private int status;
}

//我这自己创建了get,set方法

OrderMapper类,用于操作数据库

@Mapper
public interface OrderMapper {

    @Insert("insert into t_order(user_id,product_id,count,money,status) values(#{userId},#{productId},#{count},#{money},#{status})")
    int create(Order order);

    @Update("update t_order set status = #{status} where user_id = 1;")
    void update(int status);

}

StorageService接口用于使用openfeign调用另一个服务

//@FeignClient:将远程服务xxx映射为一个本地Java方法调用
@FeignClient(name = "seataStorage")
public interface StorageService {

    @PostMapping(value = "/storage/decrease")
    int decrease(@RequestParam("count") Object count,@RequestParam("XID") String XID);
}

/**
*count参数是自己定义的用于模拟发生条件不成立而回滚,XID就是TC 针对这个全局事务生成一个全局唯一的 XID
*XID参数必须传,如果不传的话Storage服务不会回滚
*/

AccountService接口用于使用openfeign调用另一个服务

@FeignClient(name = "seataAccount")
public interface AccountService {

    @PostMapping(value = "/account/decrease")
    int decrease(@RequestParam("money") Object money , @RequestParam("XID") String XID);
}

OrderService接口及其实现类

public interface OrderService {
    int create(Order order);
}


@Service
public class OrderImpl implements OrderService {

    @Autowired
    OrderMapper orderMapper;
    @Resource
    AccountService accountService;
    @Resource
    StorageService storageService;

    @Override
    //这个注解表示是一个分布式事务,在这个注解包括的代码块中,不管事本服务的sql还是feign调用的服务的sql都会被seata增强
    @GlobalTransactional(name = "service.vgroupMapping.mytest", rollbackFor = Exception.class)
    public int create(Order order) {
        order.setUserId(1);
        order.setStatus(0);
        orderMapper.create(order);
        System.out.println(RootContext.getXID());
        storageService.decrease(order.getCount(),RootContext.getXID());
        accountService.decrease(order.getMoney(),RootContext.getXID());
        orderMapper.update(0);
        return 1;
    }
}

OrderController类用于接口调用

@RestController
public class OrderController {

    @Autowired
    OrderService orderService;

    @PostMapping("/test")
    public void create(int count , int money) {
        Order order = new Order();
        order.setProductId(100);
        order.setCount(count);
        order.setMoney(money);
        orderService.create(order);
    }
}

启动类

@SpringBootApplication
//让注册中心能够发现,扫描到该服务
@EnableDiscoveryClient
//告诉框架扫描所有使用注解@FeignClient定义的feign客户端
@EnableFeignClients(basePackages = "com.example.springdemoOrder")
public class Springdemo1Application {

    public static void main(String[] args) {
        SpringApplication.run(Springdemo1Application.class, args);
    }
}
Storage服务

新建order实体

public class Storage {
    private int id;
    private int productId;
    private int total;
    private int used;
    private int residue;
    //自己建get,set方法
}

StorageMapper接口

@Mapper
public interface StorageMapper {

    @Results(id = "storage" , value = {
            @Result(property = "id" , column = "id" , id = true),
            @Result(property = "productId" , column = "product_id"),
            @Result(property = "total" , column = "total"),
            @Result(property = "used" , column = "used"),
            @Result(property = "residue" , column = "residue")
    })
    @Select("select * from t_storage where id = #{id}")
    Storage selectByProductId(int id);

    @Update("update t_storage set total = #{total},used = #{used} , residue = #{residue} where id = #{id}")
    int update(Storage record);
}

StorageService接口及其实现类

public interface StorageService {
    int decrease(Object count);
}

@Service
public class StorageServiceImpl implements StorageService {
    @Autowired
    StorageMapper storageMapper;

    @Override
    @Transactional
    public int decrease(Object count) {
        Storage storage = storageMapper.selectByProductId(1);
        int co = (int) count;
        if (storage != null && storage.getResidue().intValue() >= co) {
            Storage storage2 = new Storage();
            storage2.setId(1);
            storage.setUsed(storage.getUsed() + co);
            storage.setResidue(storage.getTotal().intValue() - storage.getUsed());
            int decrease = storageMapper.update(storage);
            System.out.println("成功");
            return decrease;
        } else {
            System.out.println("开始回滚!");
            throw new RuntimeException("失败!");
        }
    }
}

StorageController类

@RestController
@RequestMapping("/storage")
public class StorageController {
    @Autowired
    StorageService storageService;

    @PostMapping("/decrease")
    public int  test(@RequestParam("count") String count, @RequestParam("XID") String XID){
        int num = Integer.valueOf(count);
        RootContext.bind(XID);
        System.out.println(RootContext.getXID());
        int decrease = storageService.decrease(num);
        int result;
        if (decrease > 0) {
            result = 1;
        } else {
            result = 0;
        }
        return result;
    }
}
Account服务

Account实体

public class Account {
    private int id;
    private int userId;
    private int total;
    private int used;
    private int residue;
}

AccountMapper接口

@Mapper
public interface AccountMapper {
    @Results(id = "acount",value = {
            @Result(property = "id",column = "id",id = true),
            @Result(property = "userId",column = "user_id"),
            @Result(property = "total",column = "total"),
            @Result(property = "used",column = "used"),
            @Result(property = "residue",column = "residue")
    })
    @Select("select * from t_account where id = 1")
    Account selectByUserId();

    @Update("update t_account set used = #{used} , residue = #{residue} where id = 1")
    int decrease(Account account);

}

ACcountService接口及其实现类

public interface AccountService {
    int decrease(Object money);
}

@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    AccountMapper accountDao;

    @Override
    @Transactional
    public int decrease(Object money) {
        Account account = accountDao.selectByUserId();
        int used = account.getUsed();
        int residue = account.getResidue();
        int mo = (int) money;
        //如果传过来的参数小于数据库的数据就回滚
        if (mo<residue){
            account.setUsed(used+mo);
            account.setResidue(residue-mo);
            accountDao.decrease(account);
        }else {
            throw new RuntimeException("错误");
        }
        return 1;
    }
}

AccountController类

@RestController
@RequestMapping("/account")
public class AccountController {
    @Autowired
    AccountService account;

    @PostMapping("/decrease")
    public int test(@RequestParam("money") String money ,@RequestParam("XID") String XID){
        RootContext.bind(XID);
        System.out.println(RootContext.getXID());
        int m = Integer.valueOf(money);
        int decrease = account.decrease(m);
        int result;
        if (decrease==1){
            result = 1;
        }else {
            result = 0;
        }
        return result;
    }
}

接下来启动三个服务试试,然后调用接口试试正常的情况
在这里插入图片描述
storage表
在这里插入图片描述
account表
在这里插入图片描述
接下来我们模拟回滚
参数:
在这里插入图片描述
然后发现主服务抛出错误
在这里插入图片描述
但是我们也没法知道到底发没发生回滚啊,去idea打个断点试试,因为我们是模拟的account服务抛出错误,所以在主服务的OrderImpl打个断点试试
在这里插入图片描述
进入断点
在这里插入图片描述
Order表已经插入数据了
在这里插入图片描述
Order表的回滚表
在这里插入图片描述
Storage表也修改了数据
在这里插入图片描述
Storage表的回滚表

在这里插入图片描述
断点去了之后,因为account服务抛出错误,所以开始回滚

Order表
在这里插入图片描述
Storage表
在这里插入图片描述
到这里就结束了,代码地址https://github.com/Melons-skin/seata-nacos-springboot-

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值