ShardingSphere JDBC (二)基本案例入门

559 阅读9分钟

二 基本案例入门

  • order.sql
DROP TABLE IF EXISTS t_order_1;
CREATE TABLE t_order_1(
  order_id varchar(255) NOT NULL   COMMENT '订单编号' ,
  order_time DATETIME NOT NULL   COMMENT '订单时间' ,
  order_money DECIMAL(24,6) NOT NULL   COMMENT '订单金额' ,
  user_id INT NOT NULL   COMMENT '订单下单人' ,
  PRIMARY KEY (order_id)
)  COMMENT = '订单表';


DROP TABLE IF EXISTS t_order_2;
CREATE TABLE t_order_2(
  order_id INT NOT NULL   COMMENT '订单编号' ,
  order_time DATETIME NOT NULL   COMMENT '订单时间' ,
  order_money DECIMAL(24,6) NOT NULL   COMMENT '订单金额' ,
  user_id INT NOT NULL   COMMENT '订单下单人' ,
  PRIMARY KEY (order_id)
)  COMMENT = '订单表';
  • dict.sql
DROP TABLE IF EXISTS t_dict;
CREATE TABLE t_dict(
    dict_id INT(11) NOT NULL AUTO_INCREMENT  COMMENT '字典id' ,
    dict_fid INT(11)    COMMENT '所属字典;上级id;0:为字典类型,其余为该字典类型下的值' ,
    dict_label VARCHAR(255)    COMMENT '字典名称;字典名称' ,
    dict_value VARCHAR(255)    COMMENT '字典值' ,
    dict_code VARCHAR(255)    COMMENT '字典唯一编码' ,
    create_time DATETIME    COMMENT '创建时间' ,
    remark VARCHAR(255)    COMMENT '备注' ,
    is_delete VARCHAR(1) NOT NULL  DEFAULT 1 COMMENT '是否删除;0:已删除;1:未删除' ,
    PRIMARY KEY (dict_id)
)  COMMENT = '字典信息表';

2.1 基本结构搭建

  • 依赖
<?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.1.1.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>
  <groupId>com.shu</groupId>
  <artifactId>ShardingSphereJdbcDemo</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>ShardingSphereJdbcDemo</name>
  <description>ShardingSphereJdbcDemo</description>
  <properties>
    <java.version>1.8</java.version>
  </properties>
  <dependencies>
    
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
    </dependency>
    
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
    </dependency>
    
    <!--  mybatis依赖 -->
    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>2.0.0</version>
    </dependency>
    
    
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid-spring-boot-starter</artifactId>
      <version>1.2.5</version>
    </dependency>
    
    
    <dependency>
      <groupId>org.apache.shardingsphere</groupId>
      <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
      <version>4.0.0-RC1</version>
    </dependency>
    
    
    
    
    <!--swagger2依赖-->
    <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-swagger2</artifactId>
      <version>2.9.2</version>
    </dependency>
    
    <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-swagger-ui</artifactId>
      <version>2.9.2</version>
    </dependency>
    
    
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.22</version>
    </dependency>
    
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
    
    
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter</artifactId>
      <version>5.8.1</version>
      <scope>test</scope>
    </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>
  • 实体类
package com.shu.pojo;

import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

/**
 * @description: 订单实体类
 * @author: shu
 * @createDate: 2022/8/4 15:13
 * @version: 1.0
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {
    /** 订单编号 */
    @ApiModelProperty(name = "订单编号",notes = "")
    private String orderId ;
    /** 订单时间 */
    @ApiModelProperty(name = "订单时间",notes = "")
    @JsonFormat(locale="zh", timezone="GMT+8", pattern="yyyy-MM-dd HH:mm:ss")
    private Date orderTime ;
    /** 订单金额 */
    @ApiModelProperty(name = "订单金额",notes = "")
    private Double orderMoney ;
    /** 订单下单人 */
    @ApiModelProperty(name = "订单下单人",notes = "")
    private Integer userId ;
}



package com.shu.pojo;

import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

/**
 * @description: 字典表
 * @author: shu
 * @createDate: 2022/8/5 9:35
 * @version: 1.0
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dict {
    /** 字典id */
    @ApiModelProperty(name = "字典id",notes = "")
    private Integer dictId ;
    /** 所属字典;上级id;0:为字典类型,其余为该字典类型下的值 */
    @ApiModelProperty(name = "所属字典",notes = "上级id;0:为字典类型,其余为该字典类型下的值")
    private Integer dictFid ;
    /** 字典名称;字典名称 */
    @ApiModelProperty(name = "字典名称",notes = "字典名称")
    private String dictLabel ;
    /** 字典值 */
    @ApiModelProperty(name = "字典值",notes = "")
    private String dictValue ;
    /** 字典唯一编码 */
    @ApiModelProperty(name = "字典唯一编码",notes = "")
    private String dictCode ;
    /** 创建时间 */
    @ApiModelProperty(name = "创建时间",notes = "")
    @JsonFormat(locale="zh", timezone="GMT+8", pattern="yyyy-MM-dd HH:mm:ss")
    private Date createTime ;
    /** 备注 */
    @ApiModelProperty(name = "备注",notes = "")
    private String remark ;
    /** 是否删除;0:已删除;1:未删除 */
    @ApiModelProperty(name = "是否删除",notes = "0:已删除;1:未删除")
    private String isDelete ;
}
  • 其余的自行搭建

2.2 水平分表测试

  • 首先我们需要在数据库中创建相同的两张表。

  • 我们将要编写插入,与查询测试,因此我们需要编写sql

mapper

     // 插入一条记录
    @Insert("insert into t_order (order_time, order_money, user_id) values (#{orderTime}, #{orderMoney}, #{userId})")
    int insert(Order order);
    
    // 查询所有记录
    @Select("select * from t_order")
    List<Order> selectAll();
    
    // 根据主键查询一条记录
    @Select("select * from t_order where order_id = #{orderId}")
    Order selectByPrimaryKey(String orderId);

service

package com.shu.service;

import com.shu.mapper.OrderTestMapper;
import com.shu.pojo.Order;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;

/**
 * @description:
 * @author: shu
 * @createDate: 2022/8/4 15:33
 * @version: 1.0
 */
@Service
public class OrderService {
    @Autowired()
    OrderTestMapper orderTestMapper;


    public int insert(Order order) {
        return orderTestMapper.insert(order);
    }


    public List<Order> selectAll() {
        return orderTestMapper.selectAll();
    }


    public Order selectByPrimaryKey(String orderId) {
        return orderTestMapper.selectByPrimaryKey(orderId);
    }


}

controller

package com.shu.controller;

import com.shu.pojo.Order;
import com.shu.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @description:
 * @author: shu
 * @createDate: 2022/8/4 15:34
 * @version: 1.0
 */
@RestController
@Slf4j
public class OrderController {

    @Autowired
    OrderService orderService;


    @PostMapping("/order")
    public int insert(@RequestBody Order order) {
        return orderService.insert(order);
    }


    @GetMapping("/order")
    public List<Order> selectAll() {
        return orderService.selectAll();
    }


    @GetMapping("/order/{orderId}")
    public Order selectByPrimaryKey(@PathVariable("orderId") String orderId) {
        return orderService.selectByPrimaryKey(orderId);
    }


}
  • 配置文件配置

在官网中,提供了中配置,我们可以根据自己的选择来决定选择那种配置。

  • application.yaml配置

2.2.1 数据源配置

dataSources: # 数据源配置,可配置多个 <data-source-name>
<data-source-name>: # 数据源名称
dataSourceClassName: # 数据源完整类名
driverClassName: # 数据库驱动类名,以数据库连接池自身配置为准
jdbcUrl: # 数据库 URL 连接,以数据库连接池自身配置为准
username: # 数据库用户名,以数据库连接池自身配置为准
password: # 数据库密码,以数据库连接池自身配置为准
    # ... 数据库连接池的其它属性

我们可以看到我们配置多个数据源,这里我们是水平分表

# 开启大驼峰命名法
mybatis:
  configuration:
    map-underscore-to-camel-case: true
# 分库分表配置
spring:
   sharding-sphere:
     datasource:
       # 数据源名称
       names: d1,m3
       # 数据源d1配置
       d1.type: com.alibaba.druid.pool.DruidDataSource
       d1.url: jdbc:mysql://localhost:3306/order_db_1?useUnicode=true&characterEncoding=utf8
       d1.username: root
       d1.password: 123456
       d1.driverClassName: com.mysql.cj.jdbc.Driver
       # 其他数据源配置
       m3.type: com.alibaba.druid.pool.DruidDataSource
       m3.url: jdbc:mysql://localhost:3306/auth?useUnicode=true&characterEncoding=utf8
       m3.username: root
       m3.password: 123456
       m3.driverClassName: com.mysql.cj.jdbc.Driver

2.2.2 数据分片配置

rules:
- !SHARDING
  tables: # 数据分片规则配置
    <logic-table-name> (+): # 逻辑表名称
      actualDataNodes (?): # 由数据源名 + 表名组成(参考 Inline 语法规则)
      databaseStrategy (?): # 分库策略,缺省表示使用默认分库策略,以下的分片策略只能选其一
        standard: # 用于单分片键的标准分片场景
          shardingColumn: # 分片列名称
          shardingAlgorithmName: # 分片算法名称
        complex: # 用于多分片键的复合分片场景
          shardingColumns: # 分片列名称,多个列以逗号分隔
          shardingAlgorithmName: # 分片算法名称
        hint: # Hint 分片策略
          shardingAlgorithmName: # 分片算法名称
        none: # 不分片
      tableStrategy: # 分表策略,同分库策略
      keyGenerateStrategy: # 分布式序列策略
        column: # 自增列名称,缺省表示不使用自增主键生成器
        keyGeneratorName: # 分布式序列算法名称
  autoTables: # 自动分片表规则配置
    t_order_auto: # 逻辑表名称
      actualDataSources (?): # 数据源名称
      shardingStrategy: # 切分策略
        standard: # 用于单分片键的标准分片场景
          shardingColumn: # 分片列名称
          shardingAlgorithmName: # 自动分片算法名称
  bindingTables (+): # 绑定表规则列表
    - <logic_table_name_1, logic_table_name_2, ...> 
    - <logic_table_name_1, logic_table_name_2, ...> 
  broadcastTables (+): # 广播表规则列表
    - <table-name>
    - <table-name>
  defaultDatabaseStrategy: # 默认数据库分片策略
  defaultTableStrategy: # 默认表分片策略
  defaultKeyGenerateStrategy: # 默认的分布式序列策略
  defaultShardingColumn: # 默认分片列名称
  
  # 分片算法配置
  shardingAlgorithms:
    <sharding-algorithm-name> (+): # 分片算法名称
      type: # 分片算法类型
      props: # 分片算法属性配置
      # ...
  
  # 分布式序列算法配置
  keyGenerators:
    <key-generate-algorithm-name> (+): # 分布式序列算法名称
      type: # 分布式序列算法类型
      props: # 分布式序列算法属性配置
      # ...

\

  # 分片规则配置
     sharding:
       tables:
         # 订单表
         t_order.actual_data_nodes: d1.t_order_$->{1..2}
         # 订单主键
         t_order.key-generator.column: order_id
         # 订单主键生成策略,主键生成策略为雪花算法
         t_order.key-generator.type: Snowflake
         # 定义分片键
         t_order.table-strategy.inline.sharding-column: user_id
         # 定义分片策略
         t_order.table-strategy.inline.algorithm-expression: t_order_$->{user_id % 2 + 1}
  • 完整配置
# 开启大驼峰命名法
mybatis:
  configuration:
    map-underscore-to-camel-case: true
# 分库分表配置
spring:
   sharding-sphere:
     datasource:
       # 数据源名称
       names: d1,m3
       # 数据源d1配置
       d1.type: com.alibaba.druid.pool.DruidDataSource
       d1.url: jdbc:mysql://localhost:3306/order_db_1?useUnicode=true&characterEncoding=utf8
       d1.username: root
       d1.password: 123456
       d1.driverClassName: com.mysql.cj.jdbc.Driver
       # 其他数据源配置
       m3.type: com.alibaba.druid.pool.DruidDataSource
       m3.url: jdbc:mysql://localhost:3306/auth?useUnicode=true&characterEncoding=utf8
       m3.username: root
       m3.password: 123456
       m3.driverClassName: com.mysql.cj.jdbc.Driver
      # 分片规则配置
     sharding:
       tables:
         # 订单表
         t_order.actual_data_nodes: d1.t_order_$->{1..2}
         # 订单主键
         t_order.key-generator.column: order_id
         # 订单主键生成策略,主键生成策略为雪花算法
         t_order.key-generator.type: Snowflake
         # 定义分片键
         t_order.table-strategy.inline.sharding-column: user_id
         # 定义分片策略
         t_order.table-strategy.inline.algorithm-expression: t_order_$->{user_id % 2 + 1}
     props:
       sql:
         show: true

logging:
  level:
    root: info
    org.springframework.web: info
    com.shu.mapper: debug
    druid.sql: debug
  • 插入测试

  • 查询测试

2.2.3 结论

  • 我们可以看到我们以userid作为分片键,当偶数插入第一张表,奇数插入第二张表
  • 查询的时候,他会去执行两次查询,在合并结果,返回给我们
  • 当然上面的主键生成策略,等等,可以自定义,后面在测试

2.3 水平分库测试

水平分库测试,首先准备两个数据库

  • 整体框架一样,只是配置文件不一样

spring:
   sharding-sphere:
     datasource:
       # 数据源名称
       names: d1,d2,m3
       # 数据源d1配置
       d1.type: com.alibaba.druid.pool.DruidDataSource
       d1.url: jdbc:mysql://localhost:3306/order_db_1?useUnicode=true&characterEncoding=utf8
       d1.username: root
       d1.password: 123456
       d1.driverClassName: com.mysql.cj.jdbc.Driver
       # 数据源d2配置
       d2.type: com.alibaba.druid.pool.DruidDataSource
       d2.url: jdbc:mysql://localhost:3306/order_db_2?useUnicode=true&characterEncoding=utf8
       d2.username: root
       d2.password: 123456
       d2.driverClassName: com.mysql.cj.jdbc.Driver
       # 数据源d3配置
       m3.type: com.alibaba.druid.pool.DruidDataSource
       m3.url: jdbc:mysql://localhost:3306/auth?useUnicode=true&characterEncoding=utf8
       m3.username: root
       m3.password: 123456
       m3.driverClassName: com.mysql.cj.jdbc.Driver

 sharding:
       tables:
         # 订单表
         t_order.actual_data_nodes: d$->{1..2}.t_order_1
         # 订单主键
         t_order.key-generator.column: order_id
         # 订单主键生成策略,主键生成策略为雪花算法
         t_order.key-generator.type: Snowflake
         # 定义分片键
         t_order.database-strategy.inline.sharding-column: user_id
         # 定义分片策略
         t_order.database-strategy.inline.algorithm-expression: d$->{user_id % 2 + 1}
     props:
       sql:
         show: true

测试

到这我们可以看到,ShardingSphere Jdbc帮我们解决了分库分表带来的一系列问题,当然这只是简单的使用,目的是对有个了解,详情请看后面笔记

2.4 数据源配置与分片配置

数据源配置

dataSources: # 数据源配置,可配置多个 <data-source-name>
  <data-source-name>: # 数据源名称
    dataSourceClassName: # 数据源完整类名
    driverClassName: # 数据库驱动类名,以数据库连接池自身配置为准
    jdbcUrl: # 数据库 URL 连接,以数据库连接池自身配置为准
    username: # 数据库用户名,以数据库连接池自身配置为准
    password: # 数据库密码,以数据库连接池自身配置为准
    # ... 数据库连接池的其它属性
 datasource:
       # 数据源名称
       names: d1,d2,m3
       # 数据源d1配置
       d1.type: com.alibaba.druid.pool.DruidDataSource
       d1.url: jdbc:mysql://localhost:3306/order_db_1?useUnicode=true&characterEncoding=utf8
       d1.username: root
       d1.password: 123456
       d1.driverClassName: com.mysql.cj.jdbc.Driver
       # 数据源d2配置
       d2.type: com.alibaba.druid.pool.DruidDataSource
       d2.url: jdbc:mysql://localhost:3306/order_db_2?useUnicode=true&characterEncoding=utf8
       d2.username: root
       d2.password: 123456
       d2.driverClassName: com.mysql.cj.jdbc.Driver
       # 数据源d3配置
       m3.type: com.alibaba.druid.pool.DruidDataSource
       m3.url: jdbc:mysql://localhost:3306/auth?useUnicode=true&characterEncoding=utf8
       m3.username: root
       m3.password: 123456
       m3.driverClassName: com.mysql.cj.jdbc.Driver

数据源配置比较简单

分片配置详细介绍

rules:
- !SHARDING
  tables: # 数据分片规则配置
    <logic-table-name> (+): # 逻辑表名称,比如你要进行分库分表的逻辑表名称,对应着你的数据库名称
      actualDataNodes (?): # 由数据源名 + 表名组成(参考 Inline 语法规则)
      # 分库策略,上面我们用的是 inline 内联分片策略配置
      databaseStrategy (?): # 分库策略,缺省表示使用默认分库策略,以下的分片策略只能选其一
        standard: # 用于单分片键的标准分片场景
          shardingColumn: # 分片列名称
          shardingAlgorithmName: # 分片算法名称
        complex: # 用于多分片键的复合分片场景
          shardingColumns: # 分片列名称,多个列以逗号分隔
          shardingAlgorithmName: # 分片算法名称
        hint: # Hint 分片策略
          shardingAlgorithmName: # 分片算法名称
        none: # 不分片
      tableStrategy: # 分表策略,同分库策略
      keyGenerateStrategy: # 分布式序列策略
        column: # 自增列名称,缺省表示不使用自增主键生成器
        keyGeneratorName: # 分布式序列算法名称
  autoTables: # 自动分片表规则配置
    t_order_auto: # 逻辑表名称
      actualDataSources (?): # 数据源名称
      shardingStrategy: # 切分策略
        standard: # 用于单分片键的标准分片场景
          shardingColumn: # 分片列名称
          shardingAlgorithmName: # 自动分片算法名称
  bindingTables (+): # 绑定表规则列表
    - <logic_table_name_1, logic_table_name_2, ...> 
    - <logic_table_name_1, logic_table_name_2, ...> 
  broadcastTables (+): # 广播表规则列表
    - <table-name>
    - <table-name>
  defaultDatabaseStrategy: # 默认数据库分片策略
  defaultTableStrategy: # 默认表分片策略
  defaultKeyGenerateStrategy: # 默认的分布式序列策略
  defaultShardingColumn: # 默认分片列名称
  
  # 分片算法配置
  shardingAlgorithms:
    <sharding-algorithm-name> (+): # 分片算法名称
      type: # 分片算法类型
      props: # 分片算法属性配置
      # ...
  
  # 分布式序列算法配置
  keyGenerators:
    <key-generate-algorithm-name> (+): # 分布式序列算法名称
      type: # 分布式序列算法类型
      props: # 分布式序列算法属性配置
      # ...
  • : 逻辑表名称,比如你要进行分库分表的逻辑表名称,对应着你的数据库名称,比如上面的t_order,在我们的数据库汇总t_order_1,或t_order_2,但是我们程序在sql语句中使操作的逻辑表

  • actualDataNodes :真实的数据库源,由上面的数据源名称+表名称构成(参考参考 Inline 语法规则)

  • databaseStrategy :分库策略,缺省表示使用默认分库策略,以下的分片策略只能选其一

当然不同的分库策略,配置参数不同。

  • tableStrategy:分表策略:分表策略,同分库策略

  • keyGenerateStrategy:主键生成策略

  • broadcastTables:广播表规则列表,在分库分表中,有一些共用的表比如字典表

  • binding-tables:绑定表规则列表

  • default-database-strategy:默认分库策略

  • default-table-strategy:默认分表策略

  • default-key-generator:默认主键生成策略