当前位置: 首页 > article >正文

Sharding-JDBC 5.4.1+SpringBoot3.4.1+MySQL8.4.1 使用案例

最近在升级 SpringBoot 项目,原版本是 2.7.16,要升级到 3.4.0 ,JDK 版本要从 JDK8 升级 JDK21,原项目中使用了 Sharding-JDBC,版本 4.0.0-RC1,在升级 SpringBoot 版本到 3.4.0 之后,服务启动失败,因此选择升级 Sharding-JDBC,记录代码如下:

环境

SpringBoot 3.4.1
Sharding-JDBC 5.4.1
MySQL 8.4.1

代码实现

下面通过代码实现,根据年份分表,2020 年之前数据一张表,之后每 2 年一张表。

准备测试数据 SQL

create table t_order
(
    id          int auto_increment
        primary key,
    order_id    varchar(36)                        null comment '订单ID',
    amount      decimal(18, 2)                     null comment '金额',
    order_year  int                                null comment '订单年份,用来作为分表字段',
    create_time datetime default CURRENT_TIMESTAMP null,
    update_time datetime default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP,
    is_del      bit                                null
)
    comment '逻辑表,该表没有数据,但是没有这张表,sharding-jdbc执行时会报错';

create table t_order_0
(
    id          int auto_increment
        primary key,
    order_id    varchar(36)                        null comment '订单ID',
    amount      decimal(18, 2)                     null comment '金额',
    order_year  int                                null comment '订单年份,用来作为分表字段',
    create_time datetime default CURRENT_TIMESTAMP null,
    update_time datetime default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP,
    is_del      bit                                null
)
    comment '2020年以前的订单数据';

create table t_order_2020
(
    id          int auto_increment
        primary key,
    order_id    varchar(36)                        null comment '订单ID',
    amount      decimal(18, 2)                     null comment '金额',
    order_year  int                                null comment '订单年份,用来作为分表字段',
    create_time datetime default CURRENT_TIMESTAMP null,
    update_time datetime default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP,
    is_del      bit                                null
)
    comment '2020、2021年的订单数据';

create table t_order_2022
(
    id          int auto_increment
        primary key,
    order_id    varchar(36)                        null comment '订单ID',
    amount      decimal(18, 2)                     null comment '金额',
    order_year  int                                null comment '订单年份,用来作为分表字段',
    create_time datetime default CURRENT_TIMESTAMP null,
    update_time datetime default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP,
    is_del      bit                                null
)
    comment '2021、2022年的订单数据';

create table t_order_2024
(
    id          int auto_increment
        primary key,
    order_id    varchar(36)                        null comment '订单ID',
    amount      decimal(18, 2)                     null comment '金额',
    order_year  int                                null comment '订单年份,用来作为分表字段',
    create_time datetime default CURRENT_TIMESTAMP null,
    update_time datetime default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP,
    is_del      bit                                null
)
    comment '2023、2024年的订单数据';

INSERT INTO db2025.t_order_2022 (order_id, amount, order_year) VALUES ('76cfe091-d87f-11ef-b84b-0242ac110002', 7777.13, 2022);
INSERT INTO db2025.t_order_2024 (order_id, amount, order_year) VALUES ('76d66bb8-d87f-11ef-b84b-0242ac110002', 3106.80, 2023);

搭建 SpringBoot 项目
pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.4.1</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.wheelmouse</groupId>
	<artifactId>sharding-sphere-case</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>sharding-sphere-case</name>
	<description>sharding-sphere-case</description>
	<url/>
	<licenses>
		<license/>
	</licenses>
	<developers>
		<developer/>
	</developers>
	<scm>
		<connection/>
		<developerConnection/>
		<tag/>
		<url/>
	</scm>
	<properties>
		<java.version>21</java.version>
		<shardingsphere.version>5.4.1</shardingsphere.version>
		<mybatis-plus.version>3.5.9</mybatis-plus.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.apache.shardingsphere</groupId>
			<artifactId>shardingsphere-jdbc-core</artifactId>
			<version>${shardingsphere.version}</version>
			<exclusions>
				<exclusion>
					<groupId>org.yaml</groupId>
					<artifactId>snakeyaml</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<!-- 版本冲突	-->
		<dependency>
			<groupId>org.yaml</groupId>
			<artifactId>snakeyaml</artifactId>
			<version>1.33</version>
		</dependency>
		<dependency>
			<groupId>javax.xml.bind</groupId>
			<artifactId>jaxb-api</artifactId>
			<version>2.3.1</version> <!-- 根据你的Java版本选择合适的版本 -->
		</dependency>
		<dependency>
			<groupId>org.glassfish.jaxb</groupId>
			<artifactId>jaxb-runtime</artifactId>
			<version>2.3.1</version> <!-- 根据你的Java版本选择合适的版本 -->
		</dependency>


		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jdbc</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<!-- MyBatis-Plus -->
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-boot-starter</artifactId>
			<version>3.5.7</version>
		</dependency>
		<!-- Mybatis的分页插件 -->
		<dependency>
			<groupId>com.github.pagehelper</groupId>
			<artifactId>pagehelper-spring-boot-starter</artifactId>
			<version>1.3.0</version>
		</dependency>
		<dependency>
			<groupId>com.mysql</groupId>
			<artifactId>mysql-connector-j</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<!-- MyBatis-Plus -->
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
			<version>${mybatis-plus.version}</version>
		</dependency>
		<!-- 于 v3.5.9 起,PaginationInnerInterceptor 已分离出来。如需使用,则需单独引入 mybatis-plus-jsqlparser 依赖,jdk 11+ 引入可选模块 -->
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-jsqlparser</artifactId>
			<version>${mybatis-plus.version}</version>
		</dependency>
		<!-- Mybatis的分页插件, -->
		<dependency>
			<groupId>com.github.pagehelper</groupId>
			<artifactId>pagehelper-spring-boot-starter</artifactId>
			<version>1.3.0</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<annotationProcessorPaths>
						<path>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</path>
					</annotationProcessorPaths>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.graalvm.buildtools</groupId>
				<artifactId>native-maven-plugin</artifactId>
			</plugin>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

Mapper

/**
* @author 重楼
* @description 针对表【t_order】的数据库操作Mapper
* @createDate 2025-01-23 12:55:01
* @Entity generator.domain.Order0
*/
public interface OrderMapper extends BaseMapper<Order> {

}

Mapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wheelmouse.shardingsphere.mapper.OrderMapper">

    <resultMap id="BaseResultMap" type="com.wheelmouse.shardingsphere.domain.Order">
            <id property="id" column="id" jdbcType="INTEGER"/>
            <result property="orderId" column="order_id" jdbcType="VARCHAR"/>
            <result property="amount" column="amount" jdbcType="DECIMAL"/>
            <result property="orderYear" column="order_year" jdbcType="INTEGER"/>
            <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
            <result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
            <result property="isDel" column="is_del" jdbcType="BIT"/>
    </resultMap>

    <sql id="Base_Column_List">
        id,order_id,amount,
        order_year,create_time,update_time,
        is_del
    </sql>
</mapper>

Service

/**
* @author 重楼
* @description 针对表【t_order】的数据库操作Service
* @createDate 2025-01-23 12:55:01
*/
public interface OrderService extends IService<Order> {

}

ServiceImpl

/**
* @author 重楼
* @description 针对表【t_order】的数据库操作Service实现
* @createDate 2025-01-23 12:55:01
*/
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order>
    implements OrderService {

}

以下是Sharding-jdbc的配置类,Sharding-jdbc支持yaml和java 2中方式配置,这里采用java方式配置
ShardingConfig

/**
 * @author 重楼
 * @date 2025/1/14
 * @apiNote
 */
@Configuration
public class ShardingConfig {

    @Bean
    public DataSource dataSource() throws SQLException {
        // 配置数据源
        Map<String, DataSource> dataSourceMap = new HashMap<>();
        dataSourceMap.put("ds0", createDataSource("com.mysql.cj.jdbc.Driver",
                "jdbc:mysql://localhost:3306/db2025?serverTimezone=UTC&useSSL=false", // MySQL URL
                "root", // 用户名
                "123456" // 密码
        ));

        // 这里的案例是单库,所以没有做读个数据源配置
        //dataSourceMap.put("ds1", createDataSource("com.mysql.cj.jdbc.Driver",
        //        "jdbc:mysql://127.0.0.1:3306/db2025?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true", // MySQL URL
        //        "root", // 用户名
        //        "123456" // 密码
        //));

        // 配置 Sharding-JDBC 的分片规则
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
        shardingRuleConfig.getTables().add(getOrderTableRuleConfiguration());

        // 注册自定义分片算法
        // algorithmName 由用户指定,需要和分片策略中的分片算法一致
        // type 和 props,请参考分片内置算法:https://shardingsphere.apache.org/document/current/cn/user-manual/common-config/builtin-algorithm/sharding/
        Properties shardingAlgorithmProps = new Properties();
        shardingAlgorithmProps.setProperty("strategy", "COMPLEX"); // 指定算法类型
        shardingAlgorithmProps.setProperty("algorithmClassName", MyComplexKeysShardingAlgorithm.class.getName());
        shardingRuleConfig.getShardingAlgorithms().put("my-complex-keys-sharding-algorithm",
                new AlgorithmConfiguration("CLASS_BASED", shardingAlgorithmProps));

        // 创建 ShardingSphere 数据源
        Properties properties = new Properties();
        properties.put("sql-show", true);
        return ShardingSphereDataSourceFactory.createDataSource(dataSourceMap,
                Collections.singleton(shardingRuleConfig), properties);
    }


    private ShardingTableRuleConfiguration getOrderTableRuleConfiguration() {
        // 配置表规则
        ShardingTableRuleConfiguration tableRuleConfig = new ShardingTableRuleConfiguration(
                "t_order", // 逻辑表名
                "ds0.t_order_${[" + orderActualDataNodes() + "]}" // 实际数据节点
        );

        // 配置复合分片策略
        tableRuleConfig.setTableShardingStrategy(new ComplexShardingStrategyConfiguration(
                "order_year", // 分片键
                "my-complex-keys-sharding-algorithm" // 自定义分片算法名称
        ));

        return tableRuleConfig;
    }

    /**
     * 自己实现,这里演示所以写死表名
     * @return
     */
    private String orderActualDataNodes(){
        return "0,2020,2022,2024";
    }

    private DataSource createDataSource(final String driverClassName,final String url, final String username, final String password) {
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setDriverClassName(driverClassName);
        dataSource.setJdbcUrl(url); // MySQL 连接 URL
        dataSource.setUsername(username); // 数据库用户名
        dataSource.setPassword(password); // 数据库密码
        dataSource.setMaximumPoolSize(10); // 连接池最大连接数
        dataSource.setMinimumIdle(2); // 连接池最小空闲连接数
        dataSource.setIdleTimeout(30000); // 空闲连接超时时间
        dataSource.setMaxLifetime(1800000); // 连接最大存活时间
        dataSource.setConnectionTimeout(30000); // 连接超时时间
        return dataSource;
    }

}

自定义分片算法实现类

/**
 * @author 重楼
 * @date 2025/1/10
 * @apiNote
 */
@Component
public class MyComplexKeysShardingAlgorithm implements ComplexKeysShardingAlgorithm<String> {

    private static final Logger LOGGER = LoggerFactory
            .getLogger(MyComplexKeysShardingAlgorithm.class);

    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, ComplexKeysShardingValue<String> shardingValue) {
        Map<String, Collection<String>> columnNameAndShardingValuesMap = shardingValue
                .getColumnNameAndShardingValuesMap();

        // 获取 列名-值-方式1,范围查询
        Map<String, Range<String>> columnNameAndRangeValuesMap = shardingValue
                .getColumnNameAndRangeValuesMap();
        boolean hasDateYear = columnNameAndRangeValuesMap.containsKey("order_year");
        if (!hasDateYear) {
            return Lists.newArrayList();
        }

        Range<String> orderYear = columnNameAndRangeValuesMap.get("order_year");
        String orderIdUpperValue = orderYear.upperEndpoint();// 上限
        String orderIdLowerValue = orderYear.lowerEndpoint();// 下限

        String tablePrefix = "t_order";

        // 根据年份拼接物理表表名
        List<String> actualTableList = Lists.newArrayList();

        for (int dateYear = Integer.parseInt(orderIdLowerValue); dateYear <= Integer.parseInt(orderIdUpperValue); dateYear++) {
            // 如果交易年份小于2020则使用t_order_0,如果交易时间大于当前年则忽略
            //按照不同时间类型分的表  两年一张,小于2020年以前的数据数据都放在_0结尾的表中
            if (dateYear < 2020) {
                actualTableList.add(tablePrefix  + "_0");
                continue;
            }
            if (dateYear > Year.now().getValue()) {
                continue;
            }
            // 计算年份落在哪个时间分片键上
            int yearSharding = dateYear - (dateYear % 2);
            actualTableList.add(tablePrefix + "_" + yearSharding);
        }

        actualTableList = actualTableList.stream().distinct().collect(Collectors.toList());
        LOGGER.info("actual table name is :{}", actualTableList);
        return actualTableList;

}

通过SPI的方式注册该算法,在 resources 文件夹下创建文件夹 META-INF/services。 在该文件夹下创建文件 org.apache.shardingsphere.sharding.spi.ShardingAlgorithm, 文件内写上我们自定义的分片算法。

com.wheelmouse.shardingsphere.config.MyComplexKeysShardingAlgorithm

测试类

@SpringBootTest
class ShardingSphereCaseApplicationTests {

	private final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

	@Autowired
	private OrderMapper orderMapper;

	@Test
	void list() {
		QueryWrapper<Order> queryWrapper = new QueryWrapper<>();
		queryWrapper.between("order_year", "2022", "2024");
		List<Order> list = orderMapper.selectList(queryWrapper);
		for (Order order : list) {
			System.out.println(order);
		}
	}

}

结果:
在这里插入图片描述


http://www.kler.cn/a/516181.html

相关文章:

  • Effective Python系列(1.1):区别bytes和str
  • Go Fx 和 Java Spring 的区别
  • MySQL基于gtid的主从同步配置
  • 基于JAVA的微信点餐小程序设计与实现(LW+源码+讲解)
  • Day 15 卡玛笔记
  • Spring Boot整合Thymeleaf、JDBC Template与MyBatis配置详解
  • 云计算中的微服务架构是什么
  • autogen 中的 Teams 示例
  • 【数据结构进阶】红黑树超详解 + 实现(附源码)
  • 【探索 Kali Linux】渗透测试与网络安全的终极操作系统
  • 使用github提交Pull Request的完整流程
  • 差分进化算法 (Differential Evolution) 算法详解及案例分析
  • HTML5 新的 Input 类型详解
  • 计算机图形学:实验二 三维模型读取与控制
  • C++ 入门速通1【黑马】
  • 52.this.DataContext = new UserViewModel(); C#例子 WPF例子
  • Python数据类型与操作
  • matlab计算功率谱的四种方法
  • 【Linux】Linux的基本指令(1),包括ls、pwd、cd、touch、mkdir、rm、man、cp、mv、cat
  • Vue2:使用sortablejs实现el-table中行拖拽调整顺序
  • 进程优先级
  • C语言-内存管理
  • 一个面向领域的直播平台开源!
  • Codeforces Round 1000 (Div. 2)(A-D)
  • 安宝特方案 | 智能培训:安宝特AR如何提升企业技能培训的效率与互动性
  • Zookeeper启动指定JDK版本