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

本文涉及的产品
云数据库 RDS MySQL,集群版 2核4GB 100GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 【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更新语句中,需要排除掉分片键的更新。

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

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1天前
|
存储 Web App开发 Java
《手把手教你》系列基础篇(九十五)-java+ selenium自动化测试-框架之设计篇-java实现自定义日志输出(详解教程)
【7月更文挑战第13天】这篇文章介绍了如何在Java中创建一个简单的自定义日志系统,以替代Log4j或logback。
11 5
|
4天前
|
设计模式 测试技术 Python
《手把手教你》系列基础篇(九十二)-java+ selenium自动化测试-框架设计基础-POM设计模式简介(详解教程)
【7月更文挑战第10天】Page Object Model (POM)是Selenium自动化测试中的设计模式,用于提高代码的可读性和维护性。POM将每个页面表示为一个类,封装元素定位和交互操作,使得测试脚本与页面元素分离。当页面元素改变时,只需更新对应页面类,减少了脚本的重复工作和维护复杂度,有利于团队协作。POM通过创建页面对象,管理页面元素集合,将业务逻辑与元素定位解耦合,增强了代码的复用性。示例展示了不使用POM时,脚本直接混杂了元素定位和业务逻辑,而POM则能解决这一问题。
23 6
|
2天前
|
监控 Java 开发者
Spring Boot框架在java领域的优势
随着云计算、微服务架构的兴起,Java开发领域迫切需要一套高效、灵活且易于上手的框架来应对日益复杂的业务需求。正是在这样的背景下,Spring Boot应运而生,以其独特的魅力迅速成为了Java开发者手中的利器。
10 3
|
1天前
|
设计模式 Java 测试技术
《手把手教你》系列基础篇(九十四)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-下篇(详解教程)
【7月更文挑战第12天】在本文中,作者宏哥介绍了如何在不使用PageFactory的情况下,用Java和Selenium实现Page Object Model (POM)。文章通过一个百度首页登录的实战例子来说明。首先,创建了一个名为`BaiduHomePage1`的页面对象类,其中包含了页面元素的定位和相关操作方法。接着,创建了测试类`TestWithPOM1`,在测试类中初始化WebDriver,设置驱动路径,最大化窗口,并调用页面对象类的方法进行登录操作。这样,测试脚本保持简洁,遵循了POM模式的高可读性和可维护性原则。
11 2
|
3天前
|
安全 前端开发 Java
Java技术栈中的核心组件:Spring框架
Java作为一门成熟的编程语言,其生态系统拥有众多强大的组件和框架,其中Spring框架无疑是Java技术栈中最闪耀的明星之一。Spring框架为Java开发者提供了一套全面的编程和配置模型,极大地简化了企业级应用的开发流程。
9 1
|
6天前
|
XML Java 测试技术
《手把手教你》系列基础篇(九十一)-java+ selenium自动化测试-框架设计基础-Logback实现日志输出-下篇(详解教程)
【7月更文挑战第9天】在Java项目中,使用Logback配置可以实现日志按照不同包名输出到不同的文件,并且根据日志级别分开记录。
20 4
|
5天前
|
设计模式 存储 安全
Java面试题:设计一个线程安全的单例类并解释其内存占用情况?使用Java多线程工具类实现一个高效的线程池,并解释其背后的原理。结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
Java面试题:设计一个线程安全的单例类并解释其内存占用情况?使用Java多线程工具类实现一个高效的线程池,并解释其背后的原理。结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
13 1
|
3天前
|
设计模式 Java 测试技术
《手把手教你》系列基础篇(九十三)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-上篇(详解教程)
【7月更文挑战第11天】页面对象模型(POM)通过Page Factory在Java Selenium测试中被应用,简化了代码维护。在POM中,每个网页对应一个Page Class,其中包含页面元素和相关操作。对比之下,非POM实现直接在测试脚本中处理元素定位和交互,代码可读性和可维护性较低。
|
4天前
|
SQL Java 数据库连接
Java面试题:简述ORM框架(如Hibernate、MyBatis)的工作原理及其优缺点。
Java面试题:简述ORM框架(如Hibernate、MyBatis)的工作原理及其优缺点。
5 0
|
4天前
|
存储 安全 Java
Java面试题:请解释Java中的泛型集合框架?以及泛型的经典应用案例
Java面试题:请解释Java中的泛型集合框架?以及泛型的经典应用案例
10 0