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

相关推荐
勇气要爆发2 小时前
向量数据库 Milvus 极速入门:从 Docker 部署到 Python 增删改查实战
数据库·docker·milvus
xuefuhe2 小时前
如何连接到postgresql数据库
数据库
好好学操作系统2 小时前
notion+excel自动创建表格| 了解了notion api
数据库·python·oracle·excel·notion
爱吃山竹的大肚肚2 小时前
达梦(DM)数据库中设置表空间
java·数据库·sql·mysql·spring·spring cloud·oracle
启明真纳3 小时前
MySQL基本概念
数据库·mysql
wregjru3 小时前
【QT】1.QT 基础入门
数据库
2301_818732063 小时前
前端一直获取不到后端的值,和数据库字段设置有关 Oracle
前端·数据库·sql·oracle
皙然3 小时前
MyBatis 执行流程源码级深度解析:从 Mapper 接口到 SQL 执行的全链路逻辑
数据库·sql·mybatis
vx_bisheyuange3 小时前
基于SpringBoot的酒店管理系统
前端·javascript·vue.js·spring boot·毕业设计