ShardingSphere之分库&分表

笔者日常看朋友发了一篇关于ShardingSphere的文章,我对ShardingSphere挺感兴趣的,于是就学了下。


目录

辅助理解

【分库分表示例】之需求与库表说明

          需求说明

          需求相关表

【分库分表示例】之代码实现

          准备工作

          Sharding分片配置

测试一下

          测试准备

          编写测试方法

          进行测试


ShardingSphere功能强大,本文学习使用Sharding-JDBC数据分片功能下的分库分表功能。

辅助理解

ShardingSphere之分库&分表_第1张图片

注:由于本文知识需要,所以上图做了一个简单说明。

注:本文会涉及到的一些ShardingSphere基础概念,建议先去https://shardingsphere.apa.../sharding/了解一下。


【分库分表示例】之需求与库表说明

需求说明

       将职工按照不同的年龄段进行分库,按照不同的性别进行分表。具体需求为:age在[0, 50)的,分到younger库;age在[50, 100)的,分到older库;age>=100的,分到other库。其中younger库和older库又按照性别进行分表,男的录入staff_man表,女的录入staff_woman表,other库不区分男女,不论男女都直接录入staff表。

注:本文以实现此需求进行分库分表示例

需求相关库表

ShardingSphere之分库&分表_第2张图片

上图中的相关表,建表语句为:

CREATE TABLE `staff_man` (
  `id` varchar(40) NOT NULL,
  `age` int(3) DEFAULT NULL,
  `name` varchar(10) DEFAULT NULL,
  `gender` char(1) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `staff_woman` (
  `id` varchar(40) NOT NULL,
  `age` int(3) DEFAULT NULL,
  `name` varchar(10) DEFAULT NULL,
  `gender` char(1) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `staff` (
  `id` varchar(40) NOT NULL,
  `age` int(3) DEFAULT NULL,
  `name` varchar(10) DEFAULT NULL,
  `gender` char(1) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

注:这里先按照需求进行建库建表。

注:建库建表可以使用程序动态创建,不过考虑到本节知识主要讲述ShardingSphere分库分表的实现,就不再引
       入其它知识来干扰主题了,所以这里手动创建库表。


【分库分表示例】之代码实现

软硬件环境说明:Windows10、IntelliJ IDEA、SpringBoot 2.1.4.RELEASE、MySQL、MyBatis。

准备工作

在pom.xml中引入ShardingSphere分库分表依赖

ShardingSphere之分库&分表_第3张图片

这里直接给出本人完整的pom.xml:



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.1.4.RELEASE
         
    
    com.aspire
    sharding-database-table
    0.0.1-SNAPSHOT
    sharding-database-table
    分库分表示例

    
        1.8
    

    
        
            org.springframework.boot
            spring-boot-starter
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
        

        
        
            org.projectlombok
            lombok
            true
        

        
        
            org.mybatis.spring.boot
            mybatis-spring-boot-starter
            2.0.0
        


        
        
            mysql
            mysql-connector-java
            8.0.15
        

        
        
            com.alibaba
            druid
            1.1.10
        

        
        
            io.shardingjdbc
            sharding-jdbc-core
            2.0.3
        

    
    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    

Sharding分片配置

提示Sharding分片配置有四种方式

  • Java配置

  • Yaml配置

  • SpringBoot配置

  • Spring命名空间配置

注:可详见https://shardingsphere.apache.or...onfiguration/

注:本人这里采用Java配置,是因为Java配置相对于其余三种方式来说,更容易让人理解,但是代价是与代
       码耦合度较高。如果比较熟了,推荐使用Yaml配置、SpringBoot配置。

本人Sharding分片配置的相关类说明

ShardingSphere之分库&分表_第4张图片

  • ShardingConfig:核心sharding分片配置类。该类中的主要配置步骤是:
    第一步: 获取众多数据源。
    第二步: 获取总配置类。
    第三步: 获取其余配置信息(如果需要的话)。
    第四步:定制指定逻辑表的切片(分库分表)策略。
    第五步:将定制了自己的切片策略的表的配置规则,加入总配置中。
    第六步:创建并返回sharding总数据源,注入容器。

  • DatabaseShardingAlgorithm:本人用于数据源分片的算法实现,为ShardingConfig服务。

  • StaffTableComplexKeysShardingAlgorithm:本人用于逻辑表分片的算法实现,为ShardingConfig服务。

  • StaffTableTableShardingAlgorithm:本人用于逻辑表分片的算法实现,为ShardingConfig服务。

注:StaffTableComplexKeysShardingAlgorithm和StaffTableTableShardingAlgorithm都是用于对同一个逻辑表
       分片的算法实现,在这次的示例项目汇总,其实一个就够了,不过本人这里将两个常用的都进行了实现,
       分别实现的是复合分片算法、精准分片算法。

本人Sharding分片配置的相关类的具体代码

ShardingConfig:

import com.alibaba.druid.pool.DruidDataSource;
import com.aspire.shardingdatabasetable.config.algorithm.DatabaseShardingAlgorithm;
import com.aspire.shardingdatabasetable.config.algorithm.StaffTableComplexKeysShardingAlgorithm;
import com.aspire.shardingdatabasetable.config.algorithm.StaffTableTableShardingAlgorithm;
import io.shardingjdbc.core.api.ShardingDataSourceFactory;
import io.shardingjdbc.core.api.config.ShardingRuleConfiguration;
import io.shardingjdbc.core.api.config.TableRuleConfiguration;
import io.shardingjdbc.core.api.config.strategy.ComplexShardingStrategyConfiguration;
import io.shardingjdbc.core.api.config.strategy.StandardShardingStrategyConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 分库分表配置
 *
 * @author JustryDeng
 * @date 2019/5/30 9:54
 */
@Configuration
public class ShardingConfig {

    /**
     * 切片配置
     *
     * @return  数据源
     * @date 2019/5/30 21:04
     */
    @Bean
    public DataSource shardingCustomer() throws SQLException {

        // 第一步: 获取众多数据源
        Map dataSourceMap = getDatasourceMap();

        // 第二步: 获取总配置类
        ShardingRuleConfiguration shardingRuleConfig = getShardingRuleConfiguration();

        // 第三步: 获取其余配置信息(如果需要的话)
        Properties properties = getProperties();

        // 第四步: 定制指定逻辑表的切片(分库分表)策略
        List allTableRuleConfiguration = getAllTableRuleConfiguration();

        // 第五步: 将定制了自己的切片策略的表的配置规则,加入总配置中
        shardingRuleConfig.getTableRuleConfigs().addAll(allTableRuleConfiguration);

        // 第六步: 创建并返回sharding总数据源,注入容器
        return ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfig,
                new ConcurrentHashMap<>(16), properties);
    }

    /**
     * 对指定逻辑表进行切片(分库分表)个性化配置
     * 注:本人这里只配置了
     *
     * @return 定制的 指定表的分库分表配置
     * @date 2019/6/4 12:01
     */
    private List getAllTableRuleConfiguration() {
        List list = new ArrayList<>(8);
        // 配置staff表切片规则
        TableRuleConfiguration staffTableRuleConfig = new TableRuleConfiguration();
        // 逻辑表名
        staffTableRuleConfig.setLogicTable("staff");
        /*
         * 真实库表名
         *
         * 注:库与表之间使用【.】分割;
         * 注:库表与库表之间使用【,】分割
         * 下述inline表达式的结果即为 otherDb.staff, youngerDb.staff_man, youngerDb.staff_woman, olderDb.staff_man, olderDb.staff_woman
         */
        staffTableRuleConfig.setActualDataNodes("otherDb.staff, ${['youngerDb', 'olderDb']}.staff_${['man', 'woman']}");
        // 设置这张表的 分库策略(本人这里采用:标准分片策略)
        staffTableRuleConfig.setDatabaseShardingStrategyConfig(
                new StandardShardingStrategyConfiguration("age", DatabaseShardingAlgorithm.class.getName()));
        // 设置这张表的 分表策略(本人这里采用:精确分片算法实现的标准分片策略)
        StandardShardingStrategyConfiguration standardShardingStrategyConfiguration = new StandardShardingStrategyConfiguration(
                "gender", StaffTableTableShardingAlgorithm.class.getName(), null);
        staffTableRuleConfig.setTableShardingStrategyConfig(standardShardingStrategyConfiguration);
        list.add(staffTableRuleConfig);
        return list;
    }

    /**
     * 获取其余配置信息
     *
     * @return 其余配置信息
     * @date 2019/6/4 11:24
     */
    private Properties getProperties() {
        Properties properties = new Properties();
        // 打印出分库路由后的sql
        properties.setProperty("sql.show", "true");
        return properties;
    }
    /**
     * 获取切片总配置类
     *
     * @return 总配置类
     * @date 2019/6/4 11:24
     */
    private ShardingRuleConfiguration getShardingRuleConfiguration() {

        // sharding总配置类
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();

        // 设置全局默认库
        shardingRuleConfig.setDefaultDataSourceName("otherDb");

        // 设置全局默认分库的策略(本人这里采用:精确分片算法实现的标准分片策略)
        // 如果某个表,没有指定分库策略的话,那么会默认使用这个策略;如果某个表制定了自己的策略,那么就会走自己的策略不走这个默认策略
        shardingRuleConfig.setDefaultDatabaseShardingStrategyConfig(
                new StandardShardingStrategyConfiguration("age", DatabaseShardingAlgorithm.class.getName()));

        // 设置全局默认分表的策略(本人这里采用:复合分片策略)
        // 如果某个表,没有指定分表策略的话,那么会默认使用这个策略;如果某个表制定了自己的策略,那么就会走自己的策略不走这个默认策略
        ComplexShardingStrategyConfiguration complexShardingStrategyConfiguration = new ComplexShardingStrategyConfiguration(
                "age, gender", StaffTableComplexKeysShardingAlgorithm.class.getName());
        shardingRuleConfig.setDefaultTableShardingStrategyConfig(complexShardingStrategyConfiguration);
        return shardingRuleConfig;
    }

    /**
     * 获取数据源Map
     *
     * @return  获取数据源Map
     * @date 2019/6/4 10:06
     */
    private Map getDatasourceMap() {
        // 真实数据源map
        Map dataSourceMap = new HashMap<>(4);

        // 配置第一个数据源,对应库other
        DruidDataSource dataSourceDefault = new DruidDataSource();
        dataSourceDefault.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSourceDefault.setUrl("jdbc:mysql://localhost:3306/other?characterEncoding=utf8&serverTimezone=GMT%2B8");
        dataSourceDefault.setUsername("root");
        dataSourceDefault.setPassword("dengshuai");
        dataSourceMap.put("otherDb", dataSourceDefault);

        // 配置第二个数据源,对应库younger
        DruidDataSource dataSourceYounger = new DruidDataSource();
        dataSourceYounger.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSourceYounger.setUrl("jdbc:mysql://localhost:3306/younger?characterEncoding=utf8&serverTimezone=GMT%2B8");
        dataSourceYounger.setUsername("root");
        dataSourceYounger.setPassword("dengshuai");
        dataSourceMap.put("youngerDb", dataSourceYounger);
        
        // 配置第三个数据源,对应库older
        DruidDataSource dataSourceOlder = new DruidDataSource();
        dataSourceOlder.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSourceOlder.setUrl("jdbc:mysql://localhost:3306/older?characterEncoding=utf8&serverTimezone=GMT%2B8");
        dataSourceOlder.setUsername("root");
        dataSourceOlder.setPassword("dengshuai");
        dataSourceMap.put("olderDb", dataSourceOlder);
        return dataSourceMap;
    }

}

DatabaseShardingAlgorithm:

import io.shardingjdbc.core.api.algorithm.sharding.PreciseShardingValue;
import io.shardingjdbc.core.api.algorithm.sharding.standard.PreciseShardingAlgorithm;

import java.util.Collection;

/**
 * 数据源分片策略
 *
 * 注:本人这里采用的是【精确分片算法PreciseShardingAlgorithm】,类似的算法还有
 *    【范围分片算法】
 *    【复合分片算法】
 *    【Hint分片算法】
 *
 * @author JustryDeng
 * @date 2019/5/31 10:11
 */
public class DatabaseShardingAlgorithm implements PreciseShardingAlgorithm {

    /**
     * Sharding.
     *
     * @param availableTargetNames
     *            数据源(库)名称集合 或 真实表名称集合。
     *
     *            提示:数据库源一旦定下来了,那么对应有哪些可用的真实表也就随之定下来了。
     *
     *            注:这里为: 数据源(库)名称集合。
     *               如,本人此次示例中,设置了的数据源有otherDb、youngerDb、olderDb,
     *               那么availableTargetNames输出就应为[otherDb, youngerDb, olderDb]
     *
     *            注:因为是先路由定位库,再路由定位真实表;所以一旦库定下来了,那么真实表的集合就定下来了。
     *               以本人的此项目的库与表进行说明:
     *               otherDb库只有表staff.
     *               youngerDb库有表staff_man。staff_woman.
     *               olderDb库有表staff_man。staff_woman.
     *               在进行表分片之前会先进性数据库(数据源)分片,在数据源分片时,就路由定下来了走
     *               otherDb的话,那么这里定位真实表时,候选的真实表集合里只有staff;如果
     *               在数据源分片时,路由定下来了走youngerDb的话,那么这里定位真实表时,候选的
     *               真实表集合里只有staff_man和staff_woman;
     *
     * @param shardingValue
     *            分片键的值。
     *            如:本人此次示例中,要路由到那个数据库源,是由age为数据源分片键的,age的列类型为int,
     *               所以这里的泛型是Integer。
     *               如果分片键的类型是bigint的话,这里的泛型就应该是Long.
     *               如果分片键的类型是varchar/char的话,这里的泛型就应该是String.
     *
     * @return 路由后的SQL要使用的数据源(库)的名字   或   路由后的SQL要使用的真实表的名字
     *         注:这里为: 路由后的SQL要使用的数据源(库)的名字。
     */
    @Override
    public String doSharding(Collection availableTargetNames,
                             PreciseShardingValue shardingValue) {
        double value = shardingValue.getValue() * 1.0 / 50;
        // 年龄在[0, 50)之间的,入youngerDb库
        if (value < 1) {
            return "youngerDb";
        // 年龄在[50, 100)之间的,入olderDb库
        } else if (value < 2) {
            return "olderDb";
        // 年龄在>=100的,入otherDb库
        } else {
            return "otherDb";
        }

        /// 注:如果数据源的名称有规律的话(P.S.该名称是我们自己起的,当然可以起得很有规律),
        ///    也可以动态将 分片建的值 与 对应 数据源名称关联起来,如
        // for (String each : availableTargetNames) {
        //
        //     if (each.endsWith(shardingValue.getValue() % 2 + "")) {
        //         return each;
        //     }
        // }
        // throw new UnsupportedOperationException();
    }
}

StaffTableComplexKeysShardingAlgorithm:

import io.shardingjdbc.core.api.algorithm.sharding.ListShardingValue;
import io.shardingjdbc.core.api.algorithm.sharding.ShardingValue;
import io.shardingjdbc.core.api.algorithm.sharding.complex.ComplexKeysShardingAlgorithm;

import java.util.*;

/**
 * 自定义符合分片策略
 *
 * 用于处理使用多键作为分片键进行分片的场景,包含多个分片键的逻辑较复杂,需要应用开发者自行
 * 处理其中的复杂度。需要配合ComplexShardingStrategy使用。
 *
 * @author JustryDeng
 * @date 2019/5/31 10:11
 */
public class StaffTableComplexKeysShardingAlgorithm implements ComplexKeysShardingAlgorithm {

    /**
     * Sharding.
     *
     * @param availableTargetNames
     *            可用的数据源(库)名称集合 或 可用的真实表名称集合。
     *
     *            提示:数据库源一旦定下来了,那么对应有哪些可用的真实表也就随之定下来了。
     *
     *            注:这里为: 数据源(库)名称集合。
     *               如,本人此次示例中,设置了的数据源有otherDb、youngerDb、olderDb,
     *               那么availableTargetNames输出就应为[otherDb, youngerDb, olderDb]
     *
     *            注:因为是先路由定位库,再路由定位真实表;所以一旦库定下来了,那么真实表的集合就定下来了。
     *               以本人的此项目的库与表进行说明:
     *               otherDb库只有表staff.
     *               youngerDb库有表staff_man。staff_woman.
     *               olderDb库有表staff_man。staff_woman.
     *               在进行表分片之前会先进性数据库(数据源)分片,在数据源分片时,就路由定下来了走
     *               otherDb的话,那么这里定位真实表时,候选的真实表集合里只有staff;如果
     *               在数据源分片时,路由定下来了走youngerDb的话,那么这里定位真实表时,候选的
     *               真实表集合里只有staff_man和staff_woman;
     *
     * @param shardingValues
     *            分片键的值。
     *            如:本人此次示例中,要路由到那个数据库源,是由age为数据源分片键的,age的列类型为int,
     *               所以这里的泛型是Integer。
     *               如果分片键的类型是bigint的话,这里的泛型就应该是Long.
     *               如果分片键的类型是varchar/char的话,这里的泛型就应该是String.
     *            注: ComplexKeysShardingAlgorithm算法,ShardingValue接口的实现是ListShardingValue。
     *                本人这里,shardingValues的toString形如
     *            shardingValues:[
     *                            ListShardingValue(logicTableName=staff, columnName=age, values=[21]),
     *                            ListShardingValue(logicTableName=staff, columnName=gender, values=[女])
     *                            ]
     *
     * @return 路由后的SQL要使用的数据源(库)名字的集合   或   路由后的SQL要使用的真实表名字的集合
     */
    @Override
    public Collection doSharding(Collection availableTargetNames,
                                         Collection shardingValues) {
        List list = new ArrayList<>(4);
        ListShardingValue listShardingValue;
        for (ShardingValue item : shardingValues) {
            // 将item拆箱转换为listShardingValue
            listShardingValue = (ListShardingValue)item;
            System.out.println("逻辑表:" + listShardingValue.getLogicTableName());
            System.out.println("列名:" + listShardingValue.getColumnName());
            System.out.println("改列的值:" + listShardingValue.getValues());
        }

        return list;
    }
}

StaffTableTableShardingAlgorithm:

import io.shardingjdbc.core.api.algorithm.sharding.PreciseShardingValue;
import io.shardingjdbc.core.api.algorithm.sharding.standard.PreciseShardingAlgorithm;

import java.util.Collection;

/**
 * 表分片策略
 *
 * 提示:PreciseShardingAlgorithm接口的泛型,视对应分片键的数据类型而定
 *     如:列是字符串,那么这里是 String
 *        列是int,那么这里是 Integer
 *        列是bigint,那么这里是 Long
 *
 * @author JustryDeng
 * @date 2019/5/31 10:11
 */
public class StaffTableTableShardingAlgorithm implements PreciseShardingAlgorithm {

    /**
     * Sharding.
     *
     * @param availableTargetNames
     *            数据源(库)名称集合 或 真实表名称集合。
     *
     *            提示:数据库源一旦定下来了,那么对应有哪些可用的真实表也就随之定下来了。
     *
     *            注:这里为: 数据源(库)名称集合。
     *               如,本人此次示例中,设置了的数据源有otherDb、youngerDb、olderDb,
     *               那么availableTargetNames输出就应为[otherDb, youngerDb, olderDb]
     *
     *            注:因为是先路由定位库,再路由定位真实表;所以一旦库定下来了,那么真实表的集合就定下来了。
     *               以本人的此项目的库与表进行说明:
     *               otherDb库只有表staff.
     *               youngerDb库有表staff_man。staff_woman.
     *               olderDb库有表staff_man。staff_woman.
     *               在进行表分片之前会先进性数据库(数据源)分片,在数据源分片时,就路由定下来了走
     *               otherDb的话,那么这里定位真实表时,候选的真实表集合里只有staff;如果
     *               在数据源分片时,路由定下来了走youngerDb的话,那么这里定位真实表时,候选的
     *               真实表集合里只有staff_man和staff_woman;
     *
     * @param shardingValue
     *            分片键的值。
     *            如:本人此次示例中,路由至哪张表,是由gender为真实表分片键的,
     *               gender的列类型为char,所以这里的泛型是String。
     *               如果分片键的类型是int的话,这里的泛型就应该是Integer.
     *               如果分片键的类型是bigint的话,这里的泛型就应该是Long.
     *               如果分片键的类型是varchar/char的话,这里的泛型就应该是String.
     *
     * @return 路由后的SQL要使用的数据源(库)的名字   或   路由后的SQL要使用的真实表的名字
     *          注:这里为 路由后的SQL要使用的真实表的名字
     */
    @Override
    public String doSharding(Collection availableTargetNames,
                             PreciseShardingValue shardingValue) {
        // 根据本人的真实数据节点信息, 如果 可用表里面包含staff,说明一定用的是otherDb数据源,
        // 而该数据库里面只有一张表staff, 这里直接返回即可
        if (availableTargetNames.contains("staff")) {
            return "staff";
        }
        // 如果是youngerDb数据源 或 olderDb数据源 的话,会走到下面的逻辑
        String tmpGender = shardingValue.getValue();
        if ("男".equals(tmpGender)) {
            return "staff_man";
        } else if ("女".equals(tmpGender)) {
            return "staff_woman";
        }
        throw new UnsupportedOperationException();
    }
}

测试一下

测试准备:正常编写针对逻辑表的相关类以及SQL语句

ShardingSphere之分库&分表_第5张图片

StaffPO模型:

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

/**
 * 职工模型, 对应staff表
 *
 * @author JustryDeng
 * @date 2019/6/3 23:26
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class StaffPO {

    /** 分布式主键id */
    private String id;

    /** 年龄 */
    private Integer age;

    /** 姓名 */
    private String name;

    /** 性别 */
    private String gender;
}

ShardingDatabaseTableApplication启动类:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

/**
 * 启动类
 *
 * @author JustryDeng
 * @date 2019/5/29 11:03
 */
@SpringBootApplication
@EnableTransactionManagement
public class ShardingDatabaseTableApplication {

    public static void main(String[] args) {
        SpringApplication.run(ShardingDatabaseTableApplication.class, args);
    }

}

ShardingService业务接口:

import com.aspire.shardingdatabasetable.model.StaffPO;

import java.util.List;

/**
 * Service层
 *
 * @author JustryDeng
 * @date 2019/5/29 17:35
 */
public interface ShardingService {

    /**
     * 插入测试
     *
     * @param staffPO
     *            职工模型
     * @return  受影响行数
     * @date 2019/6/3 23:42
     */
    int insertDemo(StaffPO staffPO);

    /**
     * 查询测试
     *
     * @param age
     *            年龄条件
     * @return  满足要求的职工信息集合
     * @date 2019/6/4 21:55
     */
    List queryDemo(Integer age);
}

ShardingServiceImpl业务实现:

import com.aspire.shardingdatabasetable.mapper.ShardingMapper;
import com.aspire.shardingdatabasetable.model.StaffPO;
import com.aspire.shardingdatabasetable.service.ShardingService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * 业务逻辑层
 *
 * @author JustryDeng
 * @date 2019/5/29 17:35
 */
@Slf4j
@Service
public class ShardingServiceImpl implements ShardingService {

    private final ShardingMapper shardingMapper;

    public ShardingServiceImpl(ShardingMapper shardingMapper) {
        this.shardingMapper = shardingMapper;
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public int insertDemo(StaffPO staffPO) {
        log.info("got into ShardingServiceImpl -> insertDemo, param is -> {}", staffPO);
        return shardingMapper.insertData(staffPO);
    }

    @Override
    public List queryDemo(Integer age) {
        log.info("got into ShardingServiceImpl -> queryDemo, param is -> {}", age);
        return shardingMapper.queryStaffByAge(age);
    }
}

ShardingMapper数据操作层:

import com.aspire.shardingdatabasetable.model.StaffPO;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

import java.util.List;

/**
 * 持久层
 *
 * @author JustryDeng
 * @date 2019/5/29 17:36
 */
@Mapper
public interface ShardingMapper {

    /**
     * 向逻辑表staff中插入数据
     *
     * @param staffPO
     *            职工模型
     * @return  受影响行数
     * @date 2019/6/3 23:38
     */
    @Insert({"insert into staff (`id`, `age`, `name`, `gender`) values (#{staffPO.id},",
             " #{staffPO.age}, #{staffPO.name}, #{staffPO.gender})"})
    int insertData(@Param("staffPO") StaffPO staffPO);

    /**
     * 根据年龄大于等于给定age的职工信息
     *
     * @param age
     *            年龄条件
     * @return  符合要求的职工集合
     * @date 2019/6/4 21:52
     */
    @Select("select id, name, age, gender from staff where age >= #{age}")
    List queryStaffByAge(Integer age);
}

编写测试方法

ShardingSphere之分库&分表_第6张图片

import com.aspire.shardingdatabasetable.model.StaffPO;
import com.aspire.shardingdatabasetable.service.ShardingService;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;
import java.util.UUID;

@RunWith(SpringRunner.class)
@SpringBootTest
public class ShardingDatabaseTableApplicationTests {

    @Autowired
    ShardingService shardingService;

    @Test
    public void allTest() {
        oneTest();
        twoTest();
        threeTest();
        fourTest();
        fiveTest();
        sixTest();
    }

    @Test
    public void oneTest() {
        String uuid = UUID.randomUUID().toString();
        StaffPO staffPO = StaffPO.builder()
                                 .id(uuid)
                                 .name("邓沙利文")
                                 .age(25)
                                 .gender("男")
                                 .build();
        int result = shardingService.insertDemo(staffPO);
        Assert.assertEquals(1, result);
    }

    @Test
    public void twoTest() {
        String uuid = UUID.randomUUID().toString();
        StaffPO staffPO = StaffPO.builder()
                                 .id(uuid)
                                 .name("疯一般的女子")
                                 .age(123)
                                 .gender("女")
                                 .build();
        int result = shardingService.insertDemo(staffPO);
        Assert.assertEquals(1, result);
    }

    @Test
    public void threeTest() {
        String uuid = UUID.randomUUID().toString();
        StaffPO staffPO = StaffPO.builder()
                                 .id(uuid)
                                 .name("无敌小婷妹")
                                 .age(21)
                                 .gender("女")
                                 .build();
        int result = shardingService.insertDemo(staffPO);
        Assert.assertEquals(1, result);
    }

    @Test
    public void fourTest() {
        String uuid = UUID.randomUUID().toString();
        StaffPO staffPO = StaffPO.builder()
                                 .id(uuid)
                                 .name("邓~")
                                 .age(125)
                                 .gender("男")
                                 .build();
        int result = shardingService.insertDemo(staffPO);
        Assert.assertEquals(1, result);
    }

    @Test
    public void fiveTest() {
        String uuid = UUID.randomUUID().toString();
        StaffPO staffPO = StaffPO.builder()
                                 .id(uuid)
                                 .name("亨得帅")
                                 .age(66)
                                 .gender("男")
                                 .build();
        int result = shardingService.insertDemo(staffPO);
        Assert.assertEquals(1, result);
    }

    @Test
    public void sixTest() {
        String uuid = UUID.randomUUID().toString();
        StaffPO staffPO = StaffPO.builder()
                                 .id(uuid)
                                 .name("小聋女")
                                 .age(80)
                                 .gender("女")
                                 .build();
        int result = shardingService.insertDemo(staffPO);
        Assert.assertEquals(1, result);
    }

    @Test
    public void qeuryTest() {
        List result = shardingService.queryDemo(10);
        for (StaffPO staffPO : result) {
            System.out.println(staffPO);
        }
    }
}

进行测试

插入测试:运行allTest方法后,控制台输出:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.1.4.RELEASE)

23:56:49.904 [main] INFO  Sharding-JDBC-SQL:59- Logic SQL: insert into staff (`id`, `age`, `name`, `gender`) values (?,  ?, ?, ?)
23:56:49.906 [main] INFO  Sharding-JDBC-SQL:59- SQLStatement: InsertStatement(columns=[Column(name=id, tableName=staff), Column(name=age, tableName=staff), Column(name=name, tableName=staff), Column(name=gender, tableName=staff)], multipleConditions=[], columnsListLastPosition=48, generateKeyColumnIndex=-1, afterValuesPosition=57, valuesListLastPosition=69, generatedKey=null)
23:56:49.906 [main] INFO  Sharding-JDBC-SQL:59- Actual SQL: youngerDb ::: insert into staff_man (`id`, `age`, `name`, `gender`) values (?,  ?, ?, ?) ::: [2c757252-3779-4d0f-b70f-db272f12b091, 25, 邓沙利文, 男]
23:56:50.116 [main] INFO  Sharding-JDBC-SQL:59- Logic SQL: insert into staff (`id`, `age`, `name`, `gender`) values (?,  ?, ?, ?)
23:56:50.116 [main] INFO  Sharding-JDBC-SQL:59- SQLStatement: InsertStatement(columns=[Column(name=id, tableName=staff), Column(name=age, tableName=staff), Column(name=name, tableName=staff), Column(name=gender, tableName=staff)], multipleConditions=[], columnsListLastPosition=48, generateKeyColumnIndex=-1, afterValuesPosition=57, valuesListLastPosition=69, generatedKey=null)
23:56:50.116 [main] INFO  Sharding-JDBC-SQL:59- Actual SQL: otherDb ::: insert into staff (`id`, `age`, `name`, `gender`) values (?,  ?, ?, ?) ::: [5a6ed073-5891-482f-ba1e-959f4e832cfd, 123, 疯一般的女子, 女]
23:56:50.149 [main] INFO  Sharding-JDBC-SQL:59- Logic SQL: insert into staff (`id`, `age`, `name`, `gender`) values (?,  ?, ?, ?)
23:56:50.150 [main] INFO  Sharding-JDBC-SQL:59- SQLStatement: InsertStatement(columns=[Column(name=id, tableName=staff), Column(name=age, tableName=staff), Column(name=name, tableName=staff), Column(name=gender, tableName=staff)], multipleConditions=[], columnsListLastPosition=48, generateKeyColumnIndex=-1, afterValuesPosition=57, valuesListLastPosition=69, generatedKey=null)
23:56:50.150 [main] INFO  Sharding-JDBC-SQL:59- Actual SQL: youngerDb ::: insert into staff_woman (`id`, `age`, `name`, `gender`) values (?,  ?, ?, ?) ::: [3d0aadf3-94e2-4ee8-9715-421fd8f4bc9d, 21, 无敌小婷妹, 女]
23:56:50.208 [main] INFO  Sharding-JDBC-SQL:59- Logic SQL: insert into staff (`id`, `age`, `name`, `gender`) values (?,  ?, ?, ?)
23:56:50.209 [main] INFO  Sharding-JDBC-SQL:59- SQLStatement: InsertStatement(columns=[Column(name=id, tableName=staff), Column(name=age, tableName=staff), Column(name=name, tableName=staff), Column(name=gender, tableName=staff)], multipleConditions=[], columnsListLastPosition=48, generateKeyColumnIndex=-1, afterValuesPosition=57, valuesListLastPosition=69, generatedKey=null)
23:56:50.209 [main] INFO  Sharding-JDBC-SQL:59- Actual SQL: otherDb ::: insert into staff (`id`, `age`, `name`, `gender`) values (?,  ?, ?, ?) ::: [c1cde34f-3093-4296-ac57-bef30f7b4777, 125, 邓~, 男]
23:56:50.302 [main] INFO  Sharding-JDBC-SQL:59- Logic SQL: insert into staff (`id`, `age`, `name`, `gender`) values (?,  ?, ?, ?)
23:56:50.302 [main] INFO  Sharding-JDBC-SQL:59- SQLStatement: InsertStatement(columns=[Column(name=id, tableName=staff), Column(name=age, tableName=staff), Column(name=name, tableName=staff), Column(name=gender, tableName=staff)], multipleConditions=[], columnsListLastPosition=48, generateKeyColumnIndex=-1, afterValuesPosition=57, valuesListLastPosition=69, generatedKey=null)
23:56:50.302 [main] INFO  Sharding-JDBC-SQL:59- Actual SQL: olderDb ::: insert into staff_man (`id`, `age`, `name`, `gender`) values (?,  ?, ?, ?) ::: [29c8165f-ba9f-4aad-bb27-552026840ca4, 66, 亨得帅, 男]
23:56:50.374 [main] INFO  Sharding-JDBC-SQL:59- Logic SQL: insert into staff (`id`, `age`, `name`, `gender`) values (?,  ?, ?, ?)
23:56:50.375 [main] INFO  Sharding-JDBC-SQL:59- SQLStatement: InsertStatement(columns=[Column(name=id, tableName=staff), Column(name=age, tableName=staff), Column(name=name, tableName=staff), Column(name=gender, tableName=staff)], multipleConditions=[], columnsListLastPosition=48, generateKeyColumnIndex=-1, afterValuesPosition=57, valuesListLastPosition=69, generatedKey=null)
23:56:50.375 [main] INFO  Sharding-JDBC-SQL:59- Actual SQL: olderDb ::: insert into staff_woman (`id`, `age`, `name`, `gender`) values (?,  ?, ?, ?) ::: [3f0463a8-26bc-4cf2-8133-7878fda556e0, 80, 小聋女, 女]

打开库表,观察:

older库staff_man表:

ShardingSphere之分库&分表_第7张图片

older库staff_woman表:

ShardingSphere之分库&分表_第8张图片

younger库staff_man表:

ShardingSphere之分库&分表_第9张图片

younger库staff_woman表:

ShardingSphere之分库&分表_第10张图片

other库staff表:

ShardingSphere之分库&分表_第11张图片

可见分库分表成功!

查询测试:运行qeuryTest方法后,控制台输出:

ShardingSphere之分库&分表_第12张图片

查询也没问题!

ShardingSphere实现分库分表完毕!

 

^_^ 如有不当之处,欢迎指正

^_^ 参考链接
              
https://shardingsphere.apache.org/d...
              http://wuwenliang.net/2019/03/26/...
              https://www.cnblogs.com/mr-yang-localhost/p/8313360.html

^_^ 测试代码托管链接
              
https://github.com/JustryDeng/CommonRepository

^_^ 本文已经被收录进《程序员成长笔记(五)》,笔者JustryDeng

你可能感兴趣的:(Java知识大杂烩)