【Java】ruoyi框架,添加ShardingJdbc支持分库分表

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: 【Java】ruoyi框架,添加ShardingJdbc支持分库分表
1、 调研,目前主流的有Mycat、Sharding Jdbc ,准备选用后者;

分库分表,一种是按照时间范围进行,另外一种按照某个字段为分片键;

我这里,选用表数据中的DeptId字段作为分片键,这样能够保证同一个部门下的数据 能够分布到同一个数据库下的同一张表中,避免跨库操作带来性能损失以及跨库操作带来的事物问题;

2、项目实战
2.1 配置项目环境

(1)添加项目pom

<dependency>
  <groupId>org.apache.shardingsphere</groupId>
  <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
  <version>4.1.1</version>
</dependency>
2.2 配置添加

(1)添加配置 application-sharding.yml(这部分主要是为了区分开分库分表的配置文件和其他,当然也可以直接在原有的配置文件基础上添加)

我这边默认按照两个数据库进行操作,其中处理了开票记录表和明细表 如有需要自己添加 ;

我这边分片键按照部门ID进行对2取余数,这样能够保证同一个部门下的数据能够到相同的数据库下相同的表中,避免了跨库操作;分片键可以自己进行调整 。

为什么是对2取余数?因为目前总共分出来2个数据库,dev_0、dev_1 如果是三个 那就是对3取余,总之 能够保证取余之后的范围在 0~n-1 ,n对应的是分出来的库数量。表也是一样的,我这边默认按照和库一样的规则。

spring:
  shardingsphere:
    enabled: true # 是否开启分库分表
    datasource:
      names: ds0,ds1
      ds0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/xxx_dev_0?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true
        username: root
        password: root
      ds1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/xxx_dev_1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true
        username: root
        password: root
    sharding:
      tables:
        output_base_invoice_history:
          name: tableName1
          database-strategy:  #分库策略
            inline:
              sharding-column: dept_id #分片列
              algorithm-expression: ds$->{dept_id % 2} # 计算数据分到哪个数据库
          actualDataNodes: ds$->{0..1}.tableName1_$->{0..1} # 映射到 ds0 和 ds1 数据源
          table-strategy:  #分表策略
            inline:
              algorithm-expression: tableName1_$->{dept_id % 2}
              sharding-column: dept_id
        output_base_history_invoice_items:
          name: tableName2
          database-strategy: #分库策略
            inline:
              sharding-column: dept_id #分片列
              algorithm-expression: ds$->{dept_id % 2} # 计算数据分到哪个数据库
          actualDataNodes: ds$->{0..1}.tableName2_$->{0..1} # 映射到 ds0 和 ds1 数据源
          table-strategy: #分表策略
            inline:
              algorithm-expression: tableName2_$->{dept_id % 2}
              sharding-column: dept_id
      props:
        sql:
          show: true
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    druid:
      ds0:
        enabled: true
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/xxx_dev_0?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true
        username: root
        password: root
      ds1:
        enabled: true
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/xxx_dev_1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true
        username: root
        password: root

(2)在ruoyi-framework工程下 修改DruidConfig类,添加Sharding的动态数据源支持:

首先,添加SHARDING的枚举

/**
 * 数据源
 *
 * @author ruoyi
 */
public enum DataSourceType {
    /**
     * 主库
     */
    MASTER,
    /**
     * 从库
     */
    SLAVE,
    /**
     * 分库分表
     */
    SHARDING
}

其次,添加sharding规则到动态数据源

@Bean(name = "dynamicDataSource")
    @Primary
    public DynamicDataSource dataSource(DataSource masterDataSource)
    {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
        setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");
        setDataSource(targetDataSources, DataSourceType.SHARDING.name(), "shardingDataSource");
        return new DynamicDataSource(masterDataSource, targetDataSources);
    }

之后,新建ShardingJdbc的配置类

/**
 * sharding 配置信息
 *
 * @author yanjun.hou
 * <p>
 * 用法:@DataSource(DataSourceType.SHARDING)
 * 在对应需要分表操作的service方法上加上注解
 * 如整个service都需要分表操作, 可直接将该注解加载类上
 */
@Configuration
public class ShardingDataSourceConfig {
    @Value("${spring.shardingsphere.sharding.tables.tableName1.name}")
    private String historyTable;
    @Value("${spring.shardingsphere.sharding.tables.tableName1.actualDataNodes}")
    private String historyTableActualDataNodes;
    @Value("${spring.shardingsphere.sharding.tables.tableName1.database-strategy.inline.sharding-column}")
    private String historyDbShardingColumn;
    @Value("${spring.shardingsphere.sharding.tables.tableName1.database-strategy.inline.algorithm-expression}")
    private String historyDbAlgorithmExpression;
    @Value("${spring.shardingsphere.sharding.tables.tableName1.table-strategy.inline.sharding-column}")
    private String historyTableShardingColumn;
    @Value("${spring.shardingsphere.sharding.tables.tableName1.table-strategy.inline.algorithm-expression}")
    private String historyTableAlgorithmExpression;
    @Value("${spring.shardingsphere.sharding.tables.tableName2.name}")
    private String historyItemsTable;
    @Value("${spring.shardingsphere.sharding.tables.tableName2.actualDataNodes}")
    private String historyItemsTableActualDataNodes;
    @Value("${spring.shardingsphere.sharding.tables.tableName2.database-strategy.inline.sharding-column}")
    private String historyItemsDbShardingColumn;
    @Value("${spring.shardingsphere.sharding.tables.tableName2.database-strategy.inline.algorithm-expression}")
    private String historyItemsDbAlgorithmExpression;
    @Value("${spring.shardingsphere.sharding.tables.tableName2.table-strategy.inline.sharding-column}")
    private String historyItemsTableShardingColumn;
    @Value("${spring.shardingsphere.sharding.tables.tableName2.table-strategy.inline.algorithm-expression}")
    private String historyItemsTableAlgorithmExpression;
    @Bean
    @ConfigurationProperties("spring.datasource.druid.ds0")
    @ConditionalOnProperty(prefix = "spring.datasource.druid.ds0", name = "enabled", havingValue = "true")
    public DataSource order1DataSource(DruidProperties druidProperties) {
        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
        return druidProperties.dataSource(dataSource);
    }
    @Bean
    @ConfigurationProperties("spring.datasource.druid.ds1")
    @ConditionalOnProperty(prefix = "spring.datasource.druid.ds1", name = "enabled", havingValue = "true")
    public DataSource order2DataSource(DruidProperties druidProperties) {
        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
        return druidProperties.dataSource(dataSource);
    }
    @Bean(name = "shardingDataSource")
    @ConditionalOnProperty(prefix = "spring.shardingsphere", name = "enabled", havingValue = "true")
    public DataSource shardingDataSource(@Qualifier("order1DataSource") DataSource order1DataSource, @Qualifier("order2DataSource") DataSource order2DataSource) throws SQLException {
        Map<String, DataSource> dataSourceMap = new HashMap<>();
        dataSourceMap.put("ds0", order1DataSource);
        dataSourceMap.put("ds1", order2DataSource);
 
        // 表规则配置
        TableRuleConfiguration invoiceHistory = new TableRuleConfiguration(historyTable, historyTableActualDataNodes);
        // 分库策略
        invoiceHistory.setDatabaseShardingStrategyConfig(new InlineShardingStrategyConfiguration(historyDbShardingColumn, historyDbAlgorithmExpression));
        // 分表策略
        invoiceHistory.setTableShardingStrategyConfig(new InlineShardingStrategyConfiguration(historyTableShardingColumn, historyTableAlgorithmExpression));
 
        // 表规则配置
        TableRuleConfiguration invoiceHistoryItems = new TableRuleConfiguration(historyItemsTable, historyItemsTableActualDataNodes);
        // 分库策略
        invoiceHistoryItems.setDatabaseShardingStrategyConfig(new InlineShardingStrategyConfiguration(historyItemsDbShardingColumn, historyItemsDbAlgorithmExpression));
        // 分表策略
        invoiceHistoryItems.setTableShardingStrategyConfig(new InlineShardingStrategyConfiguration(historyItemsTableShardingColumn, historyItemsTableAlgorithmExpression));
        // 分片规则
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
        Collection<TableRuleConfiguration> tableRuleConfigs = shardingRuleConfig.getTableRuleConfigs();
        tableRuleConfigs.add(invoiceHistory);
        tableRuleConfigs.add(invoiceHistoryItems);
        // 获取数据源对象
        DataSource dataSource = ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfig, getProperties());
        return dataSource;
    }
    /**
     * 系统参数配置
     */
    private Properties getProperties() {
        Properties shardingProperties = new Properties();
        shardingProperties.put("sql.show", true);
        return shardingProperties;
    }
}

最后,用法

首先,需要在application.yml中添加激活配置

active: dev,sharding

其次,在对应业务Mapper或者ServiceImpl 或者对应的方法上 添加 @DataSource(DataSourceType.SHARDING) 即可。

第一种
@DataSource(DataSourceType.SHARDING)
public interface xxxMapper {}
第二种
@DataSource(DataSourceType.SHARDING)
public class xxxServiceImpl {}
第三种
@Override
@DataSource(DataSourceType.SHARDING)
 public XXX xxx() {
     return xxx;
 }

其中,第一种和第二种都表示当前类下的所有方法都默认按照分库分表的规则进行。

重点说明:由于ShardingJdbc不支持修改分片键,所以在mapper的xml更新语句中,需要排除掉分片键的更新。

说明:如果是在老项目上进行分库分表,那么一般会涉及到数据的迁移。

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
负载均衡 算法 druid
【Java多数据源实现教程】实现动态数据源、多数据源切换方式(下)
【Java多数据源实现教程】实现动态数据源、多数据源切换方式(下)
554 0
|
4月前
|
监控 数据可视化 安全
Java智慧工地系统源码(微服务+Java+Springcloud+Vue+MySQL)
Java智慧工地系统源码(微服务+Java+Springcloud+Vue+MySQL)
68 0
|
8月前
|
Java 数据库连接 数据库
探索Java中的MyBatis Plus:简化持久层开发的利器
在现代软件开发中,持久层是一个不可或缺的部分。为了更高效地操作数据库,开发者们经常使用ORM(对象关系映射)工具来简化数据库交互。而在Java领域,MyBatis Plus正是这样一个备受欢迎的ORM工具,它通过提供一系列便捷的特性,极大地简化了持久层的开发。本文将深入探讨MyBatis Plus的特点以及如何在项目中使用它。
83 0
|
8月前
|
Java 数据库 Spring
|
8月前
|
Java Maven
java操作mongdb【springboot】
java操作mongdb【springboot】
51 0
|
8月前
java202304java学习笔记第六十六天-ssm-mybatis核心配置文件-概述
java202304java学习笔记第六十六天-ssm-mybatis核心配置文件-概述
29 0
|
8月前
java202304java学习笔记第六十六天-ssm-动态sql-核心配置文件深入
java202304java学习笔记第六十六天-ssm-动态sql-核心配置文件深入
31 0
|
11月前
|
SQL Java 关系型数据库
【java_wxid项目】【第八章】【Apache ShardingSphere集成】
主项目链接:https://gitee.com/java_wxid/java_wxid 项目架构及博文总结:
241 0
|
SQL 前端开发 Java
JAVA基础复习之JDBC(配置动态数据源)
复习原因 在项目的开发当中,之前数据库连接信息都是写死在配置文件当中。但是突然接到一个需求: 获取外部数据源信息,然后将某些数据通过Echarts绘制成折线图展示出来(ps:数据源需要用户手动设定) 有点懵,因为之前都是写死在配置文件当中的,然后在网上也找了一些资料,发现都不是自己想要的。 最后想到,既然是简单的获取数据进行展示,直接使用JDBC连接数据库获取就可以了啊。没有必要搞什么骚操作。 JDBC 一直使用的JPA或者mybatis进行数据库连接,很少使用JDBC进行数据库连接。 因此也复习一下JDBC,毕竟无论是JPA还是Mybatis都是对JDBC的一个封装。 什么是JD
|
存储 Java 中间件
【Java多数据源实现教程】实现动态数据源、多数据源切换方式(上)
【Java多数据源实现教程】实现动态数据源、多数据源切换方式(上)
711 0
【Java多数据源实现教程】实现动态数据源、多数据源切换方式(上)