sharding官网参考
https://shardingsphere.apache.org/document/current/cn/overview/
https://shardingsphere.apache.org/document/legacy/4.x/document/cn/features/sharding/use-norms/pagination/
https://shardingsphere.apache.org/document/legacy/4.x/document/cn/downloads/#%E6%9C%80%E6%96%B0%E7%89%88%E6%9C%AC
https://shardingsphere.apache.org/document/5.0.0/cn/features/sharding/
优秀文章参考
ShardingSphere-JDBC - YAML 配置 - 《Apache ShardingSphere v5.1.0 中文文档》 - 书栈网 · BookStack
https://www.cnblogs.com/xfeiyun/p/16185727.html
ShardingJDBC-5.0.0及4.0.0使用示例_sharding-jdbc 5._红石丶的博客-CSDN博客
引言:
sharding各个版本相对而言不能配置都有差异,而sharding5.0.0.0相对而言是比较新的,网上相关的资料少的可怜,参考了非常多的文章写下demo,供大家参考
集成
1 当前使用shardingsphere-jdbc-core-spring-boot-starter 5.0.0版本
<!-- 动态数据源 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>${dynamic-datasource-spring-boot-starter.version}</version>
</dependency>
<!-- 分库分表 -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.0.0</version>
</dependency>
因为数据源之前框架已经有druild,为了不影响之前的框架数据源配置,增加动态多数据源
package org.jeecg.boot.shardingsphere.config;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.provider.AbstractDataSourceProvider;
import com.baomidou.dynamic.datasource.provider.DynamicDataSourceProvider;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
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.annotation.Resource;
import javax.sql.DataSource;
import java.util.Map;
/**
* 分库分表数据源配置
* @author zyf
*/
@Configuration
@AutoConfigureBefore({DynamicDataSourceAutoConfiguration.class, SpringBootConfiguration.class})
public class DataSourceConfiguration {
/**
* 分表数据源名称
*/
public static final String SHARDING_DATA_SOURCE_NAME = "sharding";
/**
* 动态数据源配置项
*/
@Resource
private DynamicDataSourceProperties dynamicDataSourceProperties;
@Lazy
@Resource
DataSource shardingDataSource;
/**
* 将shardingDataSource放到了多数据源(dataSourceMap)中
* 注意有个版本的bug,3.1.1版本 不会进入loadDataSources 方法,这样就一直造成数据源注册失败
*/
@Bean
public DynamicDataSourceProvider dynamicDataSourceProvider() {
Map<String, DataSourceProperty> datasourceMap = dynamicDataSourceProperties.getDatasource();
return new AbstractDataSourceProvider() {
@Override
public Map<String, DataSource> loadDataSources() {
Map<String, DataSource> dataSourceMap = createDataSourceMap(datasourceMap);
// 将 shardingjdbc 管理的数据源也交给动态数据源管理
dataSourceMap.put(SHARDING_DATA_SOURCE_NAME, shardingDataSource);
return dataSourceMap;
}
};
}
/**
* 将动态数据源设置为首选的
* 当spring存在多个数据源时, 自动注入的是首选的对象
* 设置为主要的数据源之后,就可以支持shardingjdbc原生的配置方式了
*
* @return
*/
@Primary
@Bean
public DataSource dataSource(DynamicDataSourceProvider dynamicDataSourceProvider) {
DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();
dataSource.setPrimary(dynamicDataSourceProperties.getPrimary());
dataSource.setStrict(dynamicDataSourceProperties.getStrict());
dataSource.setStrategy(dynamicDataSourceProperties.getStrategy());
dataSource.setProvider(dynamicDataSourceProvider);
dataSource.setP6spy(dynamicDataSourceProperties.getP6spy());
dataSource.setSeata(dynamicDataSourceProperties.getSeata());
return dataSource;
}
}
2 项目结构
3 初始化sql
#1按照年月范围分表sql
CREATE TABLE `t_order_2020_6` (
`id` bigint(20) NOT NULL COMMENT '主键',
`name` varchar(20) DEFAULT NULL COMMENT '名称',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`user_name` varchar(20) DEFAULT NULL COMMENT '订单所属用户名',
PRIMARY KEY (`id`),
KEY `user_name_index` (`user_name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='订单表';
CREATE TABLE `t_order_item_2020_6` (
`id` bigint(20) NOT NULL COMMENT '主键',
`order_id` bigint(20) DEFAULT NULL COMMENT '订单ID',
`name` varchar(20) DEFAULT NULL COMMENT '名称',
`order_create_time` datetime DEFAULT NULL COMMENT '订单创建时间',
PRIMARY KEY (`id`),
KEY `order_id_index` (`order_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='订单商品表';
CREATE TABLE `t_order_middle_test` (
`id` BIGINT(20) NOT NULL COMMENT '主键',
`order_id` VARCHAR(20) DEFAULT NULL COMMENT '名称',
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT='订单中间表测试'
#2按照年范围分表sql
CREATE TABLE `t_order_2022` (
`id` BIGINT(20) NOT NULL COMMENT '主键',
`name` VARCHAR(20) DEFAULT NULL COMMENT '名称',
`create_time` DATETIME DEFAULT NULL COMMENT '创建时间',
`user_name` VARCHAR(20) DEFAULT NULL COMMENT '订单所属用户名',
PRIMARY KEY (`id`),
KEY `user_name_index` (`user_name`) USING BTREE
) ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT='订单表'
CREATE TABLE `t_order_2023` (
`id` BIGINT(20) NOT NULL COMMENT '主键',
`name` VARCHAR(20) DEFAULT NULL COMMENT '名称',
`create_time` DATETIME DEFAULT NULL COMMENT '创建时间',
`user_name` VARCHAR(20) DEFAULT NULL COMMENT '订单所属用户名',
PRIMARY KEY (`id`),
KEY `user_name_index` (`user_name`) USING BTREE
) ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT='订单表'
CREATE TABLE `t_order_item_2022` (
`id` BIGINT(20) NOT NULL COMMENT '主键',
`order_id` BIGINT(20) DEFAULT NULL COMMENT '订单ID',
`name` VARCHAR(20) DEFAULT NULL COMMENT '名称',
`order_create_time` DATETIME DEFAULT NULL COMMENT '订单创建时间',
PRIMARY KEY (`id`),
KEY `order_id_index` (`order_id`) USING BTREE
) ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT='订单商品表'
CREATE TABLE `t_order_item_2023` (
`id` BIGINT(20) NOT NULL COMMENT '主键',
`order_id` BIGINT(20) DEFAULT NULL COMMENT '订单ID',
`name` VARCHAR(20) DEFAULT NULL COMMENT '名称',
`order_create_time` DATETIME DEFAULT NULL COMMENT '订单创建时间',
PRIMARY KEY (`id`),
KEY `order_id_index` (`order_id`) USING BTREE
) ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT='订单商品表'
CREATE TABLE `sys_log` (
`id` VARCHAR(32) NOT NULL,
`log_type` INT(2) DEFAULT NULL COMMENT '日志类型(1登录日志,2操作日志)',
`log_content` VARCHAR(1000) DEFAULT NULL COMMENT '日志内容',
`operate_type` INT(2) DEFAULT NULL COMMENT '操作类型',
`userid` VARCHAR(32) DEFAULT NULL COMMENT '操作用户账号',
`username` VARCHAR(100) DEFAULT NULL COMMENT '操作用户名称',
`ip` VARCHAR(100) DEFAULT NULL COMMENT 'IP',
`method` VARCHAR(500) DEFAULT NULL COMMENT '请求java方法',
`request_url` VARCHAR(255) DEFAULT NULL COMMENT '请求路径',
`request_param` LONGTEXT DEFAULT NULL COMMENT '请求参数',
`request_type` VARCHAR(10) DEFAULT NULL COMMENT '请求类型',
`cost_time` BIGINT(20) DEFAULT NULL COMMENT '耗时',
`create_by` VARCHAR(32) DEFAULT NULL COMMENT '创建人',
`create_time` DATETIME DEFAULT NULL COMMENT '创建时间',
`update_by` VARCHAR(32) DEFAULT NULL COMMENT '更新人',
`update_time` DATETIME DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
KEY `index_table_userid` (`userid`) USING BTREE,
KEY `index_logt_ype` (`log_type`) USING BTREE,
KEY `index_operate_type` (`operate_type`) USING BTREE,
KEY `index_createtime` (`create_time`) USING BTREE
) ENGINE=MYISAM DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='系统日志表';
CREATE TABLE `sys_log0` (
`id` varchar(32) NOT NULL,
`log_type` int(2) DEFAULT NULL COMMENT '日志类型(1登录日志,2操作日志)',
`log_content` varchar(1000) DEFAULT NULL COMMENT '日志内容',
`operate_type` int(2) DEFAULT NULL COMMENT '操作类型',
`userid` varchar(32) DEFAULT NULL COMMENT '操作用户账号',
`username` varchar(100) DEFAULT NULL COMMENT '操作用户名称',
`ip` varchar(100) DEFAULT NULL COMMENT 'IP',
`method` varchar(500) DEFAULT NULL COMMENT '请求java方法',
`request_url` varchar(255) DEFAULT NULL COMMENT '请求路径',
`request_param` longtext DEFAULT NULL COMMENT '请求参数',
`request_type` varchar(10) DEFAULT NULL COMMENT '请求类型',
`cost_time` bigint(20) DEFAULT NULL COMMENT '耗时',
`create_by` varchar(32) DEFAULT NULL COMMENT '创建人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(32) DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
KEY `index_table_userid` (`userid`) USING BTREE,
KEY `index_logt_ype` (`log_type`) USING BTREE,
KEY `index_operate_type` (`operate_type`) USING BTREE,
KEY `index_createtime` (`create_time`) USING BTREE
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='系统日志表';
CREATE TABLE `sys_log1` (
`id` varchar(32) NOT NULL,
`log_type` int(2) DEFAULT NULL COMMENT '日志类型(1登录日志,2操作日志)',
`log_content` varchar(1000) DEFAULT NULL COMMENT '日志内容',
`operate_type` int(2) DEFAULT NULL COMMENT '操作类型',
`userid` varchar(32) DEFAULT NULL COMMENT '操作用户账号',
`username` varchar(100) DEFAULT NULL COMMENT '操作用户名称',
`ip` varchar(100) DEFAULT NULL COMMENT 'IP',
`method` varchar(500) DEFAULT NULL COMMENT '请求java方法',
`request_url` varchar(255) DEFAULT NULL COMMENT '请求路径',
`request_param` longtext DEFAULT NULL COMMENT '请求参数',
`request_type` varchar(10) DEFAULT NULL COMMENT '请求类型',
`cost_time` bigint(20) DEFAULT NULL COMMENT '耗时',
`create_by` varchar(32) DEFAULT NULL COMMENT '创建人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(32) DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
KEY `index_table_userid` (`userid`) USING BTREE,
KEY `index_logt_ype` (`log_type`) USING BTREE,
KEY `index_operate_type` (`operate_type`) USING BTREE,
KEY `index_createtime` (`create_time`) USING BTREE
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='系统日志表';
CREATE TABLE `tb_cust` (
`cust_id` BIGINT (20) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`cust_name` VARCHAR (20) NOT NULL COMMENT '用户名称',
`branch_id` CHAR(3) NOT NULL COMMENT '分公司',
`create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`cust_id`)
) ENGINE = INNODB AUTO_INCREMENT = 202 DEFAULT CHARSET = utf8 COMMENT = '客户信息表';
CREATE TABLE `tb_cust_fc5` (
`cust_id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`cust_name` VARCHAR(20) NOT NULL COMMENT '用户名称',
`branch_id` CHAR(3) NOT NULL COMMENT '分公司',
`create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`cust_id`)
) ENGINE=INNODB AUTO_INCREMENT=202 DEFAULT CHARSET=utf8 COMMENT='客户信息表';
CREATE TABLE `tb_cust_fdg` (
`cust_id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`cust_name` VARCHAR(20) NOT NULL COMMENT '用户名称',
`branch_id` CHAR(3) NOT NULL COMMENT '分公司',
`create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`cust_id`)
) ENGINE=INNODB AUTO_INCREMENT=202 DEFAULT CHARSET=utf8 COMMENT='客户信息表';
CREATE TABLE `tb_cust_fdm` (
`cust_id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`cust_name` VARCHAR(20) NOT NULL COMMENT '用户名称',
`branch_id` CHAR(3) NOT NULL COMMENT '分公司',
`create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`cust_id`)
) ENGINE=INNODB AUTO_INCREMENT=202 DEFAULT CHARSET=utf8 COMMENT='客户信息表';
CREATE TABLE `tb_cust_fdw` (
`cust_id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`cust_name` VARCHAR(20) NOT NULL COMMENT '用户名称',
`branch_id` CHAR(3) NOT NULL COMMENT '分公司',
`create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`cust_id`)
) ENGINE=INNODB AUTO_INCREMENT=202 DEFAULT CHARSET=utf8 COMMENT='客户信息表';
CREATE TABLE `t_complex` (
`order_id` BIGINT(20) NOT NULL,
`user_id` BIGINT(20) NOT NULL,
`address_id` BIGINT(20) NOT NULL,
`city` VARCHAR(32) DEFAULT NULL,
`status` TINYINT(4) DEFAULT NULL,
`creator` VARCHAR(32) DEFAULT NULL,
`create_time` DATETIME DEFAULT NULL,
`updater` VARCHAR(32) DEFAULT NULL,
`update_time` DATETIME DEFAULT NULL,
PRIMARY KEY (`order_id`) USING BTREE
) ENGINE=INNODB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMI;
CREATE TABLE `t_complex_0` (
`order_id` BIGINT(20) NOT NULL,
`user_id` BIGINT(20) NOT NULL,
`address_id` BIGINT(20) NOT NULL,
`city` VARCHAR(32) DEFAULT NULL,
`status` TINYINT(4) DEFAULT NULL,
`creator` VARCHAR(32) DEFAULT NULL,
`create_time` DATETIME DEFAULT NULL,
`updater` VARCHAR(32) DEFAULT NULL,
`update_time` DATETIME DEFAULT NULL,
PRIMARY KEY (`order_id`) USING BTREE
) ENGINE=INNODB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
CREATE TABLE `t_complex_1` (
`order_id` BIGINT(20) NOT NULL,
`user_id` BIGINT(20) NOT NULL,
`address_id` BIGINT(20) NOT NULL,
`city` VARCHAR(32) DEFAULT NULL,
`status` TINYINT(4) DEFAULT NULL,
`creator` VARCHAR(32) DEFAULT NULL,
`create_time` DATETIME DEFAULT NULL,
`updater` VARCHAR(32) DEFAULT NULL,
`update_time` DATETIME DEFAULT NULL,
PRIMARY KEY (`order_id`) USING BTREE
) ENGINE=INNODB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
CREATE TABLE `t_complex_2` (
`order_id` BIGINT(20) NOT NULL,
`user_id` BIGINT(20) NOT NULL,
`address_id` BIGINT(20) NOT NULL,
`city` VARCHAR(32) DEFAULT NULL,
`status` TINYINT(4) DEFAULT NULL,
`creator` VARCHAR(32) DEFAULT NULL,
`create_time` DATETIME DEFAULT NULL,
`updater` VARCHAR(32) DEFAULT NULL,
`update_time` DATETIME DEFAULT NULL,
PRIMARY KEY (`order_id`) USING BTREE
) ENGINE=INNODB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
CREATE TABLE `t_complex_3` (
`order_id` BIGINT(20) NOT NULL,
`user_id` BIGINT(20) NOT NULL,
`address_id` BIGINT(20) NOT NULL,
`city` VARCHAR(32) DEFAULT NULL,
`status` TINYINT(4) DEFAULT NULL,
`creator` VARCHAR(32) DEFAULT NULL,
`create_time` DATETIME DEFAULT NULL,
`updater` VARCHAR(32) DEFAULT NULL,
`update_time` DATETIME DEFAULT NULL,
PRIMARY KEY (`order_id`) USING BTREE
) ENGINE=INNODB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
CREATE TABLE `tb_dict` (
`id` BIGINT UNSIGNED NOT NULL COMMENT '主键',
`status_code` TINYINT NOT NULL DEFAULT 1 COMMENT '状态编号',
`status_name` VARCHAR(10) NOT NULL DEFAULT '' COMMENT '状态名称',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_status_code` (`status_code`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COMMENT='状态码表';
CREATE TABLE `tb_student` (
`order_id` BIGINT(20) NOT NULL COMMENT '主键',
`user_id` INT UNSIGNED NOT NULL DEFAULT '0' COMMENT '用户id',
`price` INT UNSIGNED NOT NULL DEFAULT '0' COMMENT '价格(单位:分)',
`order_status` TINYINT UNSIGNED NOT NULL DEFAULT '1' COMMENT '订单状态(1:待付款,2:已付款,3:已取消)',
`order_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`title` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '订单标题',
PRIMARY KEY (`order_id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_order_time` (`order_time`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';
CREATE TABLE `tb_student_1` (
`order_id` BIGINT(20) NOT NULL COMMENT '主键',
`user_id` INT UNSIGNED NOT NULL DEFAULT '0' COMMENT '用户id',
`price` INT UNSIGNED NOT NULL DEFAULT '0' COMMENT '价格(单位:分)',
`order_status` TINYINT UNSIGNED NOT NULL DEFAULT '1' COMMENT '订单状态(1:待付款,2:已付款,3:已取消)',
`order_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`title` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '订单标题',
PRIMARY KEY (`order_id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_order_time` (`order_time`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';
CREATE TABLE `tb_student_2` (
`order_id` BIGINT(20) NOT NULL COMMENT '主键',
`user_id` INT UNSIGNED NOT NULL DEFAULT '0' COMMENT '用户id',
`price` INT UNSIGNED NOT NULL DEFAULT '0' COMMENT '价格(单位:分)',
`order_status` TINYINT UNSIGNED NOT NULL DEFAULT '1' COMMENT '订单状态(1:待付款,2:已付款,3:已取消)',
`order_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`title` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '订单标题',
PRIMARY KEY (`order_id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_order_time` (`order_time`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';
4 application配置(需要哪个放开注释就可以)
server:
port: 7010
spring:
profiles:
active: sharding
application:
name: jeecg-sharding
config:
import:
- optional:nacos:jeecg.yaml
- optional:nacos:jeecg-@profile.name@.yaml
#导入classpath下,按照年月范围分表
#- classpath:sharding-y-m-range-dev.yml
#导入classpath下,按照年范围分表
# - classpath:sharding-y-range-dev.yml
#导入classpath下,分库分表
#- classpath:application-sharding-multi.yml
#需求:业务上存在多个分支机构,需要将不同分支的客户拆分到不同的表
#- classpath:sharding-diff-branch-dev.yml
#复合分片算法单表
#- classpath:sharding-complex-dev.yml
#广播表
- classpath:application-sharding-broadcast.yml
cloud:
nacos:
config:
server-addr: @config.server-addr@
discovery:
server-addr: ${spring.cloud.nacos.config.server-addr}
案列
案列1:按照年月范围分表
sharding-y-m-range-dev.yml
#单库分表配置
spring:
shardingsphere:
props:
sql-show: true
datasource:
names: ds
#添加分库数据源
ds:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://jeecg-boot-mysql:3306/shardingjdbc?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: xgm@2023..
type: com.alibaba.druid.pool.DruidDataSource
# 规则配置
rules:
sharding:
tables:
# 逻辑表名称
t_order:
#配置具体表的数据节点
actual-data-nodes: ds.t_order_$->{2023..2023}_$->{3..6}
# 分表策略None(不分片),Inline(最基础的策略、只支持普通的查询和排序),
#Standard(可支持范围查询,但查询条件若不是分片键或没有查询条件,则查所有表。需要定制具体的分片算法类),
#Complex(支持范围查询,并支持多分片键的复杂分片策略)
#Hint(操作时可以指定具体的库、表。无需分片键,只需定制具体的分片算法类)
table-strategy:
#分配策略:
standard:
# 分片算法名称
sharding-algorithm-name: table-classbased
# 分片列名称(对应数据库字段)
sharding-column: create_time
t_order_item:
actual-data-nodes: ds.t_order_item_$->{2023..2023}_$->{3..6}
table-strategy:
standard:
sharding-column: order_create_time
sharding-algorithm-name: table-classbased
# 配置绑定表,每一行为一组
binding-tables:
- t_order,t_order_item
defaultDataSourceName: ds
sharding-algorithms:
table-classbased:
props:
strategy: standard
algorithmClassName: org.jeecg.modules.sharding.algorithm.StandardYMRangeShardAlgorithm
type: CLASS_BASED
# 分布式序列算法配置
key-generators:
snowflake:
type: SNOWFLAKE
props:
worker-id: 123
package org.jeecg.modules.sharding.algorithm;
import com.alibaba.fastjson.JSON;
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 org.jeecg.modules.sharding.utils.ShardingUtils;
import java.util.*;
/**
* @version 1.0
* @Author zhaozhiqiang
* @Date 2023/6/16 14:09
* @Description //TODO 标准年月分表算法
*/
@Slf4j
public class StandardYMRangeShardAlgorithm implements StandardShardingAlgorithm<Date> {
private Properties props = new Properties();
public static void main(String[] args) {
for (int i=0;i<2;i++) {
//根据值进行取模,得到一个目标值
int temp= i% 2;
System.out.println(temp);
}
String name="test_01";
System.out.println(name.indexOf("1"));
}
/**
* 用于处理=和IN的分片
*
* @param availableTargetNames 目标/物理表分片的集合(表名)
* @param preciseShardingValue 逻辑表相关信息 precise准确的
* @return
*/
@Override
public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Date> preciseShardingValue) {
log.info("精确集合:{},值:{}", JSON.toJSONString(availableTargetNames),JSON.toJSONString(preciseShardingValue));
Date date = preciseShardingValue.getValue();
String suffix = ShardingUtils.getSuffixByYearMonth(date);
for (String tableName : availableTargetNames) {
if (tableName.endsWith(suffix)) {
return tableName;
}
}
throw new IllegalArgumentException("未找到匹配的数据表");
}
/**
* 用于处理BETWEEN AND分片,如果不配置RangeShardingAlgorithm,SQL中的BETWEEN AND将按照全库路由处理
*
* @param availableTargetNames
* @param rangeShardingValue
* @return
*/
@Override
public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Date> rangeShardingValue) {
List<String> list = new ArrayList<>();
log.info("范围集合:{},值:{}", JSON.toJSONString(availableTargetNames),JSON.toJSONString(rangeShardingValue));
Range<Date> valueRange = rangeShardingValue.getValueRange();
Date lowerDate = valueRange.lowerEndpoint();
Date upperDate = valueRange.upperEndpoint();
String lowerSuffix = ShardingUtils.getSuffixByYearMonth(lowerDate);
String upperSuffix = ShardingUtils.getSuffixByYearMonth(upperDate);
//年月前缀计算
TreeSet<String> suffixList = ShardingUtils.getSuffixYMListForRange(lowerSuffix, upperSuffix);
for (String tableName : availableTargetNames) {
if (containTableName(suffixList, tableName)) {
list.add(tableName);
}
}
log.info("match tableNames-----------------------" + list.toString());
return list;
}
private boolean containTableName(Set<String> suffixList, String tableName) {
boolean flag = false;
for (String s : suffixList) {
if (tableName.endsWith(s)) {
flag = true;
break;
}
}
return flag;
}
/**
* 初始化对象的时候调用的方法
*/
@Override
public void init() {
}
/**
* 对应分片算法(sharding-algorithms)的类型
*
* @return
*/
@Override
public String getType() {
return "STANDARD_MOD";
}
@Override
public Properties getProps() {
return this.props;
}
/**
* 获取分片相关属性
*
* @param properties
*/
@Override
public void setProps(Properties properties) {
this.props = properties;
}
}
package org.jeecg.modules.sharding.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.modules.sharding.entity.Order;
import org.jeecg.modules.sharding.service.IOrderItemService;
import org.jeecg.modules.sharding.service.IOrderService;
import org.jeecg.modules.sharding.utils.DateUtils;
import org.jeecg.modules.sharding.vo.OrderAndItemVo;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
@Api(tags = "按照年范围分表")
@RestController
@RequestMapping("/sharding/y")
@Slf4j
public class ShardingYRangeController {
@Resource
private IOrderService orderService;
@Resource
private IOrderItemService orderItemService;
/**
* 制造测试数据
*
* @return
*/
@ApiOperation(value = "制造测试数据", httpMethod = "POST")
@PostMapping("/initData")
public Result<?> initData() {
orderService.initData();
return Result.OK();
}
@ApiOperation(value = "根据时间段查询列表数据", httpMethod = "GET")
@ApiImplicitParams({
@ApiImplicitParam(name = "bgtm", value = "开始时间", required = false, dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "edtm", value = "结束时间", required = false, dataType = "String", paramType = "query")
})
@GetMapping("/listAllByDate")
public Result<?> listAll(@RequestParam(value = "bgtm", required = false) String bgtm, @RequestParam(value = "edtm", required = false) String edtm) {
LambdaQueryWrapper<Order> objectLambdaQueryWrapper = new LambdaQueryWrapper<Order>();
if(StringUtils.isNotBlank(bgtm)&&StringUtils.isNotBlank(edtm)){
objectLambdaQueryWrapper.between(Order::getCreateTime, DateUtils.parseDate(bgtm), DateUtils.getEndTimeOfDay(edtm));
objectLambdaQueryWrapper.orderByDesc(Order::getCreateTime);
}
List<Order> list = orderService.list(objectLambdaQueryWrapper);
return Result.OK(list);
}
}
package org.jeecg.modules.sharding.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.jeecg.modules.sharding.entity.Order;
import org.jeecg.modules.sharding.vo.OrderAndItemVo;
import java.util.Date;
import java.util.List;
/**
* <p>
* Mapper 接口
* </p>
*
* @author XHD
* @since 2020-07-16
*/
public interface OrderMapper extends BaseMapper<Order> {
List<Order> listByRowNum(@Param("suffix") String suffix, @Param("size") Integer size, @Param("rowNum") Integer rowNum
, @Param("userName") String userName);
List<OrderAndItemVo> getItemByParentId(@Param("id")Long id, @Param("name")String name, @Param("createTime")String createTime);
List<Order> getUnBindLis(@Param("id")Long id,@Param("createTime")String createTime);
List<Order> unitMainAndShardingList(@Param("id")Long id,@Param("createTime")String createTime);
}
<?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="org.jeecg.modules.sharding.mapper.OrderMapper">
<select id="listByRowNum" resultType="org.jeecg.modules.sharding.entity.Order">
SELECT
A.*
FROM
(
SELECT
@rownum := @rownum + 1 AS rowNum,
t.id,t.name,t.create_time,t.user_name
FROM
t_order_${suffix} t,
( SELECT @rownum := 0 ) r
<trim prefix="WHERE" prefixOverrides="AND|OR">
<if test="userName != null and userName != ''">
AND t.user_name = #{userName}
</if>
</trim>
ORDER BY
create_time DESC,id
) A
WHERE
A.rowNum > #{rowNum}
LIMIT #{size}
</select>
<select id="getItemByParentId" resultType="org.jeecg.modules.sharding.vo.OrderAndItemVo">
SELECT
t1.id AS orderId,
t1.NAME AS orderName,
t1.user_name AS userName,
t2.name AS itemName
FROM
t_order t1
INNER JOIN t_order_item t2
ON t1.id = t2.order_id
where
1=1
<if test="id !=null and id!=''">
and t1.id=#{id}
</if>
<if test="createTime !=null and createTime!=''">
and DATE_FORMAT(t1.create_time, '%Y-%m-%d')=#{createTime}
</if>
<if test="name !=null and name !=''">
and t1.name like CONCAT('%',#{name},'%')
</if>
</select>
<select id="getUnBindLis" resultType="org.jeecg.modules.sharding.entity.Order">
SELECT
t1.*
FROM
t_order t1
INNER JOIN t_order_middle_test t2
ON t1.id = t2.order_id
WHERE 1 = 1
<if test="id !=null and id !=''">
and t2.id=#{id}
</if>
<if test="createTime !=null and createTime!=''">
and DATE_FORMAT(t1.create_time, '%Y-%m-%d')=#{createTime}
</if>
</select>
<select id="unitMainAndShardingList" resultType="org.jeecg.modules.sharding.entity.Order">
SELECT
t1.*
FROM
t_order t1
INNER JOIN t_order_middle_main_test t2
ON t1.id = t2.order_id
WHERE 1 = 1
<if test="id !=null and id !=''">
and t2.id=#{id}
</if>
<if test="createTime !=null and createTime!=''">
and DATE_FORMAT(t1.create_time, '%Y-%m-%d')=#{createTime}
</if>
</select>
</mapper>
package org.jeecg.modules.sharding.service.impl;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.modules.sharding.entity.Order;
import org.jeecg.modules.sharding.entity.OrderItem;
import org.jeecg.modules.sharding.mapper.OrderMapper;
import org.jeecg.modules.sharding.service.IOrderItemService;
import org.jeecg.modules.sharding.service.IOrderService;
import org.jeecg.modules.sharding.utils.DataUtils;
import org.jeecg.modules.sharding.utils.DateUtils;
import org.jeecg.modules.sharding.utils.ShardingUtils;
import org.jeecg.modules.sharding.vo.OrderAndItemVo;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;
/**
* <p>
* 服务实现类
* </p>
*
* @author XHD
* @since 2020-07-16
*/
@Service
@DS("sharding")
public class OrderServicImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {
@Resource
private IOrderItemService orderItemService;
@Override
public void initData() {
//主表50万 从表50*2=100万
int count = 5000;
//每次入库20条
int size = 1000;
List<Order> tempList = new ArrayList<>();
List<OrderItem> orderItemList = new ArrayList<>();
while (count > 0) {
addBatch(count, size, tempList, orderItemList);
count -= size;
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public Page<Order> listPage(String bgtm, String edtm, Integer current, Integer size, String userName) {
Page<Order> pageParam = new Page<>();
pageParam.setCurrent(current);
if (null != size) {
pageParam.setSize(size);
}
LambdaQueryWrapper<Order> ew = new LambdaQueryWrapper<>();
ew.between(Order::getCreateTime, DateUtils.parseDate(bgtm), DateUtils.getEndTimeOfDay(edtm));
ew.orderByDesc(Order::getCreateTime);
if (StringUtils.isNotBlank(userName)) {
ew.eq(Order::getUserName, userName);
}
Page<Order> page =this.page(pageParam,ew);
LambdaQueryWrapper<OrderItem> orderItemWrapper = new LambdaQueryWrapper<OrderItem>()
.between(OrderItem::getOrderCreateTime, DateUtils.parseDate(bgtm), DateUtils.getEndTimeOfDay(edtm));
this.appendOrderItem(page.getRecords(), orderItemWrapper);
return page;
}
@Override
public List<Order> listPageByTime(String lastCreateTime, Integer lastRowNum, Integer size, String userName) {
//lastCreateTime 有助于快速定位当前查询的分表 ,如果是第一页则可不传,默认使用当前时间
Date date = StringUtils.isBlank(lastCreateTime) ? new Date() : DateUtils.parseTime(lastCreateTime);
String suffix = ShardingUtils.getSuffixByYearMonth(date);
int resultSize = size == null ? 10 : size;
//rowNum用于获取当前页数据的起始位置,如果是第一页可以不传,默认为0
int rowNum = lastRowNum == null ? 0 : lastRowNum;
List<Order> orderList = baseMapper.listByRowNum(suffix, resultSize, rowNum, userName);
if (orderList.size() > 0) {
while (orderList.size() < resultSize) { //查询出的数据不足 找更早的分表补足
if ("2020_6".equals(suffix)) { //假设最早的分表为 t_order_2020_6
break;
}
suffix = ShardingUtils.getPrevSuffix(suffix);
List<Order> tempOrderList = baseMapper.listByRowNum(suffix, resultSize - orderList.size(), 0, userName);
if (tempOrderList.size() > 0) {
orderList.addAll(tempOrderList);
}
}
//获取orderList中数据的时间范围 查询子表数据
LambdaQueryWrapper<OrderItem> orderItemWrapper = new LambdaQueryWrapper<OrderItem>()
.between(OrderItem::getOrderCreateTime, orderList.get(orderList.size() - 1).getCreateTime(), orderList.get(0).getCreateTime());
this.appendOrderItem(orderList, orderItemWrapper);
}
return orderList;
}
@Override
public Order findById(Long id, String createTime) {
LambdaQueryWrapper<Order> ew = new LambdaQueryWrapper<>();
ew.eq(Order::getCreateTime,DateUtils.parseTime(createTime));
ew.eq(Order::getId,id);
Order one = this.getOne(ew);
return one;
}
@Override
public void removeById(Long id, String createTime) {
Order entity=new Order();
entity.setId(id);
entity.setCreateTime(DateUtils.parseTime(createTime));
this.removeById(entity);
}
@Override
public List<OrderAndItemVo> getItemByParentId(Long id, String name, String createTime) {
return baseMapper.getItemByParentId(id,name,createTime);
}
@Override
public List<Order> getUnBindLis(Long id,String createTime) {
return baseMapper.getUnBindLis(id,createTime);
}
@Override
public List<Order> unitMainAndShardingList(Long id, String createTime) {
return baseMapper.unitMainAndShardingList(id,createTime);
}
private void appendOrderItem(List<Order> orderList, LambdaQueryWrapper<OrderItem> ew) {
if (null != orderList && orderList.size() > 0) {
Set<Long> orderIds = orderList.stream().map(Order::getId).collect(Collectors.toSet());
ew.in(OrderItem::getOrderId, orderIds).orderByDesc(OrderItem::getOrderCreateTime);
List<OrderItem> orderItemList = orderItemService.list(ew);
Map<Long, List<OrderItem>> orderItemMap = DataUtils.listToMapForLong("orderId", orderItemList);
orderList.forEach(order -> order.setOrderItemList(orderItemMap.get(order.getId())));
}
}
private void addBatch(int ct, int size, List<Order> tempList, List<OrderItem> orderItemList) {
for (int i = 0; i < size; i++) {
long orderId = IdWorker.getId();
// Date orderCreateTime = randomCreateTime(new Random());
Date orderCreateTime = new Date();
tempList.add(new Order().setId(orderId).setName("订单-" + (ct - i)).setCreateTime(orderCreateTime)
.setUserName("guest-" + new Random().nextInt(1000)));
orderItemList.add(new OrderItem().setOrderId(orderId).setName("商品-" + (ct * 2 - i * 2)).setOrderCreateTime(orderCreateTime));
orderItemList.add(new OrderItem().setOrderId(orderId).setName("商品-" + (ct * 2 - i * 2 - 1)).setOrderCreateTime(orderCreateTime));
}
saveBatch(tempList);
orderItemService.saveBatch(orderItemList);
tempList.clear();
orderItemList.clear();
}
// private Date randomCreateTime(Random random) {
// Calendar calendar = Calendar.getInstance();
// calendar.set(Calendar.YEAR, 2023);
// calendar.set(Calendar.MONTH, 5);
// calendar.set(Calendar.DAY_OF_MONTH, random.nextInt(31) + 1);
// calendar.set(Calendar.HOUR_OF_DAY, random.nextInt(24));
// calendar.set(Calendar.MINUTE, random.nextInt(60));
// calendar.set(Calendar.SECOND, random.nextInt(60));
// return calendar.getTime();
// }
}
后续代码太多就不一一贴出来,下载地址
https://download.csdn.net/download/weixin_38501485/87917171