springboot2整合seata(AT模式)
1.启动seata服务
docker启动seata-server实例,使用1.3.0版本
docker pull seataio/seata-server:1.3.0
docker run --name seata-server -p 8091:8091 seataio/seata-server
2.创建三个项目实现下订单、库存减少分布式事物
dkn-provider-order 下订单服务
dkn-provider-store 库存服务
dkn-shop 模拟用户购买商品服务
1.dkn-provider-orde和dkn-provider-store的pom.xml
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.3.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.8.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR8</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-seata</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
2.yml配置文件 dkn-provider-orde和dkn-provider-store
端口,服务名称、数据库信息,application-id需要换下
server:
port: 9102
spring:
application:
name: dkn-provider-order
datasource:
druid:
url: jdbc:mysql://localhost:3306/dkn-provider-order?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
driverClassName: com.mysql.cj.jdbc.Driver
username: root
password: root
seata:
enabled: true
application-id: dkn-provider-order
tx-service-group: dkn_tx_group
enable-auto-data-source-proxy: false #一定要是false
service:
vgroup-mapping:
dkn_tx_group: default #key与上面的tx-service-group的值对应
grouplist:
default: 10.0.0.70:8091 #seata-server地址仅file注册中心需要
config:
type: file
registry:
type: file
logging:
level:
com.dkn: debug
3.SeataConfig配置,这个都一样,dkn-shop不需要添加
@Configuration
public class SeataConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.druid")
public DataSource druidDataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
return druidDataSource;
}
@Primary
@Bean("dataSource")
public DataSourceProxy dataSource(DataSource druidDataSource){
return new DataSourceProxy(druidDataSource);
}
}
4.service和controller
@Service
public class DknOrderServiceImpl extends ServiceImpl<DknOrderMapper,DknOrder> implements DknOrderService {
@Override
public boolean add(Integer productid, Integer num) {
DknOrder dknOrder=new DknOrder();
dknOrder.setNum(num);
dknOrder.setProductid(productid);
return this.save(dknOrder);
}
}
@Service
public class StoreServiceImpl extends ServiceImpl<StoreMapper,Store> implements StoreService {
@Override
public boolean reduce(Integer productid,Integer n) {
UpdateWrapper<Store> updateWrapper=new UpdateWrapper<Store>();
updateWrapper.setSql("num = num - "+n);
updateWrapper.eq("productid", productid);
return this.update(updateWrapper);
}
}
@RestController
@RequestMapping("/Order")
public class DknOrderController {
@Autowired
private DknOrderService dknOrderService;
@GetMapping("add")
public AjaxResult add(Integer productid, Integer num){
boolean result=dknOrderService.add(productid,num);
if(result){
return AjaxResult.success();
}else{
return AjaxResult.error();
}
}
}
@RestController
@RequestMapping("/Store")
public class StoreController {
@Autowired
private StoreService storeService;
@GetMapping("reduce")
public AjaxResult reduce(Integer productid,Integer num){
boolean result=storeService.reduce(productid,num);
if(result){
return AjaxResult.success();
}else{
return AjaxResult.error();
}
}
}
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class StoreApplication {
public static void main(String[] args) {
SpringApplication.run(StoreApplication.class, args);
}
}
5.dkn-shop配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-seata</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
server:
port: 9100
spring:
application:
name: dkn-shop
seata:
enabled: true
application-id: dkn-shop
tx-service-group: dkn_tx_group
enable-auto-data-source-proxy: false #一定要是false
service:
vgroup-mapping:
dkn_tx_group: default #key与上面的tx-service-group的值对应
grouplist:
default: 10.0.0.70:8091 #seata-server地址仅file注册中心需要
config:
type: file
registry:
type: file
#开启使用 httpclient
feign:
httpclient:
enabled: true
logging:
level:
com.dkn: debug
org.apache.http: debug
@EnableFeignClients
@SpringBootApplication
public class ShopApplication {
public static void main(String[] args) {
SpringApplication.run(ShopApplication.class, args);
}
}
@FeignClient(name="dkn-provider-order", url = "http://localhost:9102")
@Service
public interface OrderService {
@GetMapping("/Order/add")
public AjaxResult add(@RequestParam(name = "productid") Integer productid, @RequestParam(name = "num") Integer num);
}
@FeignClient(name="dkn-provider-store", url = "http://localhost:9101")
@Service
public interface StoreService {
@GetMapping("/Store/reduce")
public AjaxResult reduce(@RequestParam(name = "productid") Integer productid, @RequestParam(name = "num") Integer num);
}
@Service
public class ShopService {
@Autowired
OrderService orderService;
@Autowired
StoreService storeService;
@GlobalTransactional
public void buy(Integer productid,Integer num){
orderService.add(productid,num);
storeService.reduce(productid,num);
}
@GlobalTransactional
public void buyError(Integer productid,Integer num){
orderService.add(productid,num);
int a=1/0;
storeService.reduce(productid,num);
}
}
@RestController
@RequestMapping("/Shop")
public class ShopController {
@Autowired
private ShopService shopService;
//测试正常 http://localhost:9100/Shop/buy?productid=101&num=2
@GetMapping("buy")
public AjaxResult buy(Integer productid, Integer num){
shopService.buy(productid,num);
return AjaxResult.success();
}
//测试错误 http://localhost:9100/Shop/buy?productid=101&num=2
@GetMapping("buyError")
public AjaxResult buyError(Integer productid, Integer num){
shopService.buyError(productid,num);
return AjaxResult.success();
}
}
6.数据库文件
-- ----------------------------
-- Table structure for store
-- ----------------------------
DROP TABLE IF EXISTS `store`;
CREATE TABLE `store` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号',
`productid` int(11) DEFAULT NULL COMMENT '商品id',
`productname` varchar(255) DEFAULT NULL COMMENT '商品名称',
`num` int(11) DEFAULT NULL COMMENT '库存数量',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Records of store
-- ----------------------------
INSERT INTO `store` VALUES ('1', '101', '笔记本', '100');
-- ----------------------------
-- Table structure for undo_log
-- ----------------------------
DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) NOT NULL,
`context` varchar(128) NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
`ext` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of undo_log
-- ----------------------------
-- ----------------------------
-- Table structure for dkn_order
-- ----------------------------
DROP TABLE IF EXISTS `dkn_order`;
CREATE TABLE `dkn_order` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号',
`productid` int(11) DEFAULT NULL COMMENT '商品id',
`num` int(11) DEFAULT NULL COMMENT '购买数量',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Records of dkn_order
-- ----------------------------
-- ----------------------------
-- Table structure for undo_log
-- ----------------------------
DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) NOT NULL,
`context` varchar(128) NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
`ext` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8;