SpringBoot集成Shardingjdbc+seata AT模式

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后,有两点需要注意:

  1. @ShardingTransactionType@Transactional注解,必须同时使用
  2. 需要显式设置分布式事务模式,也就是你在注解里写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表的自增值和修改时间,确定是否生效
在这里插入图片描述

能力有限,不足之处希望有大佬指正~

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值