springboot整合shardingsphere-jdbc5.1.1-按月分表

文章目录


环境

JDK17

spring-boot-starter-parent2.7.0

shardingsphere-jdbc5.1.1

数据库表

sql 复制代码
CREATE TABLE `goods` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
  `number` int DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

CREATE TABLE `orders_202509` (
  `id` bigint NOT NULL,
  `create_time` datetime DEFAULT NULL,
  `price` decimal(16,4) DEFAULT NULL,
  `status` tinyint DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

CREATE TABLE `orders_202510` (
  `id` bigint NOT NULL,
  `create_time` datetime DEFAULT NULL,
  `price` decimal(16,4) DEFAULT NULL,
  `status` tinyint DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

CREATE TABLE `orders_202511` (
  `id` bigint NOT NULL,
  `create_time` datetime DEFAULT NULL,
  `price` decimal(16,4) DEFAULT NULL,
  `status` tinyint DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

CREATE TABLE `orders_202512` (
  `id` bigint NOT NULL,
  `create_time` datetime DEFAULT NULL,
  `price` decimal(16,4) DEFAULT NULL,
  `status` tinyint DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

pom.xml

xml 复制代码
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.qf</groupId>
<artifactId>sharding-jdbc-test2</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<name>sharding-jdbc-test2</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
    <dependencies>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <dependency>
          <groupId>org.apache.shardingsphere</groupId>
          <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
        
        <!--      <version>5.1.1</version>-->
        <version>5.2.0</version>
        </dependency>
        <!--阿里数据库连接池 -->
        <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>druid-spring-boot-starter</artifactId>
          <version>1.1.14</version>
        </dependency>
        <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>8.0.21</version>
          <scope>runtime</scope>
        </dependency>
        
        <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.2.0</version>
        </dependency>
        <!-- mybatis plus 代码生成器 -->
        <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.3.1</version>
        </dependency>
        <dependency>
          <groupId>org.projectlombok</groupId>
          <artifactId>lombok</artifactId>
          <version>1.18.10</version>
          <optional>true</optional>
        </dependency>
        
        <dependency>
          <groupId>cn.hutool</groupId>
          <artifactId>hutool-all</artifactId>
          <version>4.6.3</version>
        </dependency>
        
        <dependency>
          <groupId>org.apache.commons</groupId>
          <artifactId>commons-lang3</artifactId>
          <version>3.9</version>
        </dependency>
        
        <!--  json序列化  -->
        <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>fastjson</artifactId>
          <version>1.2.73</version>
        </dependency>
    
    </dependencies>
</project>

配置

js 复制代码
server:
    port: 18080
mybatis-plus:
    global-config:
        db-config:
            id-type: none
    mapper-locations: classpath:/mapper/*.xml
    type-aliases-package: com.qf.entity
spring:
    main:
        #启用Bean覆盖 解决同名的dataSource Bean定义冲突
        allow-bean-definition-overriding: true
    shardingsphere:
        props:
            sql-show: true
        datasource:
          names: ds0
          ds0:
            type: com.alibaba.druid.pool.DruidDataSource
            driver-class-name: com.mysql.jdbc.Driver
            url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&allowMultiQueries=true&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
            username: root
            password: 123456
        rules:
          sharding:
            # 表的分片策略
            tables:
              # 逻辑表的名称
              meteorology_device_record:
                # 数据节点配置,采用Groovy表达式
                actual-data-nodes: ds0.meteorology_device_record_$->{202509..202512}
                # 配置策略
                table-strategy:
                  # 用于单分片键的标准分片场景
                  standard:
                    sharding-column: detected_time
                    # 分片算法名字
                    sharding-algorithm-name: monthly_sharding
                key-generate-strategy: # 主键生成策略
                  column: id  # 主键列
                  key-generator-name: snowflake  # 策略算法名称(推荐使用雪花算法)
              orders:
                # 数据节点配置,采用Groovy表达式
                actual-data-nodes: ds0.orders_$->{202509..202512}
                # 配置策略
                table-strategy:
                  # 用于单分片键的标准分片场景
                  standard:
                    sharding-column: create_time
                    # 分片算法名字
                    sharding-algorithm-name: order_sharding
                key-generate-strategy: # 主键生成策略
                  column: id  # 主键列
                  key-generator-name: snowflake  # 策略算法名称(推荐使用雪花算法)
            sharding-algorithms:
              monthly_sharding:
                type: CLASS_BASED  # 或其他合适类型
                props:
                  strategy: STANDARD
                  algorithmClassName: com.qf.config.MonthlyShardingAlgorithm
              order_sharding:
                type: CLASS_BASED
                props:
                  strategy: STANDARD
                  algorithmClassName: com.qf.config.OrderShardingAlgorithm
            key-generators:
              snowflake:
                type: SNOWFLAKE

logging:
    level:
        org.apache.shardingsphere: INFO
java 复制代码
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MybatisPlusConfig {
    @Bean
    public PaginationInterceptor mybatisPlusInterceptor() {
        PaginationInterceptor interceptor = new PaginationInterceptor();
        // 添加分页拦截器
        interceptor.setDbType(DbType.MYSQL); // 设置数据库类
        return interceptor;
    }
}
java 复制代码
import cn.hutool.core.date.DateUtil;
import com.google.common.collect.Range;
import lombok.extern.slf4j.Slf4j;
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 java.util.*;


@Slf4j
public class OrderShardingAlgorithm implements StandardShardingAlgorithm<Date> {
    @Override
    public String doSharding(Collection<String> collection, PreciseShardingValue<Date> preciseShardingValue) {
        Date date = preciseShardingValue.getValue();
        String suffix = DateUtil.format(date,"yyyyMM");
         for (String tableName : collection) {
             if (tableName.endsWith(suffix)) {
                 return tableName;
             }
         }
         throw new IllegalArgumentException("No matching table found for date: " + date);    
    }
    
    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Date> rangeShardingValue) {
        Set<String> result = new LinkedHashSet<>();
         // 获取范围边界
         Range<Date> valueRange = rangeShardingValue.getValueRange();
        
         // 处理不同类型的范围查询
         if (valueRange.hasLowerBound() && valueRange.hasUpperBound()) {
             // BETWEEN情况: 处理区间范围
             Date lowerDate = valueRange.lowerEndpoint();
             Date upperDate = valueRange.upperEndpoint();
        
             // 生成从开始月份到结束月份的所有可能表
             Calendar current = Calendar.getInstance();
             current.setTime(lowerDate);
        
             Calendar end = Calendar.getInstance();
             end.setTime(upperDate);
        
             while (!current.after(end)) {
                 String suffix = DateUtil.format(current.getTime(), "yyyyMM");
                 for (String tableName : availableTargetNames) {
                     if (tableName.endsWith(suffix)) {
                         result.add(tableName);
                         break;
                     }
                 }
                 // 移动到下一个月
                 current.add(Calendar.MONTH, 1);
             }
         } else if (valueRange.hasLowerBound()) {
             // >= 情况: 大于等于某个时间
             Date lowerDate = valueRange.lowerEndpoint();
             for (String tableName : availableTargetNames) {
                 String tableSuffix = extractSuffixFromTableName(tableName);
                 if (tableSuffix != null) {
                     try {
                         Date tableDate = DateUtil.parse(tableSuffix, "yyyyMM");
                         Date lowerBoundDate = DateUtil.parse(DateUtil.format(lowerDate, "yyyyMM"), "yyyyMM");
        
                         // 如果表的时间大于等于查询起始时间,则包含该表
                         if (!tableDate.before(lowerBoundDate)) {
                             result.add(tableName);
                         }
                     } catch (Exception e) {
                         log.warn("Failed to parse date for table: {}", tableName, e);
                     }
                 }
             }
         } else if (valueRange.hasUpperBound()) {
             // <= 情况: 小于等于某个时间
             Date upperDate = valueRange.upperEndpoint();
             for (String tableName : availableTargetNames) {
                 String tableSuffix = extractSuffixFromTableName(tableName);
                 if (tableSuffix != null) {
                     try {
                         Date tableDate = DateUtil.parse(tableSuffix, "yyyyMM");
                         Date upperBoundDate = DateUtil.parse(DateUtil.format(upperDate, "yyyyMM"), "yyyyMM");
        
                         // 如果表的时间小于等于查询结束时间,则包含该表
                         if (!tableDate.after(upperBoundDate)) {
                             result.add(tableName);
                         }
                     } catch (Exception e) {
                         log.warn("Failed to parse date for table: {}", tableName, e);
                     }
                 }
             }
         }
     // 如果结果为空,返回所有表(保守策略)
     return result.isEmpty() ? availableTargetNames : result;
    }
    /**
    从表名中提取年月后缀*/
    private String extractSuffixFromTableName(String tableName) {
        // 假设表名以年月结尾,如 table_202509
        int lastIndex = tableName.lastIndexOf("_");
        if (lastIndex > 0 && lastIndex < tableName.length() - 1) {
            String suffix = tableName.substring(lastIndex + 1);
            if (suffix.matches("\\d{6}")) { 
                // 匹配 yyyyMM 格式
                return suffix;
            }
        }
        return null;
    }
    
    @Override
    public String getType() {
        return "";
    }
    @Override
    public Properties getProps() {
        return null;
    }
    @Override
    public void init(Properties properties) {
    }
}

启动类

java 复制代码
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
@author qixiansheng
@version 1.0
@data 2025/12/7 11:01
*/
@SpringBootApplication(exclude = DruidDataSourceAutoConfigure.class)
@MapperScan("com.qf.mapper")
public class MainApplication {
    public static void main(String[] args) {
        SpringApplication.run(MainApplication.class, args);
    }
}

实体类

java 复制代码
import lombok.Data;
/**
@author qixiansheng
@version 1.0
@data 2025/12/18 8:14
*/
@Data
public class Goods {
		private Long id;
    private String name;
    private Integer number;
}
java 复制代码
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
@author qixiansheng
@version 1.0
@data 2025/12/18 21:31
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class Orders implements Serializable {
    private static final long serialVersionUID = 1L;
    private Long id;
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    private BigDecimal price;
    private Integer status;
}

mapper

java 复制代码
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qf.entity.Goods;
import org.apache.ibatis.annotations.Mapper;
/**
@author qixiansheng
@version 1.0
@data 2025/12/18 8:15
*/
@Mapper
public interface GoodsMapper extends BaseMapper<Goods> {
    
}
java 复制代码
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qf.entity.Orders;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
@author qixiansheng
@version 1.0
@data 2025/12/18 21:33
*/
@Mapper
public interface OrderMapper extends BaseMapper<Orders> {
    int batchInsert(@Param("list") List<Orders> orders);
    IPage<Orders> getPage(@Param("page") Page page, @Param("params") Map<String, Object> params);
}
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.qf.mapper.OrderMapper">
    <insert id="batchInsert">
        insert into orders(id,create_time,price,status) values
        <foreach collection="list" item="item" separator=",">
            (#{item.id},#{item.createTime},#{item.price},#{item.status})
        </foreach>
    </insert>
    
    <select id="getPage" resultType="com.qf.entity.Orders">
        select *
        from orders
        <where>
            <if test="params.createTime != '' and params.createTime != null">
                and create_time = #{params.createTime}
            </if>
        </where>
    </select>
</mapper>

controller

java 复制代码
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qf.entity.Goods;
import com.qf.entity.Orders;
import com.qf.mapper.GoodsMapper;
import com.qf.mapper.OrderMapper;
import com.qf.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;
import java.util.*;
/**
@author qixiansheng
@version 1.0
@data 2025/12/18 21:31
*/
@RestController
@RequestMapping("/order")
public class OrderController {
    @Autowired
    private OrderService orderService;
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private GoodsMapper goodsMapper;
    
    /**
    给每张表插入数据
    @return
    */
    @GetMapping("/add")
    public Boolean add() {
        Date date = DateUtil.parse("2025-08-01 21:00:00");
        List<Orders> orders = new ArrayList<>();
        for (int i = 1; i <= 4; i++) {
            Date offset = DateUtil.offsetMonth(date, i);
            for (int j = 0; j < 4; j++) {
                Orders order = new Orders();
                order.setId(IdUtil.getSnowflake(1, 1).nextId());
                order.setCreateTime(DateUtil.offsetDay(offset, j));
                order.setPrice(new BigDecimal(j));
                order.setStatus(1);
                orders.add(order);
            }
        }
        orderMapper.batchInsert(orders);
        return true;
    }
    
    @GetMapping("/getPage")
    public String getPage(@RequestParam Map<String, Object> params) {
        Page page = new Page<>(Integer.parseInt(Optional.ofNullable(params.get("page")).orElse("1").toString()),
        Integer.parseInt(Optional.ofNullable(params.get("limit")).orElse("5").toString()));
        //        Page reuslt = orderMapper.selectPage(page, new LambdaQueryWrapper<Orders>());
        IPage<Orders> result = orderMapper.getPage(page,params);
        return JSON.toJSONString(result);
    }
    
    @GetMapping("/getPage2")
    public String getPage2(@RequestParam Map<String, Object> params) {
        Page page = new Page<>(Integer.parseInt(Optional.ofNullable(params.get("page")).orElse("1").toString()),
        Integer.parseInt(Optional.ofNullable(params.get("limit")).orElse("5").toString()));
        Page reuslt = goodsMapper.selectPage(page, new LambdaQueryWrapper<Goods>());
        return JSON.toJSONString(reuslt);
    }
    
    @GetMapping("/getList")
    public String getList(@RequestParam Map<String, Object> params) {
        Date date = DateUtil.parse("2025-09-01 21:00:00");
        List<Orders> orders1 = orderMapper.selectList(new LambdaQueryWrapper<Orders>()
            .eq(Orders::getCreateTime, date));
         Date dateStart = DateUtil.parse("2025-09-01 00:00:00");
         Date dateEnd = DateUtil.parse("2025-10-30 23:59:59");
         List<Orders> orders2 = orderMapper.selectList(new LambdaQueryWrapper<Orders>()
                 .ge(Orders::getCreateTime, date));
         List<Orders> orders3 = orderMapper.selectList(new LambdaQueryWrapper<Orders>()
                 .le(Orders::getCreateTime, dateEnd));
         List<Orders> orders4 = orderMapper.selectList(new LambdaQueryWrapper<Orders>()
                 .ge(Orders::getCreateTime, dateStart)
                 .le(Orders::getCreateTime, dateEnd));
         List<Orders> orders5 = orderMapper.selectList(new LambdaQueryWrapper<Orders>()
                 .between(Orders::getCreateTime, dateStart, dateEnd));
         return JSON.toJSONString(orders1);
    }
    
    @GetMapping("/getList2")
    public String getList2(@RequestParam Map<String, Object> params) {
        List<Goods> list = goodsMapper.selectList(new LambdaQueryWrapper<Goods>());
        return JSON.toJSONString(list);
    }
}

相关推荐
KevinCyao2 小时前
java视频短信接口怎么调用?SpringBoot集成视频短信及回调处理Demo
java·spring boot·音视频
科技小花2 小时前
数据治理平台架构演进观察:AI原生设计如何重构企业数据管理范式
数据库·重构·架构·数据治理·ai-native·ai原生
一江寒逸2 小时前
零基础从入门到精通MySQL(中篇):进阶篇——吃透多表查询、事务核心与高级特性,搞定复杂业务SQL
数据库·sql·mysql
D4c-lovetrain2 小时前
linux个人心得22 (mysql)
数据库·mysql
總鑽風2 小时前
搭建Spring Boot + ELK日志平台,实现可视化日志监控
spring boot·elk·macos
阿里小阿希2 小时前
CentOS7 PostgreSQL 9.2 升级到 15 完整教程
数据库·postgresql
荒川之神3 小时前
Oracle 数据仓库雪花模型设计(完整实战方案)
数据库·数据仓库·oracle
做个文艺程序员3 小时前
MySQL安全加固十大硬核操作
数据库·mysql·安全
不吃香菜学java3 小时前
Redis简单应用
数据库·spring boot·tomcat·maven
新知图书3 小时前
搭建Spring Boot开发环境
java·spring boot·后端