sharding-jdbc和mybatis-plus的数据分片路由
maven文件
<?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>2.3.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>springboot-shardingsphere</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-shardingsphere</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
<version>8.0.13</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- for spring boot -->
<dependency>
<groupId>io.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>3.1.0</version>
</dependency>
<!-- for spring namespace -->
<dependency>
<groupId>io.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-namespace</artifactId>
<version>3.1.0</version>
</dependency>
<!-- 加载jdbc连接数据库 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.baomidou</groupId>-->
<!-- <artifactId>mybatis-plus-boot-starter</artifactId>-->
<!-- <version>2.2.0</version>-->
<!-- </dependency>-->
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<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>
application.properties文件
server.port=8089
mybatis-plus.mapper-locations=classpath*:mapper
启动类
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
@MapperScan("com.example.springbootshardingsphere.**.mapper")
public class SpringbootShardingsphereApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootShardingsphereApplication.class, args);
}
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
复合分片配置
package com.example.springbootshardingsphere.system.config;
import com.alibaba.fastjson.JSONObject;
import com.example.springbootshardingsphere.system.utils.SnowFlakeUtil;
import com.google.common.collect.Range;
import io.shardingsphere.api.algorithm.sharding.ListShardingValue;
import io.shardingsphere.api.algorithm.sharding.PreciseShardingValue;
import io.shardingsphere.api.algorithm.sharding.RangeShardingValue;
import io.shardingsphere.api.algorithm.sharding.ShardingValue;
import io.shardingsphere.api.algorithm.sharding.complex.ComplexKeysShardingAlgorithm;
import lombok.extern.slf4j.Slf4j;
import java.text.SimpleDateFormat;
import java.util.*;
@Slf4j
public class CustomComplexKeysShardingAlgorithm implements ComplexKeysShardingAlgorithm{
@Override
public Collection<String> doSharding(Collection<String> availableTargetNames, Collection<ShardingValue> shardingValues) {
Collection<String> routTables = new HashSet<>();
Collection id=getColumnValue(shardingValues,"id");
Collection shardTime=getColumnValue(shardingValues, "shardtime");
if(id!=null){
Object[] objects=id.toArray();
if(objects.length>0){
Object val=objects[0];
SimpleDateFormat dft=new SimpleDateFormat("yyyyMM");
Date date=SnowFlakeUtil.getTimeBySnowFlakeId(Long.parseLong(String.valueOf(val)));
routTables.add("log_"+dft.format(date));
log.info("走的是id路由");
return routTables;
}
}
if(shardTime != null){
Object[] objects=shardTime.toArray();
if (objects.length > 0) {
Object val=objects[0];
routTables.add("log_"+val);
log.info("走的是时间分片路由");
return routTables;
}
}
return routTables;
}
private Collection getColumnValue(Collection<ShardingValue> shardingValues,String key){
if(shardingValues == null){
return null;
}
for (ShardingValue shardingValue : shardingValues) {
if(shardingValue instanceof ListShardingValue){
ListShardingValue listShardingValue=(ListShardingValue) shardingValue;
if(listShardingValue.getColumnName().equals(key)){
return listShardingValue.getValues();
}
}
}
return null;
}
}
单个分片配置
package com.example.springbootshardingsphere.system.config;
import com.example.springbootshardingsphere.system.utils.SnowFlakeUtil;
import io.shardingsphere.api.algorithm.sharding.PreciseShardingValue;
import io.shardingsphere.api.algorithm.sharding.standard.PreciseShardingAlgorithm;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
public class SnowFlakeLogShard implements PreciseShardingAlgorithm<Long> {
@Override
public String doSharding(Collection<String> collection, PreciseShardingValue<Long> preciseShardingValue) {
Long id = (Long) preciseShardingValue.getValue();
Date date=SnowFlakeUtil.getTimeBySnowFlakeId(id);
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM");
String tableName="log_"+sdf.format(date);
return tableName;
}
}
sql脚本
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for log_202301
-- ----------------------------
DROP TABLE IF EXISTS `log_202301`;
CREATE TABLE `log_202301` (
`id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`note` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`shardtime` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`createdate` datetime(0) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
ID雪花算法工具类
package com.example.springbootshardingsphere.system.utils;
import java.text.SimpleDateFormat;
import java.util.Date;
public class SnowFlakeUtil {
private static SnowFlakeUtil snowFlakeUtil;
static {
snowFlakeUtil = new SnowFlakeUtil();
}
private static final long INIT_EPOCH = 1650789964886L;
private static final long TIME_BIT = 0b1111111111111111111111111111111111111111110000000000000000000000L;
private long lastTimeMillis = -1L;
private static final long DATA_CENTER_ID_BITS = 5L;
private static final long MAX_DATA_CENTER_ID = ~(-1L << DATA_CENTER_ID_BITS);
private long dataCenterId;
private static final long WORKER_ID_BITS = 5L;
private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
private long workerId;
private static final long SEQUENCE_BITS = 12L;
private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS);
private long sequence;
private static final long WORK_ID_SHIFT = SEQUENCE_BITS;
private static final long DATA_CENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATA_CENTER_ID_BITS;
public SnowFlakeUtil() {
this(1, 1);
}
public SnowFlakeUtil(long dataCenterId, long workerId) {
if (dataCenterId < 0 || dataCenterId > MAX_DATA_CENTER_ID) {
throw new IllegalArgumentException(
String.format("dataCenterId 值必须大于 0 并且小于 %d", MAX_DATA_CENTER_ID));
}
if (workerId < 0 || workerId > MAX_WORKER_ID) {
throw new IllegalArgumentException(String.format("workId 值必须大于 0 并且小于 %d", MAX_WORKER_ID));
}
this.workerId = workerId;
this.dataCenterId = dataCenterId;
}
public static Long getSnowFlakeId() {
long currentTimeMillis = System.currentTimeMillis();
return snowFlakeUtil.nextId(currentTimeMillis);
}
public synchronized long nextId(){
long currentTimeMillis = System.currentTimeMillis();
return nextId(currentTimeMillis);
}
public synchronized long nextId(Date date){
if(date == null){
long currentTimeMillis = System.currentTimeMillis();
return nextId(currentTimeMillis);
}else {
long timeMills=date.getTime();
return nextId(timeMills);
}
}
public synchronized long nextId(Long currentTimeMillis) {
if (currentTimeMillis < lastTimeMillis) {
throw new RuntimeException(
String.format("可能出现服务器时钟回拨问题,请检查服务器时间。当前服务器时间戳:%d,上一次使用时间戳:%d", currentTimeMillis,
lastTimeMillis));
}
if (currentTimeMillis == lastTimeMillis) {
sequence = (sequence + 1) & SEQUENCE_MASK;
if (sequence == 0) {
currentTimeMillis = getNextMillis(lastTimeMillis);
}
} else {
sequence = 0;
}
lastTimeMillis = currentTimeMillis;
return
((currentTimeMillis - INIT_EPOCH) << TIMESTAMP_SHIFT)
| (dataCenterId << DATA_CENTER_ID_SHIFT)
| (workerId << WORK_ID_SHIFT)
| sequence;
}
private long getNextMillis(long lastTimeMillis) {
long currentTimeMillis = System.currentTimeMillis();
while (currentTimeMillis <= lastTimeMillis) {
currentTimeMillis = System.currentTimeMillis();
}
return currentTimeMillis;
}
public static String getRandomStr() {
return Long.toString(getSnowFlakeId(), Character.MAX_RADIX);
}
public static Date getTimeBySnowFlakeId(long id) {
return new Date(((TIME_BIT & id) >> 22) + INIT_EPOCH);
}
}
Log对象
package com.example.springbootshardingsphere.book.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
@TableName(value ="log")
@Data
public class Log implements Serializable {
@TableId
private String id;
private String name;
private String note;
private String shardtime;
private Date createdate;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getClass().getSimpleName());
sb.append(" [");
sb.append("Hash = ").append(hashCode());
sb.append(", id=").append(id);
sb.append(", name=").append(name);
sb.append(",shardtime=").append(shardtime);
sb.append(", note=").append(note);
sb.append(", createdate=").append(createdate);
sb.append(", serialVersionUID=").append(serialVersionUID);
sb.append("]");
return sb.toString();
}
}
分片路由测试
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.example.springbootshardingsphere.book.domain.Log;
import com.example.springbootshardingsphere.book.service.BookService;
import com.example.springbootshardingsphere.book.service.LogService;
import com.example.springbootshardingsphere.system.utils.SnowFlakeUtil;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
@SpringBootTest
class SpringbootShardingsphereApplicationTests {
@Autowired
private BookService book0Service;
@Autowired
private LogService logService;
@Test
void test() throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time="2023-03-17 22:22:22";
Date date=sdf.parse(time);
SimpleDateFormat sdfMM = new SimpleDateFormat("yyyyMM");
SnowFlakeUtil snowFlakeUtil = new SnowFlakeUtil();
Log log=new Log();
log.setId(String.valueOf(snowFlakeUtil.nextId(date)));
log.setName("这是文案");
log.setNote("这是文案");
log.setShardtime(sdfMM.format(SnowFlakeUtil.getTimeBySnowFlakeId(Long.valueOf(log.getId()))));
log.setCreatedate(SnowFlakeUtil.getTimeBySnowFlakeId(Long.valueOf(log.getId())));
logService.save(log);
}
@Test
void get(){
String id="108076212898893824";
Log log=logService.getById(id);
System.out.println(log.toString());
}
@Test
void page() throws ParseException {
String time="2023-03-15 22:22:22";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
SimpleDateFormat sdfMM = new SimpleDateFormat("yyyyMM");
LambdaQueryWrapper<Log> lambda=new LambdaQueryWrapper<>();
lambda.eq(Log::getShardtime,sdfMM.format(sdf.parse(time)));
IPage<Log> iPage=logService.page(new Page<>(1,10),lambda);
List<Log> logList=iPage.getRecords();
for (Log log : logList) {
System.out.println(log.toString()+" ");
}
}
}