• 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-8project.build.sourceEncoding>
        <maven.compiler.source>1.8maven.compiler.source>
        <maven.compiler.target>1.8maven.compiler.target>
        <junit.version>4.12junit.version>
        <log4j.version>1.2.17log4j.version>
        <lombok.version>1.16.18lombok.version>
        <mysql.version>5.1.47mysql.version>
        <druid.version>1.1.16druid.version>
        <mybatis.spring.boot.version>1.3.0mybatis.spring.boot.version>
        <spring-nacos.version>2.2.1.RELEASEspring-nacos.version>
        <mybatis-plus.version>3.0.3mybatis-plus.version>
        <mybatis-starter.version>1.0.5mybatis-starter.version>
        <sharding.jdbc.starter.version>4.1.1sharding.jdbc.starter.version>
        <sharding.jdbc.at.version>4.1.1sharding.jdbc.at.version>
        <fastjson.version>1.2.69fastjson.version>
        <spring-cloud.version>Hoxton.RELEASEspring-cloud.version>
        <seata.starter.version>1.4.2seata.starter.version>
    properties>
    <dependencyManagement>
    	<dependencies>
    		<dependency>
    		    <groupId>org.springframework.bootgroupId>
    		    <artifactId>spring-boot-dependenciesartifactId>
    		    <version>2.2.2.RELEASEversion>
    		    <type>pomtype>
    		    <scope>importscope>
    		dependency>
    		<dependency>
    		    <groupId>org.springframework.cloudgroupId>
    		    <artifactId>spring-cloud-dependenciesartifactId>
    		    <version>Hoxton.RELEASEversion>
    		    <type>pomtype>
    		    <scope>importscope>
    		dependency>
    		<dependency>
    		    <groupId>com.alibaba.cloudgroupId>
    		    <artifactId>spring-cloud-alibaba-dependenciesartifactId>
    		    <version>2.2.0.RELEASEversion>
    		    <type>pomtype>
    		    <scope>importscope>
    		dependency>
    		<dependency>
    		    <groupId>mysqlgroupId>
    		    <artifactId>mysql-connector-javaartifactId>
    		    <version>${mysql.version}version>
    		dependency>
    		<dependency>
    		    <groupId>com.alibaba.cloudgroupId>
    		    <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
    		    <version>${spring-nacos.version}version>
    		dependency>
    		<dependency>
    		    <groupId>com.alibabagroupId>
    		    <artifactId>druidartifactId>
    		    <version>${druid.version}version>
    		dependency>
    		<dependency>
    		    <groupId>org.mybatis.spring.bootgroupId>
    		    <artifactId>mybatis-spring-boot-starterartifactId>
    		    <version>${mybatis.spring.boot.version}version>
    		dependency>
    		<dependency>
    		    <groupId>com.baomidougroupId>
    		    <artifactId>mybatisplus-spring-boot-starterartifactId>
    		    <version>${mybatis-starter.version}version>
    		dependency>
    		<dependency>
    		    <groupId>com.baomidougroupId>
    		    <artifactId>mybatis-plus-boot-starterartifactId>
    		    <version>${mybatis-plus.version}version>
    		dependency>
    		
    		<dependency>
    		    <groupId>org.apache.shardingspheregroupId>
    		    <artifactId>sharding-jdbc-spring-boot-starterartifactId>
    		    <version>${sharding.jdbc.starter.version}version>
    		dependency>
    		
    		<dependency>
    		    <groupId>org.apache.shardingspheregroupId>
    		    <artifactId>sharding-transaction-base-seata-atartifactId>
    		    <version>${sharding.jdbc.at.version}version>
    		dependency>
    		<dependency>
    		    <groupId>io.seatagroupId>
    		    <artifactId>seata-spring-boot-starterartifactId>
    		    <version>${seata.starter.version}version>
    		dependency>
    	dependencies>
    dependencyManagement>
    
    • 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
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91

    pom

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-openfeignartifactId>
        dependency>
        <dependency>
            <groupId>com.alibaba.cloudgroupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <scope>providedscope>
        dependency>
        <dependency>
            <groupId>com.alibaba.cloudgroupId>
            <artifactId>spring-cloud-alibaba-seataartifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.seatagroupId>
                    <artifactId>seata-spring-boot-starterartifactId>
                exclusion>
            exclusions>
        dependency>
        <dependency>
            <groupId>io.seatagroupId>
            <artifactId>seata-spring-boot-starterartifactId>
        dependency>
        <dependency>
            <groupId>org.apache.shardingspheregroupId>
            <artifactId>sharding-transaction-base-seata-atartifactId>
        dependency>
    dependencies>
    
    • 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
    • 36
    • 37
    • 38
    • 39

    2. seata-server

    seata官方文档:https://seata.io/zh-cn/docs/overview/what-is-seata.html,可以去看看

    2.1 下载seate-server

    https://github.com/seata/seata/releases
    总是打不开…我下载的版本是seata-server.1.4.2

    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 = "xxxx"
        ## ...省略
      }
    
      ## redis store property
      redis {
        ## ...省略
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    然后再修改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;
    
    • 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
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    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执行
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    如果你采用了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
    
    • 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
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    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;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    下面内容请仔细观看

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

    5. 测试结果

    执行后可以看到如下日志
    在这里插入图片描述
    然后可以去检查数据库,也可以去查看undolog表的自增值和修改时间,确定是否生效
    在这里插入图片描述

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

  • 相关阅读:
    CentOS7 建立静态地址网桥br0
    动作捕捉用于仿生机器人的运动规划
    【项目方案】利用Zookeeper实现集群缓存一致
    【Leetcode】211. 添加与搜索单词 - 数据结构设计
    PC_寻址方式
    基于R语言的贝叶斯网络模型、现代贝叶斯统计学方法
    Chrome速度惊人,不到30天修复40个漏洞
    香港金融科技周VERTU CSO Sophie谈Web3.0的下一个风口 手机虚拟货币移动支付
    shell循环
    海外动态IP代理可以用来批量注册邮箱吗?
  • 原文地址:https://blog.csdn.net/qq_41563912/article/details/126305589