尽管世界和人生是坏透了,其中却有一件东西永远是好,那便是青春——显克维奇
首先是下载seata1.4.1
然后解压
先修改conf
下的registry.conf
把type
改为nacos
进入bin
打开控制台运行seata-server.bat
然后在项目中引入依赖,记得需要分布式式事务的服务都要配置
1 2 3 4 5
| <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> </dependency>
|
配置类
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
| package com.ruben.config;
import com.alibaba.druid.pool.DruidDataSource; import com.zaxxer.hikari.HikariDataSource; import io.seata.rm.datasource.DataSourceProxy; import org.apache.commons.lang3.StringUtils; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource; import javax.sql.DataSource; import java.util.Optional;
@Configuration public class SeataConfig {
@Bean @ConfigurationProperties(prefix = "spring.datasource") public DataSource druidDataSource(){ return new DruidDataSource(); }
}
|
然后是项目中的配置文件
1 2 3 4 5 6 7 8 9 10
| seata: enabled: true application-id: ${spring.application.name} tx-service-group: ruben config: type: file file: name: file.conf service: disable-global-transaction: false
|
如果之后启动一直报没配置disableGlobalTransaction
就需要新建一个file.conf
到resources
目录下
内容为
1 2 3
| service{ disableGlobalTransaction = false }
|
接下来是编写两个接口
首先是调用方
controller
1 2 3 4 5 6 7 8 9 10 11
| @RestController @RequestMapping("user") public class UserController { @Resource private UserService userService; @GetMapping("order") public AjaxJson order() { return userService.order(); } }
|
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @Slf4j @Service public class UserServiceImpl implements UserService { @Resource private MpOrderMapper mpOrderMapper; @Resource private ConsumerService consumerService; @Override @Transactional @GlobalTransactional public AjaxJson order() { consumerService.dropWare(); mpOrderMapper.insert(OrderPO.builder().id(1L).build()); return AjaxJson.success(); } }
|
ConsumerService
是使用feign
远程调用另一个接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package com.ruben.feign;
import com.ruben.pojo.dto.PageDTO; import com.ruben.utils.AjaxJson; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestBody;
@FeignClient("ruben-consumer") public interface ConsumerService {
@GetMapping("ware") AjaxJson dropWare(); }
|
MpOrderMapper
是mybatis-plus
调用数据库的方法
1 2 3 4 5 6 7 8 9
| package com.ruben.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.ruben.pojo.po.OrderPO; import org.apache.ibatis.annotations.Mapper;
@Mapper public interface MpOrderMapper extends BaseMapper<OrderPO> { }
|
然后是调用的ruben-consumer
首先也是引入依赖
其次是配置
1 2 3 4 5 6 7 8 9 10
| seata: enabled: true application-id: ${spring.application.name} tx-service-group: SEATA_GROUP config: type: file file: name: file.conf service: disable-global-transaction: false
|
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
| package com.ruben.rubenproducerdemo.config;
import com.alibaba.druid.pool.DruidDataSource; import com.zaxxer.hikari.HikariDataSource; import io.seata.rm.datasource.DataSourceProxy; import org.apache.commons.lang3.StringUtils; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource; import javax.sql.DataSource; import java.util.Optional;
@Configuration public class SeataConfig {
@Bean @ConfigurationProperties(prefix = "spring.datasource") public DataSource druidDataSource(){ return new DruidDataSource(); }
}
|
然后是接口
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
| package com.ruben.rubenproducerdemo.controller;
import com.ruben.rubenproducerdemo.service.WareService; import com.ruben.rubenproducerdemo.utils.AjaxJson; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController @RequestMapping("ware") public class WareController { @Resource private WareService wareService;
@GetMapping public AjaxJson dropWare() { return wareService.dropWare(); } }
|
service
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
| package com.ruben.rubenproducerdemo.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruben.dao.WareMapper; import com.ruben.rubenproducerdemo.pojo.po.WarePO; import com.ruben.rubenproducerdemo.service.WareService; import com.ruben.rubenproducerdemo.utils.AjaxJson; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional;
import java.util.Optional;
@Service public class WareServiceImpl extends ServiceImpl<WareMapper, WarePO> implements WareService {
@Override @Transactional public AjaxJson dropWare() { WarePO ware = this.getById("1"); Optional.ofNullable(ware).map(WarePO::getWare).filter(w -> w <= 0).ifPresent(wi -> { throw new RuntimeException("卖光啦!"); }); Optional.ofNullable(ware).ifPresent(w -> this.updateById(WarePO.builder().id("1").ware(w.getWare() - 1).build())); return AjaxJson.success("成功"); } }
|
我们开始测试
往数据库放条数据
首次调用接口时可以看到订单生成并且库存成功扣减
第二次调用发现抛出异常并成功回滚库存服务
至此,成功使用了seata
实现了分布式事务