sharding jdbc 按日分表 自動建表實現


前言

  • 本項目分表方案是按照時間字段按日分表 其他分表方案也可參考本文檔實現自動建表
  • 需要提前准備待分表的主表寫入數據庫
  • 優勢:
    可以實現自動建表 且不需要配置 SQL
    范圍分表查詢時自動排除不存在的表

配置

主要依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

<!-- huTool -->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.7.7</version>
</dependency>

<!-- sharding jdbc-->
<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
    <version>4.0.1</version>
</dependency>

分表工具類 ShardingAlgorithmTool

import com.**.**.dao.CommonMapper;
import com.**.**.domain.db.CreateTableSql;
import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingAlgorithm;

import java.util.HashSet;
import java.util.List;

/**
 * 分表工具
 */
@Slf4j
public abstract class ShardingAlgorithmTool<T extends Comparable<?>> implements PreciseShardingAlgorithm<T>, RangeShardingAlgorithm<T> {

    private static CommonMapper commonMapper;

    private static final HashSet<String> tableNameCache = new HashSet<>();

    /**
     * 手動注入
     */
    public static void setCommonMapper(CommonMapper commonMapper) {
        ShardingAlgorithmTool.commonMapper = commonMapper;
    }

    /**
     * 判斷 分表獲取的表名是否存在 不存在則自動建表
     *
     * @param logicTableName  邏輯表名(表頭)
     * @param resultTableName 真實表名
     * @return 確認存在於數據庫中的真實表名
     */
    public String shardingTablesCheckAndCreatAndReturn(String logicTableName, String resultTableName) {

        synchronized (logicTableName.intern()) {
            // 緩存中有此表 返回
            if (shardingTablesExistsCheck(resultTableName)) {
                return resultTableName;
            }

            // 緩存中無此表 建表 並添加緩存
            CreateTableSql createTableSql = commonMapper.selectTableCreateSql(logicTableName);
            String sql = createTableSql.getCreateTable();
            sql = sql.replace("CREATE TABLE", "CREATE TABLE IF NOT EXISTS");
            sql = sql.replace(logicTableName, resultTableName);
            commonMapper.executeSql(sql);
            tableNameCache.add(resultTableName);
        }

        return resultTableName;
    }

    /**
     * 判斷表是否存在於緩存中
     *
     * @param resultTableName 表名
     * @return 是否存在於緩存中
     */
    public boolean shardingTablesExistsCheck(String resultTableName) {
        return tableNameCache.contains(resultTableName);
    }

    /**
     * 緩存重載方法
     *
     * @param schemaName 待加載表名所屬數據庫名
     */
    public static void tableNameCacheReload(String schemaName) {
        // 讀取數據庫中所有表名
        List<String> tableNameList = commonMapper.getAllTableNameBySchema(schemaName);
        // 刪除舊的緩存(如果存在)
        ShardingAlgorithmTool.tableNameCache.clear();
        // 寫入新的緩存
        ShardingAlgorithmTool.tableNameCache.addAll(tableNameList);
    }

}

分表實現類 DateShardingAlgorithm

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.google.common.collect.Range;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingValue;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;

/**
 * 日期分表策略
 */
public class DateShardingAlgorithm extends ShardingAlgorithmTool<Date> {

    /**
     * 獲取 指定分表
     */
    @Override
    public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Date> preciseShardingValue) {
        return shardingTablesCheckAndCreatAndReturn(preciseShardingValue.getLogicTableName(), preciseShardingValue.getLogicTableName() + DateUtil.format(preciseShardingValue.getValue(), "_yyyy_MM_dd"));
    }

    /**
     * 獲取 范圍分表
     */
    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Date> rangeShardingValue) {
        Range<Date> valueRange = rangeShardingValue.getValueRange();
        Date lowerDate = valueRange.lowerEndpoint();
        Date upperDate = valueRange.upperEndpoint();
        List<String> tableNameList = new ArrayList<>();
        for (DateTime dateTime : DateUtil.rangeToList(DateUtil.beginOfDay(lowerDate), DateUtil.endOfDay(upperDate), DateField.DAY_OF_YEAR)) {
            String resultTableName = rangeShardingValue.getLogicTableName() + DateUtil.format(dateTime, "_yyyy_MM_dd");
            if (shardingTablesExistsCheck(resultTableName)) {
                tableNameList.add(resultTableName);
            }
        }
        return tableNameList;
    }
}

項目啟動時將表載入緩存/注入工具類屬性 ShardingTablesLoadRunner

import com.**.**.config.sharding.ShardingAlgorithmTool;
import com.**.**.dao.CommonMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * 項目啟動后 讀取已有分表 進行緩存
 */
@Slf4j
@Order(value = 1) // 數字越小 越先執行
@Component
public class ShardingTablesLoadRunner implements CommandLineRunner {

    @Value("${db.schema-name}")
    private String schemaName;

    @Resource
    private CommonMapper commonMapper;

    @Override
    public void run(String... args) throws Exception {

        // 給 分表工具類注入屬性
        ShardingAlgorithmTool.setCommonMapper(commonMapper);
        // 調用緩存重載方法
        ShardingAlgorithmTool.tableNameCacheReload(schemaName);

        log.info("ShardingTablesLoadRunner start OK");
    }
}

Mybatis SQL 映射 CommonMapper

import com.**.**.domain.db.CreateTableSql;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * 常用工具 mapper
 */
public interface CommonMapper {

    /**
     * 查詢數據庫中的所有表名
     *
     * @param schema 數據庫名
     * @return 表名列表
     */
    List<String> getAllTableNameBySchema(@Param("schema") String schema);

    /**
     * 查詢建表語句
     *
     * @param tableName 表名
     * @return 建表語句
     */
    CreateTableSql selectTableCreateSql(@Param("tableName") String tableName);

    /**
     * 執行SQL
     *
     * @param sql 待執行SQL
     */
    void executeSql(@Param("sql") String sql);

}

Mybatis SQL 映射 XML CommonMapper

<?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.**.**.dao.CommonMapper">

    <resultMap id="selectTableCreateSqlResultMap" type="com.**.**.domain.db.CreateTableSql">
        <result column="Table" property="table"/>
        <result column="Create Table" property="createTable"/>
    </resultMap>

    <select id="getAllTableNameBySchema" resultType="java.lang.String">
        SELECT TABLES.TABLE_NAME
        FROM information_schema.TABLES
        WHERE TABLES.TABLE_SCHEMA = #{schema}
    </select>

    <select id="selectTableCreateSql" resultMap="selectTableCreateSqlResultMap">
        SHOW CREATE TABLE ${tableName}
    </select>

    <update id="executeSql">
        ${sql}
    </update>

</mapper>

Mybatis SQL 映射實體 CreateTableSql

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 建表語句查詢結果
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CreateTableSql {

    private String table;

    private String createTable;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM