1.创建微服务数据表
- 建库建表
undo_log建表语句地址:https://gitee.com/seata-io/seata/blob/develop/script/client/at/db/mysql.sql
-- 订单表 ----------------------------
DROP TABLE IF EXISTS `tb_item`;
CREATE TABLE `tb_item` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`num` int(11) DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
-- Records of tb_item ----------------------------
INSERT INTO `tb_item` VALUES ('1', '手机', '100');
INSERT INTO `tb_item` VALUES ('3', '电脑', '100');
-- for AT mode you must to init this sql for you business database. the seata server not need it.
CREATE TABLE IF NOT EXISTS `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(11) 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
AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';
- 建库建表
建表语句:
-- Table structure for tb_order ----------------------------
DROP TABLE IF EXISTS `tb_order`;
CREATE TABLE `tb_order` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`item_id` int(11) DEFAULT NULL,
`num` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- for AT mode you must to init this sql for you business database. the seata server not need it.
CREATE TABLE IF NOT EXISTS `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(11) 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
AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';
2. 创建工程
2.1 创建seata_demo
- pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bjpowernode</groupId>
<artifactId>seata_demo</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<!-- 自定义属性标签 -->
<properties>
<!-- 项目源码及编译输出的编码 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- 项目编译JDK版本 -->
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring-boot-version>2.3.2.RELEASE</spring-boot-version>
<spring-cloud-version>Hoxton.SR9</spring-cloud-version>
<spring-cloud-alibaba-version>2.2.6.RELEASE</spring-cloud-alibaba-version>
<mybatis-version>3.5.1</mybatis-version>
<mysql-connector-java-version>5.1.38</mysql-connector-java-version>
<druid-version>1.0.9</druid-version>
<spring-mybatis-version>2.0.1</spring-mybatis-version>
</properties>
<!--管理依赖版本号-->
<dependencyManagement>
<dependencies>
<!--Spring Boot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot-version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--spring cloud Netflix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud-version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--spring cloud 阿里巴巴-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba-version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis-version}</version>
</dependency>
<!-- MySql Driver -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector-java-version}</version>
</dependency>
<!--Alibaba DataBase Connection Pool-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid-version}</version>
</dependency>
<!--MyBatis And Spring Integration Starter-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${spring-mybatis-version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<resources>
<!-- mapper.xml文件在java目录下 -->
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<!--解决maven项目resources目录显示为普通目录问题-->
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
</build>
</project>
2.2 创建common_pojo
- pojo逆向工程造一下
2.3 创建seata_item_service
- pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>seata_demo</artifactId>
<groupId>com.bjpowernode</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>seata_item_service</artifactId>
<dependencies>
<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>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</dependency>
<!-- MySql Driver -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--Alibaba DataBase Connection Pool-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<!--MyBatis And Spring Integration Starter-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.bjpowernode</groupId>
<artifactId>common_pojo</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
- application.yml
8server:
port: 8091
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.112.137:3306/seata_demo?characterEncoding=UTF-8
username: root
password: 1111
type: com.alibaba.druid.pool.DruidDataSource
cloud:
nacos:
discovery:
server-addr: 192.168.112.133:8848
application:
name: seata-item-service
- mapper
逆向工程一下 - service
import com.bjpowernode.mapper.TbItemMapper;
import com.bjpowernode.pojo.TbItem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class ItemServiceImpl implements ItemService {
@Autowired
private TbItemMapper tbItemMapper;
@Override
public void updateItem(Integer itemId, Integer num) {
TbItem tbItem = tbItemMapper.selectByPrimaryKey(itemId);
tbItem.setNum(tbItem.getNum()-num);
tbItemMapper.updateByPrimaryKeySelective(tbItem);
}
}
- controller
import com.bjpowernode.service.ItemService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/item")
public class ItemController {
@Autowired
private ItemService itemService;
@RequestMapping("/updateItem")
public void updateItem(Integer itemId, Integer num){
itemService.updateItem(itemId,num);
}
}
- 启动类
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
@MapperScan("com.alicc.mapper")
public class SeataItemServiceApp {
public static void main(String[] args) {
SpringApplication.run(SeataItemServiceApp.class, args);
}
}
3 创建seata_item_feign
- feign
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient("seata-item-service")
@RequestMapping("/item")
public interface ItemServiceFeign {
@RequestMapping("/updateItem")
public void updateItem(@RequestParam("itemId") Integer itemId,
@RequestParam("num") Integer num);
}
4 创建seata_order_service
- pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>seata_demo</artifactId>
<groupId>com.bjpowernode</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>seata_order_service</artifactId>
<dependencies>
<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>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</dependency>
<!-- MySql Driver -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--Alibaba DataBase Connection Pool-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<!--MyBatis And Spring Integration Starter-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.bjpowernode</groupId>
<artifactId>common_pojo</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.bjpowernode</groupId>
<artifactId>seata_item_feign</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
- application.yml
server:
port: 8090
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.112.137:3306/seata_demo?characterEncoding=UTF-8
username: root
password: 1111
type: com.alibaba.druid.pool.DruidDataSource
cloud:
nacos:
discovery:
server-addr: 192.168.112.133:8848
application:
name: seata-order-service
- mapper
逆向工程 - service
import com.bjpowernode.feign.ItemServiceFeign;
import com.bjpowernode.mapper.TbOrderMapper;
import com.bjpowernode.pojo.TbOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private TbOrderMapper tbOrderMapper;
@Autowired
private ItemServiceFeign itemServiceFeign;
@Transactional
@Override
public void insertOrder(TbOrder tbOrder) {
//1、保存订单
tbOrderMapper.insertSelective(tbOrder);
//2、扣减库存
itemServiceFeign.updateItem(tbOrder.getItemId(), tbOrder.getNum());
//模拟扣款失败,此时扣减库存会回滚吗?
int a = 6/0;
}
}
- controller
import com.bjpowernode.pojo.TbOrder;
import com.bjpowernode.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
@RequestMapping("/insertOrder")
public Map insertOrder(TbOrder tbOrder){
HashMap<Integer, Object> result = new HashMap<>();
try {
orderService.insertOrder(tbOrder);
result.put(200,"下单成功");
return result;
} catch (Exception e) {
e.printStackTrace();
result.put(500,"下单失败");
return result;
}
}
}
- 启动类
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@MapperScan("com.alicc.mapper")
public class SeataOrderServiceApp {
public static void main(String[] args) {
SpringApplication.run(SeataOrderServiceApp.class, args);
}
}
测试
- 浏览器访问:http://127.0.0.1:8090/order/insertOrder?userId=2&itemId=1&num=4
- 扣款失败时,订单事务回滚,商品事务不回滚,库存丢失