尽管世界和人生是坏透了,其中却有一件东西永远是好,那便是青春——显克维奇

首先是下载seata1.4.1

然后解压

先修改conf下的registry.conf

type改为nacos

image-20210314184336107

进入bin

打开控制台运行seata-server.bat

然后在项目中引入依赖,记得需要分布式式事务的服务都要配置

1
2
3
4
5
<!--    seata 分布式事务    -->
<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;

/**
* @ClassName: SeataConfig
* @Description: 我还没有写描述
* @Date: 2021/3/14 0014 16:53
* *
* @author: <achao1441470436@gmail.com>
* @version: 1.0
* @since: JDK 1.8
*/
@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.confresources目录下

内容为

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();
}

MpOrderMappermybatis-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;

/**
* @ClassName: SeataConfig
* @Description: 我还没有写描述
* @Date: 2021/3/14 0014 16:53
* *
* @author: <achao1441470436@gmail.com>
* @version: 1.0
* @since: JDK 1.8
*/
@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;

/**
* @ClassName: WareController
* @Description: 我还没有写描述
* @Date: 2021/3/13 0013 22:10
* *
* @author: <achao1441470436@gmail.com>
* @version: 1.0
* @since: JDK 1.8
*/
@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;

/**
* @ClassName: WareServiceImpl
* @Description: 我还没有写描述
* @Date: 2021/3/13 0013 22:14
* *
* @author: <achao1441470436@gmail.com>
* @version: 1.0
* @since: JDK 1.8
*/
@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("成功");
}
}

我们开始测试

往数据库放条数据

image-20210314185153412

首次调用接口时可以看到订单生成并且库存成功扣减

image-20210314185243786

image-20210314185247897

第二次调用发现抛出异常并成功回滚库存服务

至此,成功使用了seata实现了分布式事务