1. 依赖
1.1基础环境版本如下:
SpringCloud Hoxton.RELEASE
SpringCloud alibaba 2.2.0.RELEASE
SpringBoot 2.2.2.RELEASE
1.2 pom
文件:
父pom
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<junit.version>4.12</junit.version>
<log4j.version>1.2.17</log4j.version>
<lombok.version>1.16.18</lombok.version>
<mysql.version>5.1.47</mysql.version>
<druid.version>1.1.16</druid.version>
<mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
<spring-nacos.version>2.2.1.RELEASE</spring-nacos.version>
<mybatis-plus.version>3.0.3</mybatis-plus.version>
<mybatis-starter.version>1.0.5</mybatis-starter.version>
<sharding.jdbc.starter.version>4.1.1</sharding.jdbc.starter.version>
<sharding.jdbc.at.version>4.1.1</sharding.jdbc.at.version>
<fastjson.version>1.2.69</fastjson.version>
<spring-cloud.version>Hoxton.RELEASE</spring-cloud.version>
<seata.starter.version>1.4.2</seata.starter.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>${spring-nacos.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.spring.boot.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatisplus-spring-boot-starter</artifactId>
<version>${mybatis-starter.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- sharding-jdbc 分库分表 -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>${sharding.jdbc.starter.version}</version>
</dependency>
<!-- 使用BASE(AT)事务时,需要引入此模块 seata -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-transaction-base-seata-at</artifactId>
<version>${sharding.jdbc.at.version}</version>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>${seata.starter.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
子pom
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--springboot启动包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-seata</artifactId>
<exclusions>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-transaction-base-seata-at</artifactId>
</dependency>
</dependencies>
2. seata-server
seata
官方文档:点我跳转,可以去看看
2.1 下载seate-server
https://github.com/seata/seata/releases
总是打不开…我下载的版本是seata-server.1.4.2
,如果打不开,可以去我这下载https://download.csdn.net/download/qq_41563912/86401892
2.2 修改配置
下载完成后解压,conf
目录下修改一下配置文件file.conf
,mode
改为db
,并且将对应的数据库连接以及用户密码修改
store {
mode = "db"
publicKey = ""
file {
## ...省略
}
## database store property
db {
datasource = "druid"
dbType = "mysql"
driverClassName = "com.mysql.jdbc.Driver"
## 改成你自己的
url = "jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true"
user = "root"
password = "****"
## ...省略
}
## redis store property
redis {
## ...省略
}
}
然后再修改conf
目录下的register.conf
配置文件
我用的注册中心是nacos
,你改成你相应的即可
然后启动seata-server
,可以到nacos
控制台看下是否注册成功
2.3 数据库
如果你没有seata
数据库,则需要创建,并且创建相应的表,在git
里是有脚本的,但是我打不开,索性先把我本地的贴到这里把,能打开的可以参考这里:https://seata.io/zh-cn/docs/ops/deploy-ha.html
CREATE TABLE `branch_table` (
`branch_id` bigint(20) NOT NULL,
`xid` varchar(128) NOT NULL,
`transaction_id` bigint(20) DEFAULT NULL,
`resource_group_id` varchar(32) DEFAULT NULL,
`resource_id` varchar(256) DEFAULT NULL,
`branch_type` varchar(8) DEFAULT NULL,
`status` tinyint(4) DEFAULT NULL,
`client_id` varchar(64) DEFAULT NULL,
`application_data` varchar(2000) DEFAULT NULL,
`gmt_create` datetime(6) DEFAULT NULL,
`gmt_modified` datetime(6) DEFAULT NULL,
PRIMARY KEY (`branch_id`),
KEY `idx_xid` (`xid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `distributed_lock` (
`lock_key` char(20) NOT NULL,
`lock_value` varchar(20) NOT NULL,
`expire` bigint(20) DEFAULT NULL,
PRIMARY KEY (`lock_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `global_table` (
`xid` varchar(128) NOT NULL,
`transaction_id` bigint(20) DEFAULT NULL,
`status` tinyint(4) NOT NULL,
`application_id` varchar(32) DEFAULT NULL,
`transaction_service_group` varchar(32) DEFAULT NULL,
`transaction_name` varchar(128) DEFAULT NULL,
`timeout` int(11) DEFAULT NULL,
`begin_time` bigint(20) DEFAULT NULL,
`application_data` varchar(2000) DEFAULT NULL,
`gmt_create` datetime DEFAULT NULL,
`gmt_modified` datetime DEFAULT NULL,
PRIMARY KEY (`xid`),
KEY `idx_gmt_modified_status` (`gmt_modified`,`status`),
KEY `idx_transaction_id` (`transaction_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `lock_table` (
`row_key` varchar(128) NOT NULL,
`xid` varchar(128) DEFAULT NULL,
`transaction_id` bigint(20) DEFAULT NULL,
`branch_id` bigint(20) NOT NULL,
`resource_id` varchar(256) DEFAULT NULL,
`table_name` varchar(32) DEFAULT NULL,
`pk` varchar(36) DEFAULT NULL,
`gmt_create` datetime DEFAULT NULL,
`gmt_modified` datetime DEFAULT NULL,
PRIMARY KEY (`row_key`),
KEY `idx_branch_id` (`branch_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3. 项目配置
3.1 在用到seata
的服务的resources
文件夹下添加eata.conf
配置文件
在项目的resources
文件夹下添加seata.conf
配置文件,当然也有其他方式,我用yml+seata.conf
的方式,内容如下
client {
application.id = shop-account-service
transaction.service.group = shop-account-service-group
}
## 两种方式执行
## 1.yml配置文件+seata.conf执行
## 2.registry.conf+seata.conf执行
3.1 yml seata
配置
seata:
client:
undo:
log-serialization: fastjson
#config:
#type: nacos
#nacos:
#group: SEATA_GROUP
#server-addr: 127.0.0.1:8848
registry:
type: nacos
nacos:
application: seata-server
server-addr: 127.0.0.1:8848
group: SEATA_GROUP
namespace: public
enabled: true # 开启自动装配
tx-service-group: shop-account-service-group # 事务组, 直接设置为项目名即可
enable-auto-data-source-proxy: true
service: # 如果配置中心用nacos, 可以将该配置写到nacos config中
vgroup-mapping:
shop-account-service-group: default
如果你采用了nacos
作为配置中心,则可以将配置放到nacos
里,如何操作这里就不讲了
3.2 sharding-jdbc配置
spring:
application:
name: shop-account-service
main:
allow-bean-definition-overriding: true
shardingsphere:
datasource:
names: ds0, ds1, ds2, ds3, ds-common
ds0:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/overseas_shop_account_0?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: root
ds1:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/overseas_shop_account_1?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: root
ds2:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/overseas_shop_account_2?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: root
ds3:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/overseas_shop_account_3?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: root
ds-common:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/overseas_shop_common?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: root
sharding:
# 默认分库策略
default-database-strategy:
inline:
algorithm-expression: ds$->{user_id % 4}
sharding-column: user_id
# 分表策略
tables:
t_user:
actual-data-nodes: ds$->{0..3}.t_user
table-strategy:
inline:
sharding-column: user_id
algorithm-expression: t_user
t_login_name_id_map:
actual-data-nodes: ds-common.t_login_name_id_map
table-strategy:
inline:
sharding-column: login_name
algorithm-expression: t_login_name_id_map
database-strategy:
inline:
sharding-column: login_name
algorithm-expression: ds-common
t_user_id_name_map:
actual-data-nodes: ds-common.t_user_id_name_map
table-strategy:
inline:
sharding-column: user_id
algorithm-expression: t_user_id_name_map
database-strategy:
inline:
sharding-column: user_id
algorithm-expression: ds-common
3.3 在用到seata
的服务对应的数据库创建undolog
表
需要注意的是在每个库都要创建,分库了,就需要在各个分库创建
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,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
4. 具体代码
@ShardingTransactionType(TransactionType.BASE)
@Transactional
public UserPO buildAndSaveUseAndMappings(long userId, AccountReqDTO reqDTO) {
log.info("【 构建用户即userId与username映射, 落库... 】");
UserPO userPO = new UserPO();
userPO.setUserId(userId);
this.save(userPO); // account库
// seata测试
System.out.println(1 / 0);
LoginNameIdMapPO loginNameIdMapDO = new LoginNameIdMapPO();
String loginName = reqDTO.getLoginName();
loginNameIdMapDO.setLoginName(loginName);
loginNameIdMapDO.setUserId(userId);
loginNameIdMapService.save(loginNameIdMapDO); // common库
UserIdNameMapPO userIdNameMapPO = new UserIdNameMapPO();
userIdNameMapPO.setLoginName(loginName);
userIdNameMapPO.setUserId(userId);
userIdNameMapService.save(userIdNameMapPO); // common库
return userPO;
}
下面内容请仔细观看
PS:可以看上面代码一个方法操作了多个数据库,其中account
库分了四个库,另外还涉及到了一个common
库,所以可以确定是分布式事务,或者微服务之间的调用产生的分布式事务,需要注意的是,单用seata
时,我们只需要在存在分布式事务的地方加@GlobalTransactional
注解就可以了,但是引入shardingjdbc
后,有两点需要注意:
@ShardingTransactionType
和@Transactional
注解,必须同时使用- 需要显式设置分布式事务模式,也就是你在注解里写
TransactionType.BASE
是不生效的,需要在方法执行前,调用TransactionTypeHolder.set(TransactionType.BASE);
你可以在方法最开始加上这行代码,也可以写aop
,都行
我是用的aop
的方式,可以参考下:
@Component
@Aspect
@Order(-1) // 如果一个方法被多个aop增强, 可以用@Order来控制增强顺序
public class ShardingTransactionAspect {
private final static Logger LOGGER = LoggerFactory.getLogger(ShardingTransactionAspect.class);
// 切点, 执行增强代码的地方
@Pointcut("@annotation(org.apache.shardingsphere.transaction.annotation.ShardingTransactionType)")
public void pointCut() {
}
//@Before 标识一个前置增强方法, 方法内部的增强代码叫做Advice切面或通知
@Before("pointCut()")
public void doBefore(JoinPoint point) {
ShardingTransactionType ShardingTransaction = point.getTarget().getClass().getAnnotation(ShardingTransactionType.class);
if (ShardingTransaction == null) {
//注解在类上
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
ShardingTransaction = method.getAnnotation(ShardingTransactionType.class);
}
TransactionTypeHolder.set(ShardingTransaction.value());
LOGGER.info("选择Sharding分布式事务---" + ShardingTransaction.value().toString());
}
// @After 标识一个后置增强方法, 方法内部的增强代码叫做Advice切面或通知
@After("pointCut()")
public void doAfter() {
TransactionTypeHolder.clear();
}
}
5. 测试结果
执行后可以看到如下日志
然后可以去检查数据库,也可以去查看undolog
表的自增值和修改时间,确定是否生效
能力有限,不足之处希望有大佬指正~