目录
环境准备
我是在windows上安装的
下载如下网址中对应版本的binary即可
https://seata.io/zh-cn/blog/download.html
此处需要注意:需要手动的将mysql的连接包传入lib文件夹中
我这里用的是mysql8 所以传入了mysql-connector-java-8.0.22.jar
sql准备
1.新建seata库 加入如下表
-- -------------------------------- The script used when storeMode is 'db' --------------------------------
-- the table to store GlobalSession data
CREATE TABLE IF NOT EXISTS `global_table`
(
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT,
`status` TINYINT NOT NULL,
`application_id` VARCHAR(32),
`transaction_service_group` VARCHAR(32),
`transaction_name` VARCHAR(128),
`timeout` INT,
`begin_time` BIGINT,
`application_data` VARCHAR(2000),
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`xid`),
KEY `idx_gmt_modified_status` (`gmt_modified`, `status`),
KEY `idx_transaction_id` (`transaction_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
-- the table to store BranchSession data
CREATE TABLE IF NOT EXISTS `branch_table`
(
`branch_id` BIGINT NOT NULL,
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT,
`resource_group_id` VARCHAR(32),
`resource_id` VARCHAR(256),
`branch_type` VARCHAR(8),
`status` TINYINT,
`client_id` VARCHAR(64),
`application_data` VARCHAR(2000),
`gmt_create` DATETIME(6),
`gmt_modified` DATETIME(6),
PRIMARY KEY (`branch_id`),
KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
-- the table to store lock data
CREATE TABLE IF NOT EXISTS `lock_table`
(
`row_key` VARCHAR(128) NOT NULL,
`xid` VARCHAR(96),
`transaction_id` BIGINT,
`branch_id` BIGINT NOT NULL,
`resource_id` VARCHAR(256),
`table_name` VARCHAR(32),
`pk` VARCHAR(36),
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`row_key`),
KEY `idx_branch_id` (`branch_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
2.在需要全局事务的库中加入undo_log
-- 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`
(
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 'increment id',
`branch_id` BIGINT(20) NOT NULL COMMENT 'branch transaction id',
`xid` VARCHAR(100) 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 NOT NULL COMMENT 'create datetime',
`log_modified` DATETIME NOT NULL COMMENT 'modify datetime',
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';
配置文件修改
要修改两个配置文件
1.file.conf
mode改为db 数据库连接串修改
目的就是连接到上文创建的那个库
2.registry.conf
这个是将seata服务注册到注册中心用的
我这里用的ureka 就将type修改成eureka
然后修改相应的eureka的地址我是本地运行的
这样 环境准备就可以了 打开自己的注册中心并运行bin文件夹中的seata-server.sh 看看seata是否正常启动
代码准备
pom引入
我是用的1.2
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
spring启动的Application也要修改
加入
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class
})
@EnableAutoDataSourceProxy
引入三个文件
@Configuration
public class DataSourceConfiguration {
@Value("${spring.cloud.alibaba.seata.tx-service-group}")
private String group;
@Value("${spring.application.name}")
private String appName;
@Bean
public FescarXidFilter fescarXidFilter(){
return new FescarXidFilter();
}
@Bean
public GlobalTransactionScanner globalTransactionScanner(){
GlobalTransactionScanner scanner = new GlobalTransactionScanner(appName,group);
return scanner;
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
return druidDataSource;
}
@Primary
@Bean("dataSource")
public DataSourceProxy dataSource(DataSource druidDataSource){
return new DataSourceProxy(druidDataSource);
}
// @Bean
// public SqlSessionFactory sqlSessionFactory(DataSourceProxy dataSourceProxy)throws Exception{
// SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
// sqlSessionFactoryBean.setDataSource(dataSourceProxy);
// sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
// .getResources("classpath*:/mapper/*.xml"));
// sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
// return sqlSessionFactoryBean.getObject();
// }
}
@Slf4j
public class FescarXidFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String xid = RootContext.getXID();
String restXid = request.getHeader("Fescar-Xid");
boolean bind = false;
if(StringUtils.isBlank(xid)&&StringUtils.isNotBlank(restXid)){
RootContext.bind(restXid);
bind = true;
if (logger.isDebugEnabled()) {
logger.debug("bind[" + restXid + "] to RootContext");
}
}
try{
filterChain.doFilter(request, response);
} finally {
if (bind) {
String unbindXid = RootContext.unbind();
if (logger.isDebugEnabled()) {
logger.debug("unbind[" + unbindXid + "] from RootContext");
}
if (!restXid.equalsIgnoreCase(unbindXid)) {
logger.warn("xid in change during http rest from " + restXid + " to " + unbindXid);
if (unbindXid != null) {
RootContext.bind(unbindXid);
logger.warn("bind [" + unbindXid + "] back to RootContext");
}
}
}
}
}
}
@Component
public class RequestHeaderInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
String xid = RootContext.getXID();
if(StringUtils.isNotBlank(xid)){
template.header("Fescar-Xid",xid);
}
}
}
配置文件加入如下
最后 在事务管理的主入口加入如下注解即可
@GlobalTransactional(name="register_tx",rollbackFor = Exception.class)