ShardingSphere集成Mybatis Plus使用ShardingSphere最新版本5.1.2

在前面的内容中,我们集成使用的ShardingSphere Spring Boot是4.1.1的版本,这个版本在20年就已经停止更新了,而比较新的版本为5.1.2。这个版本也在2022年已经停止更新了,这个版本的配置在官网上的配置和现实的差距比较大,使用官网的配置只能进行简单根据取模等实现简单的分库分表,无法实现自定义的分库分表。而使用自定义的分库分表配置时,使用官网的配置总是报错,根据多次试错,得出了一个比较合理的配置,特此记录。该demo是在之前的系列文章里面更新的,有些代码可参考之前的系列

Maven依赖

 <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <!-- 由于Mybatis Plus使用了druid作为默认的数据源,分库分表使用的是HikariDataSource作为数据源管理,所以需要排除druid的starter -->
            <exclusions>
                <exclusion>
                    <groupId>com.alibaba</groupId>
                    <artifactId>druid-spring-boot-starter</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    <!-- 分库分表,先去掉druid数据源 -->
<!--        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.18</version>
        </dependency>-->
        <!-- web 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.7.16</version>
        </dependency>
        <!-- Mybatis与JacksonJson时间转换异常处理 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-typehandlers-jsr310</artifactId>
            <version>1.0.1</version>
        </dependency>
        <!-- ES依赖 -->
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.elasticsearch</groupId>
                    <artifactId>elasticsearch</artifactId>
                </exclusion>
            </exclusions>
            <version>${elasticsearch.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.elasticsearch/elasticsearch
        -->
        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch</artifactId>
            <version>${elasticsearch.version}</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-extension</artifactId>
            <version>3.5.3.1</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus</artifactId>
            <version>3.5.3.1</version>
        </dependency>
        <!-- mybatis-plus 依赖 -->
        <!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.3.1</version>
        </dependency>

        <!-- mybatis plus 解决sharding+动态数据源启动报错:Property ‘sqlSessionFactory‘ or ‘sqlSessionTemplate‘ are required -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
            <version>3.5.2</version>
        </dependency>
        <!-- mybatis-plus 代码生成器依赖 -->
        <!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-generator -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.5.3.1</version>
        </dependency>
        <!-- freemarker 依赖 -->
        <!-- https://mvnrepository.com/artifact/org.freemarker/freemarker -->
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.31</version>
        </dependency>
        <dependency>
            <groupId>com.example</groupId>
            <artifactId>ft-contract</artifactId>
            <version>0.0.1-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>
        <!-- 旧版本分库分表  -->
      <!--  <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
            <version>4.1.1</version>
        </dependency>-->
        <!-- 5.1.2版本分库分表  -->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
            <version>5.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

YML配置

主application配置:

server:
  port: 8081

spring:
  # sharding-jdbc配置
  shardingsphere:
    datasource:
      names: ftdb0,ftdb1
      ftdb0:
        type: com.zaxxer.hikari.HikariDataSource
        jdbc-url: jdbc:mysql://localhost:3306/ftdb0?useUnicode=true&autoReconnect=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: root
        password: xxx
      ftdb1:
        type: com.zaxxer.hikari.HikariDataSource
        jdbc-url: jdbc:mysql://localhost:3306/ftdb1?useUnicode=true&autoReconnect=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: root
        password: xxx
     rules:
       sharding:
          key-generators: # 配置分布式主键
           snowflake:# 雪花算法主键配置
              type: SNOWFLAKE
              props:
                worker-id: 1034 # 工作Id不能大于1024
                datacenter-id: 895623 
                max-tolerate-time-difference-milliseconds: 2000 # 时钟回拨最大容忍时间(单位:毫秒),默认值为:0
                max-vibrate-offset: 10 # 最大抖动数,默认值为:0  
    props:
      sql-show: true # 是否开启SQL显示,默认值: false
  application:
    name: ft-server
  profiles:
    active: sharding-core
#    active: sharding-jdbc,master-slave
  # dynamic数据源配置
  datasource:
      dynamic:
           primary: master0 # 设置默认的数据源或者数据源组,默认值即为master
           datasource:
                master0:
                     driver-class-name: com.mysql.cj.jdbc.Driver # mysql版本为5.x使用该配置: com.mysql.jdbc.Driver,8.0版本使用:com.mysql.cj.jdbc.Driver
                     url: jdbc:mysql://localhost:3306/ftdb1?useUnicode=true&autoReconnect=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false
                     username: root
                     password: xxx
                master1:
                     driver-class-name: com.mysql.cj.jdbc.Driver # mysql版本为5.x使用该配置: com.mysql.jdbc.Driver,8.0版本使用:com.mysql.cj.jdbc.Driver
                     url: jdbc:mysql://localhost:3306/ftdb0?useUnicode=true&autoReconnect=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false
                     username: root
                     password: xxx

mybatis-plus:
  mapper-locations: classpath:mapper/*.xml
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

分库分表application-sharding-core.yml配置:

spring:
  shardingsphere:
      rules:
        sharding:
            tables:
              #测试自定义标准分片策略
                  c_order:
                    actual-data-nodes:  ftdb$->{0..1}.c_order$->{2023..2024}
                    database-strategy:
                      standard:
                        sharding-column: delivery_time
                        sharding-algorithm-name: test_range_algorithm_db
                    table-strategy:
                      standard:
                        sharding-column: delivery_time
                        sharding-algorithm-name: test_range_algorithm_table
                        key-generate-strategy:
                            column: id
                            key-generator-name: SNOWFLAKE
              #测试自定义复合分片策略
                  b_order:
                    actual-data-nodes: ftdb$->{0..1}.b_order$->{0..2}
                    database-strategy:
                      complex:
                        sharding-columns: company_id,position_id
                        sharding-algorithm-name:  complex_sharding_algorithm_db
                    table-strategy:
                      complex:
                        sharding-columns: company_id,position_id
                        sharding-algorithm-name: complex_sharding_algorithm_table
                        key-generate-strategy:
                          column: id
                          key-generator-name: MYKEY #自定义主键策略
            sharding-algorithms:
              test_range_algorithm_db:
                type: CLASS_BASED_DB
                strategy: STANDARD
                algorithm-class-name: com.example.ftserver.sharding.shardingalgrithm.range.FtDataBaseRangeShardingAlgorithm
              test_range_algorithm_table:
                type: CLASS_BASED_TABLE
                strategy: STANDARD
                algorithm-class-name: com.example.ftserver.sharding.shardingalgrithm.range.FtTableRangeShardingAlgorithm
              complex_sharding_algorithm_db:
                type: CLASS_BASED_COMPLEX_DB
                strategy: COMPLEX
                algorithm-class-name: com.example.ftserver.sharding.shardingalgrithm.complex.FtDataBaseComplexShardingAlgorithm
              complex_sharding_algorithm_table:
                type: CLASS_BASED_COMPLEX_TABLE
                strategy: COMPLEX
                algorithm-class-name: com.example.ftserver.sharding.shardingalgrithm.complex.FtTableComplexShardingAlgorithm



分库的application-sharding-core.yml配置需要特别注意的是shardingsphere的拼法,在IDEA中会提示该词拼写错误,应该使用sharding-sphere。如果按照IDEA的提示改为sharding-sphere后,在项目启动时,读取配置文件的key配置是shardingsphere,会报找不到type的报错。具体信息如图所示:
IDEA中的提示:
提示拼写错误
ShardingAlgorithmProvidedBeanRegistry类中注册Bean的key:
在这里插入图片描述
点击ShardingAlgorithmProvidedBeanRegistry类的registerBean方法跳转到AbstractAlgorithmProvidedBeanRegistry类,最终的createAlgorithmConfiguration()方法中返回的type会为null,在后面的ShardingSphereAlgorithmConfiguration构建中会检查type的类型,如果为空时会抛出异常:
在这里插入图片描述
ShardingSphereAlgorithmConfiguration检查null:
在这里插入图片描述

多数据源配置类

将之前的Mybatis Plus的多数据源的版本更新到3.5.2,之前demo的多数据源需要重新配置。
多数据源配置类:

import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;

import javax.sql.DataSource;

@Configuration
@ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource")
// 使用lombok的构造器注入时,需要使用@RequiredArgsConstructor注解,且变量必须用final或者@NonNull,@NotNull注解修饰
@RequiredArgsConstructor(onConstructor_ = @Autowired) 
public class ShardingDataSourceConfig {
    @NonNull // lombok非空注解
    private  DynamicDataSourceProperties properties;
    @Lazy
    @NotNull // org.jetbrains.annotations非空注解
    private  DataSource shardingDataSource;
    @Primary
    @Bean("shardingDataSource")
    public DataSource dataSource() {
        DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();
        dataSource.setPrimary(properties.getPrimary());
        dataSource.setStrict(properties.getStrict());
        dataSource.setStrategy(properties.getStrategy());
        dataSource.setP6spy(properties.getP6spy());
        dataSource.setSeata(properties.getSeata());
        dataSource.addDataSource("sharding", shardingDataSource);
        return dataSource;
    }
}

Mybatis Plus配置类信息,去掉之前的多数据源配置信息

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.example.ftserver.datapermission.MyDataPermission;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author tencent wb
 * @date 2023-09-27 17:54
 * @description
 */
@Configuration
@MapperScan(basePackages = "com.example.ftserver.mapper")
//  需要配置自动注入设置,不然无法使用,SpringBootConfiguration.class是位于 org.apache.shardingsphere.shardingjdbc.spring.boot.包下的类
//@AutoConfigureBefore({DynamicDataSourceAutoConfiguration.class, SpringBootConfiguration.class})
//@AutoConfigureBefore({DynamicDataSourceAutoConfiguration.class, ShardingSphereAutoConfiguration.class})
public class MybatisPlusConfig {

    /**
     * 自定义SQL注入拦截器
     *
     * @return
     */
    @Bean
    public CustomizedSqlInjector sqlInjector() {
        return new CustomizedSqlInjector();
    }

    /**
     * 添加分页插件
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 添加数据权限
        interceptor.addInnerInterceptor(new MyDataPermission());
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//如果配置多个插件,切记分页最后添加
        return interceptor;
    }



}

自定义分库分表类更新

标准分片分库分表,将之前的分片改为继承StandardShardingAlgorithm类,并重写getType方法,该方法返回的值需要与配置文件中的sharding-algorithms下的xx中的type的值一致
标准分表算法:

package com.example.ftserver.sharding.shardingalgrithm.range;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.Properties;

/**
 * @author zhangxin
 * &#064;description:  表范围分片算法,需要配置精确分片算法,且这两者的参数需要一致
 */
@Component
@Slf4j
public class FtTableRangeShardingAlgorithm extends AbstractRangSharding<Long> implements StandardShardingAlgorithm<Comparable<?>> {
    private Properties props;

    @Override
    public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<Comparable<?>> rangeShardingValue) {
        Collection<String> dataSource = super.rangeTargetDataSource(rangeShardingValue, true);
        return CollectionUtils.isEmpty(dataSource) ? collection : dataSource;
    }

    @Override
    public String doSharding(Collection<String> collection, PreciseShardingValue<Comparable<?>> preciseShardingValue) {
        String dataSource = super.preciseTargetDataSource(preciseShardingValue, true);
        // 为空时使用默认数据表
        return StringUtils.isEmpty(dataSource) ? "c_order2023" : dataSource;
    }

    @Override
    public String getType() {
        return "CLASS_BASED_TABLE";
    }

    @Override
    public Properties getProps() {
        return props;
    }

    @Override
    public void init(Properties properties) {
        this.props = properties;
    }
}

标准分库算法:

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.Properties;

@Component
@Slf4j
public class FtDataBaseRangeShardingAlgorithm extends AbstractRangSharding<Long> implements StandardShardingAlgorithm<Comparable<?>> {
    private Properties props;
    @Override
    public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<Comparable<?>> rangeShardingValue) {
        Collection<String> dataSource = super.rangeTargetDataSource(rangeShardingValue, false);
        return CollectionUtils.isEmpty(dataSource) ? collection : dataSource;
    }

    @Override
    public String doSharding(Collection<String> collection, PreciseShardingValue<Comparable<?>> preciseShardingValue) {
        String dataSource = super.preciseTargetDataSource(preciseShardingValue, false);
        return StringUtils.isEmpty(dataSource) ? "ftdb0" : dataSource;
    }

    @Override
    public Properties getProps() {
        return props;
    }

    @Override
    public void init(Properties properties) {
        this.props = properties;
    }

    @Override
    public String getType() {
        return "CLASS_BASED_DB";
    }
}

在标准分库中,该示例的demo的数据库type配置示例如图所示,图中的test_range_algorithm_table为分表配置:
在这里插入图片描述
在这里插入图片描述
自定义复合分库分表算法时,将之前的改为继承ComplexKeysShardingAlgorithm类,并重写getType方法,该方法返回的值需要与配置文件中的sharding-algorithms下的xx中的type的值一致
自定义复合分库算法:

import com.example.ftserver.sharding.shardingalgrithm.AbstractComplexSharding;
import com.example.ftserver.sharding.sharingvalue.ShardingParam;
import com.example.ftserver.sharding.sharingvalue.ShardingRangeConfig;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;

import org.apache.shardingsphere.sharding.api.sharding.complex.ComplexKeysShardingAlgorithm;
import org.apache.shardingsphere.sharding.api.sharding.complex.ComplexKeysShardingValue;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.List;
import java.util.Properties;

@Component
@Slf4j
public class FtDataBaseComplexShardingAlgorithm extends AbstractComplexSharding<Long> implements ComplexKeysShardingAlgorithm<Comparable<?>> {

    private Properties props;
    /**
     * 根据复杂的键进行分片操作。
     *
     * @param collection 需要进行分片操作的集合。
     * @param complexKeysShardingValue 复杂键分片值,包含逻辑表名和分片参数。
     * @return 分片后的数据集合。如果无法进行分片或分片结果为空,则返回原始集合。
     */
    @Override
    public Collection<String> doSharding(Collection<String> collection, ComplexKeysShardingValue<Comparable< ? >> complexKeysShardingValue) {
        // 获取逻辑表名
        String logicTableName = complexKeysShardingValue.getLogicTableName();
        // 初始化分片参数
        List<ShardingParam> sharingParams = super.complexKeyInitSharingParams(complexKeysShardingValue);
        if (CollectionUtils.isEmpty(sharingParams)) {
            return collection;
        }
        List<ShardingRangeConfig> rangeConfigs = super.shardingRangeConfigs(false,logicTableName);
        // 根据分片参数和范围配置获取目标数据源
        Collection<String> dataSource = super.getTargetDataSource(sharingParams, rangeConfigs);
        return CollectionUtils.isEmpty(dataSource) ? collection : dataSource;
    }

    @Override
    public String getType() {
        return "CLASS_BASED_COMPLEX_DB";
    }

    @Override
    public Properties getProps() {
        return props;
    }

    @Override
    public void init(Properties properties) {
        this.props = properties;
    }
}

自定义复合分片分表算法

package com.example.ftserver.sharding.shardingalgrithm.complex;

import com.example.ftserver.sharding.shardingalgrithm.AbstractComplexSharding;
import com.example.ftserver.sharding.sharingvalue.ShardingParam;
import com.example.ftserver.sharding.sharingvalue.ShardingRangeConfig;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;

import org.apache.shardingsphere.sharding.api.sharding.complex.ComplexKeysShardingAlgorithm;
import org.apache.shardingsphere.sharding.api.sharding.complex.ComplexKeysShardingValue;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.List;
import java.util.Properties;

@Component
@Slf4j
public class FtTableComplexShardingAlgorithm extends AbstractComplexSharding<Long> implements ComplexKeysShardingAlgorithm<Comparable<?>> {
    private Properties props;

    /**
     * 根据复杂的键进行分片操作。
     *
     * @param collection               需要进行分片操作的集合。
     * @param complexKeysShardingValue 复杂键分片值,包含逻辑表名和分片键值信息。
     * @return 分片后的数据集合。如果无法进行分片或分片结果为空,则返回原始集合。
     */
    @Override
    public Collection<String> doSharding(Collection<String> collection, ComplexKeysShardingValue<Comparable<?>> complexKeysShardingValue) {
        List<ShardingParam> sharingParams = super.complexKeyInitSharingParams(complexKeysShardingValue);
        if (CollectionUtils.isEmpty(sharingParams)) {
            return collection;
        }
        String logicTableName = complexKeysShardingValue.getLogicTableName();
        List<ShardingRangeConfig> rangeConfigs = super.shardingRangeConfigs(true, logicTableName);
        // 根据分片参数和范围配置获取目标数据源
        Collection<String> dataSource = super.getTargetDataSource(sharingParams, rangeConfigs);
        return CollectionUtils.isEmpty(dataSource) ? collection : dataSource;
    }

    @Override
    public String getType() {
        return "CLASS_BASED_COMPLEX_TABLE";
    }

    @Override
    public Properties getProps() {
        return props;
    }

    @Override
    public void init(Properties properties) {
        this.props = properties;
    }
}

SPI配置

在算法完成后,需要配置到SPI中,在resources目录下新建META-INF文件夹,再新建services文件夹,然后新建文件的名字为org.apache.shardingsphere.sharding.spi.ShardingAlgorithm【META-INF/services/org.apache.shardingsphere.sharding.spi.ShardingAlgorithm】,文件内容为分库分表算法的全限定类名
如图所示:
在这里插入图片描述

自定义的主键策略

由于更新了版本,所以需要更新自定义的主键策略配置。具体步骤如下:
1.新建一个类,继承KeyGenerateAlgorithm,并重写相关方法:

import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.sharding.spi.KeyGenerateAlgorithm;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicLong;
@Slf4j
public class MyShardingKeyGenerator implements KeyGenerateAlgorithm {
    private final AtomicLong atomicLong = new AtomicLong(0);
    private Properties properties;

    @Override
    public Comparable<?> generateKey() {
        log.info("generateKey 开始生成");
        Long id = atomicLong.incrementAndGet();
        log.info("generateKey 生成结束,生成结果:{}", id);
        // 这里可以根据自己的业务需求生成不同的key
        return id;
    }

    @Override
    public String getType() {
        // 这里需要和配置文件中属性 sharding.tables. key-generate-strategy.key-generator-name值一致
        return "MYKEY";
    }

    @Override
    public Properties getProps() {
        return properties;
    }

    @Override
    public void init(Properties properties) {
        this.properties = properties;
    }
}

2.然后在META-INF/services包下面新建文件的名字为org.apache.shardingsphere.sharding.spi.KeyGenerateAlgorithm的文件【META-INF/services/org.apache.shardingsphere.sharding.spi.KeyGenerateAlgorithm】,文件内容为自定义主键生成策略的全限定类名
如图所示:
在这里插入图片描述

结语

**注意点:在表的Mapper类,或者service类上面,需要配置@DS()注解,该注解的值需要与DynamicRoutingDataSource 中的addDataSource方法的名称一致。**该demo中配置示例如图:
在这里插入图片描述
在这里插入图片描述
其实其配置很简单,但是缺乏相关的文献,网上资料也是混乱不堪。需要多看看源码,看哪里有错误,修正配置信息。
附录:
一个自定义的雪花算法工具类,用于计算雪花Id生成的时间:

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.Singleton;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.sharding.algorithm.keygen.SnowflakeKeyGenerateAlgorithm;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * SnowflakeUtil 类实现了 InitializingBean 接口,
 * 提供了一个基于 Snowflake 算法的 ID 生成器。
 */
@Component
@PropertySource("classpath:application.yml")
@Slf4j
public class SnowflakeUtil implements InitializingBean {
    @Value("${spring.shardingsphere.rules.sharding.key-generators.snowflake.props.worker-id}")
    private long workerId;

    @Value("${spring.shardingsphere.rules.sharding.key-generators.snowflake.props.datacenter-id}")
    private long datacenterId;
    @Value("${spring.shardingsphere.rules.sharding.key-generators.snowflake.props.max-tolerate-time-difference-milliseconds}")
    private long milliseconds;

    private static final Map<String, Object> CONFIG_MAP = new ConcurrentHashMap<>(2);

    // ShardingSphere雪花算法 初始化时间
private static final String TIME="20161101";
private static final String FORMAT="yyyyMMdd";
    private static SnowflakeKeyGenerateAlgorithm getInstance() {
        return Singleton.get(SnowflakeKeyGenerateAlgorithm.class);
    }
    /**
     * 生成一个密钥。
     * 该方法是静态方法,直接通过类名调用,无需实例化对象。
     * 它通过调用某个实例方法来生成并返回一个密钥。
     *
     * @return 返回生成的密钥,类型为long。
     */
    public static long generateKey() {
        return getInstance().generateKey();
    }

    /**
     * 使用HuTool工具库生成一个唯一的长整型键值。
     *
     * @return 生成的唯一键值,为Snowflake算法生成的长整型ID。
     */
    public static long generateKeyByHuTool() {
        return getSnowflake().nextId();
    }
    private static Snowflake getSnowflake() {
        return Singleton.get(Snowflake.class);
    }
    public static String getDateByHuto(long id) {
        long time =getSnowflake().getGenerateDateTime(id);
        DateTime date = DateUtil.date(time);
        return DateUtil.format(date, FORMAT);
    }

    /**
     * 根据给定的ID获取对应格式的日期字符串。
     * 方法首先会从CONFIG_MAP中尝试获取“time”键对应的时间戳,如果未找到,则初始化时间戳,
     * 并将其存储到CONFIG_MAP中。然后,根据给定的ID计算日期,并将其格式化为字符串返回。
     *
     * @param id 一个长整型数字,用于计算日期。
     * @return 返回一个格式化的日期字符串。
     */
    public static String getDate(long id) {
        Long epochMilli = (Long) CONFIG_MAP.get("time");
        if (Objects.isNull(epochMilli)) {
            // 初始化时间
            LocalDate localDate = LocalDateTimeUtil.parseDate(TIME, FORMAT);
            epochMilli = LocalDateTimeUtil.toEpochMilli(localDate);
            CONFIG_MAP.put("time",epochMilli);
        }
        long time = (id >> 22L) +epochMilli;
        DateTime dateTime = DateUtil.date(time);
        return DateUtil.format(dateTime, FORMAT);
    }

    // 实现InitializingBean接口的afterPropertiesSet方法,用于在bean初始化后执行逻辑
    @Override
    public void afterPropertiesSet() {
        try {
            SnowflakeKeyGenerateAlgorithm algorithm = new SnowflakeKeyGenerateAlgorithm();
            Singleton.put(algorithm);
            LocalDate localDate = LocalDateTimeUtil.parseDate(TIME, FORMAT);
            long epochMilli = LocalDateTimeUtil.toEpochMilli(localDate);
            CONFIG_MAP.putIfAbsent("time", epochMilli);
            log.info("加载完成,map的值:{}", JSONUtil.toJsonStr(CONFIG_MAP));
            Date dateTime = DateUtil.parse("20230101", FORMAT);
            // hutool雪花算法 工作机器ID(workerId),不能大于20
            // hutool雪花算法 数据中心ID(datacenterId),不能大于31
            Snowflake snowflake = new Snowflake(dateTime, workerId, datacenterId, true, milliseconds);
            Singleton.put(snowflake);
        } catch (Exception e) {
            log.error("加载过程中发生异常:", e);
        }
    }
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值