目录
Seata AT事务
TC事务协调器
业务模块中添加Seata AT事务
Seata TCC事务
TCC - 两个阶段的三种操作
业务模块中添加Seata TCC事务
Seata AT事务
TC事务协调器
1.从seata官网下载事务协调器服务
2.配置:
- ?? - seata/conf/registry.conf -- 连接 eureka,向注册表注册
- ?? - seata/conf/file.conf -- 协调器运行期间记录的日志数据的存储位置
- ?? - seata/bin/seata-server.bat -- 设置 JVM 使用的内存大小
3.在bin目录下cmd打开dos窗口,运行seata-server.bat
- 必须用JDK1.8
- 命令窗口不能关闭
- 命令窗口不能选中,否则应用会被挂起,暂停执行
业务模块中添加Seata AT事务
1.添加seata依赖
<!--seata 依赖有自动配置
如果不添加其他 Seata配置文件,项目无法启动
-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-seata</artifactId>
<version>2.0.0.RELEASE</version>
<exclusions>
<exclusion>
<artifactId>seata-all</artifactId>
<groupId>io.seata</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>1.3.0</version>
</dependency>
2.三个配置:
- ?? - application.yml -- 事务组的组名
spring:
cloud:
alibaba:
seata:
tx-service-group: order_tx_group # 事务组组名
- ?? - registry.conf -- 设置注册中心的地址
- ?? - file.conf -- 事务组对用使用哪个协调器
service {
#transaction service group mapping
# order_tx_group 与 yml 中的 “tx-service-group: order_tx_group” 配置一致
# “seata-server” 与 TC 服务器的注册名一致
# 从eureka获取seata-server的地址,再向seata-server注册自己,设置group
vgroupMapping.order_tx_group = "seata-server"
#only support when registry.type=file, please don't set multiple addresses
order_tx_group.grouplist = "127.0.0.1:8091"
#degrade, current not support
enableDegrade = false
#disable seata
disableGlobalTransaction = false
}
3.自定义自动配置类,创建数据源代理对象
- AT事务的自动事务控制代码,都是由数据源代理对象提供的
package cn.tedu.order;
import com.zaxxer.hikari.HikariDataSource;
import io.seata.rm.datasource.DataSourceProxy;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
/*
自定义自动配置类,创建原始数据源和数据源代理对象
*/
@Configuration
public class DSConf {
// 原始数据源
@ConfigurationProperties(prefix = "spring.datasource") //将配置文件中的数据库参数注入HikariDataSource对象
@Bean
public DataSource DataSource(){
return new HikariDataSource();
}
// Seata AT事务的数据源代理对象
@Primary //首选对象
@Bean
public DataSource dataSourceProxy(DataSource ds){
return new DataSourceProxy(ds);
}
}
4.排除springboot默认的数据源自动配置
在启动类中添加以下注解:
//排除默认的数据源配置,应用自定义的自动配置类
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
5.业务方法上添加事务注解
?? - `@Transactional` - 控制本地事务 ?? - `@GlobalTransactional` - 创建 TM,启动全局事务,只在第一个模块添加
Seata TCC事务
TCC 是一种有侵入的事务方案
- 所有事务控制代码都需要自己实现 - 底层的数据库、业务代码都需要修改
有些复杂情况下,AT事务无法实现自动事务控制,就需要手动控制事务的方案
TCC - 两个阶段的三种操作
- 第一个阶段
? Try - 预留资源、冻结数据
- 第二阶段
? - Confirm - 确认、提交,使用一阶段冻结的数据实现业务数据处理 ? - Cancel - 取消、回滚,把一阶段冻结的数据恢复回去
业务模块中添加Seata TCC事务
1.添加seata依赖
2.三个配置文件
- ?? - application.yml -- 组名
- ?? - registry.conf -- eureka地址
- ?? - file.conf -- 事务组对应使用的协调器
3.修改Mapper,添加新的TCC三个数据库操作
4.按照Seata TCC的规则,添加TccAction接口和实现类,实现三个操作的方法
接口
package cn.tedu.order.tcc;
import io.seata.rm.tcc.api.BusinessActionContext;
import io.seata.rm.tcc.api.BusinessActionContextParameter;
import io.seata.rm.tcc.api.LocalTCC;
import io.seata.rm.tcc.api.TwoPhaseBusinessAction;
import java.math.BigDecimal;
/*
在接口中要定义 TCC 三个方法
*/
@LocalTCC
public interface OrderTccAction {
/*
BusinessActionContext上下文对象,用来从第一阶段向第二阶段传递数据
第一阶段需要业务数据参数用来冻结业务数据,
为了避开seata的一个bug,最好不传递封装的对象,而是把数据打散一个个的单独传递
第一阶段方法需要在业务代码中手动调用
*/
@TwoPhaseBusinessAction(name = "OrderTccAction", //指定第一阶段方法
commitMethod = "commit", //指定Confirm方法
rollbackMethod = "rollback")//指定Cancel方法
boolean prepare(BusinessActionContext ctx,
@BusinessActionContextParameter(paramName = "orderId") Long id,
Long userId, Long productId, Integer count, BigDecimal money); //T - Try
/*
第二阶段方法由RM(资源管理器)接收TC的二阶段指令,执行二阶段方法
*/
boolean commit(BusinessActionContext ctx); //C - Confirm
boolean rollback(BusinessActionContext ctx); //C - Cancel
}
实现类
package cn.tedu.order.tcc;
import cn.tedu.order.entity.Order;
import cn.tedu.order.mapper.OrderMapper;
import io.seata.rm.tcc.api.BusinessActionContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
@Component
public class OrderTccActionImpl implements OrderTccAction {
@Autowired
private OrderMapper orderMapper;
@Override
@Transactional
public boolean prepare(BusinessActionContext ctx, Long id, Long userId, Long productId, Integer count, BigDecimal money) {
orderMapper.createFrozen(new Order(id,userId,productId,count,money,0));
return true; //第一阶段冻结成功
}
@Override
@Transactional
public boolean commit(BusinessActionContext ctx) {
//第一阶段向第二阶段传递上下文对象时,先转成json进行传递,会丢失数据的类型信息
Long orderId = Long.valueOf(ctx.getActionContext("orderId").toString());
orderMapper.updateStatus(orderId, 1);//订单状态改成1,正常状态
return true; //确认提交成功
}
@Override
@Transactional
public boolean rollback(BusinessActionContext ctx) {
Long orderId = Long.valueOf(ctx.getActionContext("orderId").toString());
orderMapper.deleteById(orderId);
return true;//取消回滚成功
}
}
5.调整业务方法,不直接完成业务,而是调用第一阶段方法冻结数据
tcc.prepare(null,order.getId(),order.getUserId(),
order.getProductId(),order.getCount(),order.getMoney());
6.在第一个模块的业务方法上添加`@GlobalTransactional`
|