在二十、springCloudAlibaba-seata环境搭建TC(Server端)我们已经搭建好了seata的环境,那么我们这里来搭建一下项目的环境
一、案例
我们打算搭建两个服务,一个订单服务,一个库存服务,分别有自己的数据库,订单服务通过feign调用库存服务。伪代码如下
@Transactional
public String add(Long merchId) {
System.out.println("新增订单开始"+merchId);
Order order = new Order();
order.setOrderNo(1l);
order.setOrderName("新增订单,对应商品"+merchId);
orderMapper.insert(order);
System.out.println("新增订单结束"+merchId);
//这里是远程调用库存服务扣减库存
stockService.reduce(1l);
System.out.println("扣减库存结束"+merchId);
//这里报错
int i =1/0;
return null;
}
上面开始是在订单数据库插入一条订单数据,然后用feign调用远程库存服务扣减库存,扣减库存完后再执行一个i=1/0;方法触发异常导致该事务回滚,由于没有解决分布式事务的问题,结果应该是订单表没有数据,而库存却扣减成功。下面我们来搭建一下。
二、环境搭建准备
1、参考
五分钟搭建springboot2.7.16+druid+mybatis-plus环境
三、springCloudAlibaba-feign的简单使用
2、订单数据库
CREATE DATABASE order_seata;
CREATE TABLE tb_order(
order_no BIGINT not null primary key COMMENT '主键ID',
order_name VARCHAR(64) COMMENT '订单名字'
);
3、库存数据库
CREATE DATABASE stock_seata;
CREATE TABLE tb_stock(
merch_id BIGINT not null primary key COMMENT '主键ID',
count BIGINT COMMENT '库存'
);
INSERT INTO `tb_stock` (`merch_id`, `count`) VALUES (1, 100);
三、订单服务代码
1、依赖pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--服务注册与发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--数据库配置-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency>
<!--数据源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.22</version>
</dependency>
<!--自动化配置-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.22</version>
</dependency>
<!--mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
2、OrderController
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
OrderService orderService;
@RequestMapping("/add")
public String add(){
String msg = orderService.add(1l);
return "新增订单成功"+msg;
}
}
3、OrderMapper
@Repository
public interface OrderMapper extends BaseMapper<Order> {
}
4、StockService
@FeignClient(name="stock-service",path="/stock")
public interface StockService {
@RequestMapping("/reduce")
public String reduce(@RequestParam("merchId")Long merchId);
}
5、Order
@TableName("tb_order")
public class Order {
@TableField("order_no")
private Long orderNo;
@TableField("order_name")
private String orderName;
public Long getOrderNo() {
return orderNo;
}
public void setOrderNo(Long orderNo) {
this.orderNo = orderNo;
}
public String getOrderName() {
return orderName;
}
public void setOrderName(String orderName) {
this.orderName = orderName;
}
@Override
public String toString() {
return "Order{" +
"orderNo=" + orderNo +
", orderName='" + orderName + '\'' +
'}';
}
}
6、OrderService
public interface OrderService {
public String add(Long merchId);
}
7、OrderServiceImpl
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
StockService stockService;
@Autowired
OrderMapper orderMapper;
@Transactional
@Override
public String add(Long merchId) {
System.out.println("新增订单开始"+merchId);
Order order = new Order();
order.setOrderNo(1l);
order.setOrderName("新增订单,对应商品"+merchId);
orderMapper.insert(order);
System.out.println("新增订单结束"+merchId);
//新增订单
stockService.reduce(1l);
System.out.println("扣减库存结束"+merchId);
int i =1/0;
return null;
}
}
8、OrderNacosFeignSeataApplication
@SpringBootApplication
@MapperScan("com.suibibk.springCloud.order.mapper")
@EnableFeignClients
public class OrderNacosFeignSeataApplication {
public static void main( String[] args ) {
SpringApplication.run(OrderNacosFeignSeataApplication.class,args);
}
}
9、application.yml
server:
port: 8013
spring:
application:
name: order-nacos-feign-seata
cloud:
nacos:
server-addr: 127.0.0.1:8848
discovery:
username: nacos
password: nacos
namespace: public
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.192.19:3309/order_seata?serverTimezone=Asia/Shanghai
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource #Druid类型
四、库存服务代码
库存服务暂时不调用别的服务,所以不引入feign
1、引入依赖pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--服务注册与发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--数据库配置-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency>
<!--数据源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.22</version>
</dependency>
<!--自动化配置-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.22</version>
</dependency>
<!--mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
2、StockController
@RestController
@RequestMapping("/stock")
public class StockController {
@Autowired
StockService stockService;
@RequestMapping("/reduce")
public String reduce(Long merchId){
stockService.reduce(merchId);
return "库存扣减成功";
}
}
3、StockMapper
@Repository
public interface StockMapper extends BaseMapper<Stock> {
}
4、Stock
@TableName("tb_stock")
public class Stock {
@TableField("merch_id")
private Long merchId;
@TableField("count")
private Long count;
public Long getMerchId() {
return merchId;
}
public void setMerchId(Long merchId) {
this.merchId = merchId;
}
public Long getCount() {
return count;
}
public void setCount(Long count) {
this.count = count;
}
@Override
public String toString() {
return "Stock{" +
"merchId=" + merchId +
", count=" + count +
'}';
}
}
5、StockService
public interface StockService {
void reduce(Long merchId);
}
6、StockServiceImpl
@Service
public class StockServiceImpl implements StockService {
@Autowired
StockMapper stockMapper;
@Transactional
@Override
public void reduce(Long merchId) {
System.out.println("扣减库存开始"+merchId);
QueryWrapper<Stock> queryWrapper = new QueryWrapper<Stock>();
queryWrapper.eq("merch_id",1l);
Stock stock= stockMapper.selectOne(queryWrapper);
UpdateWrapper<Stock> updateWrapper = new UpdateWrapper<Stock>();
updateWrapper.eq("merch_id",1l);
stock.setCount(stock.getCount()-1);
stockMapper.update(stock,updateWrapper);
System.out.println("扣减库存成功"+merchId);
}
}
7、StockApplication
@SpringBootApplication
@MapperScan("com.suibibk.springCloud.stock.mapper")
public class StockApplication {
public static void main( String[] args ) {
SpringApplication.run(StockApplication.class,args);
}
}
8、application.yml
server:
port: 8014
spring:
application:
name: stock-service
cloud:
nacos:
server-addr: 127.0.0.1:8848
discovery:
username: nacos
password: nacos
namespace: public
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.192.19:3309/stock_seata?serverTimezone=Asia/Shanghai
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource #Druid类型
五、启动测试
启动后可以看到nacos
访问http://localhost:8013/order/add 可以看到返回
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Wed Nov 08 21:01:51 CST 2023
There was an unexpected error (type=Internal Server Error, status=500).
表明执行到了预想中的逻辑1/0,再去看订单库中的订单表和库存库中的库存表,发现订单表没有记录,库存减了1.
达到预期效果,但是业务肯定得保证事务,所以订单下单报错,库存也不能扣减,接下来就尝试使用seata解决这个问题!
可能会遇到feign调用参数传递不过去的问题请注意
FeignClient中的方法有参数传递一般要加@RequestParam(“xxx”)注解