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

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

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

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
3月前
|
Java 数据库
在Java中使用Seata框架实现分布式事务的详细步骤
通过以上步骤,利用 Seata 框架可以实现较为简单的分布式事务处理。在实际应用中,还需要根据具体业务需求进行更详细的配置和处理。同时,要注意处理各种异常情况,以确保分布式事务的正确执行。
|
3月前
|
消息中间件 Java Kafka
在Java中实现分布式事务的常用框架和方法
总之,选择合适的分布式事务框架和方法需要综合考虑业务需求、性能、复杂度等因素。不同的框架和方法都有其特点和适用场景,需要根据具体情况进行评估和选择。同时,随着技术的不断发展,分布式事务的解决方案也在不断更新和完善,以更好地满足业务的需求。你还可以进一步深入研究和了解这些框架和方法,以便在实际应用中更好地实现分布式事务管理。
|
2月前
|
存储 安全 Java
Java 集合框架中的老炮与新秀:HashTable 和 HashMap 谁更胜一筹?
嗨,大家好,我是技术伙伴小米。今天通过讲故事的方式,详细介绍 Java 中 HashMap 和 HashTable 的区别。从版本、线程安全、null 值支持、性能及迭代器行为等方面对比,帮助你轻松应对面试中的经典问题。HashMap 更高效灵活,适合单线程或需手动处理线程安全的场景;HashTable 较古老,线程安全但性能不佳。现代项目推荐使用 ConcurrentHashMap。关注我的公众号“软件求生”,获取更多技术干货!
52 3
|
6天前
|
存储 缓存 Java
java语言后台管理ruoyi后台管理框架-登录提示“无效的会话,或者会话已过期,请重新登录。”-扩展知识数据库中密码加密的方法-问题如何解决-以及如何重置若依后台管理框架admin密码-优雅草卓伊凡
java语言后台管理ruoyi后台管理框架-登录提示“无效的会话,或者会话已过期,请重新登录。”-扩展知识数据库中密码加密的方法-问题如何解决-以及如何重置若依后台管理框架admin密码-优雅草卓伊凡
25 3
java语言后台管理ruoyi后台管理框架-登录提示“无效的会话,或者会话已过期,请重新登录。”-扩展知识数据库中密码加密的方法-问题如何解决-以及如何重置若依后台管理框架admin密码-优雅草卓伊凡
|
1月前
|
并行计算 算法 Java
Java中的Fork/Join框架详解
Fork/Join框架是Java并行计算的强大工具,尤其适用于需要将任务分解为子任务的场景。通过正确使用Fork/Join框架,可以显著提升应用程序的性能和响应速度。在实际应用中,应结合具体需求选择合适的任务拆分策略,以最大化并行计算的效率。
50 23
|
29天前
|
Java 数据库连接 数据库
【潜意识Java】使用 Ruoyi 框架开发企业级应用,从零开始的实践指南和分析问题
本文介绍了基于Spring Boot的开源企业级框架Ruoyi,涵盖环境搭建、项目初始化及用户管理模块的创建。
130 4
|
3月前
|
存储 缓存 安全
Java 集合框架优化:从基础到高级应用
《Java集合框架优化:从基础到高级应用》深入解析Java集合框架的核心原理与优化技巧,涵盖列表、集合、映射等常用数据结构,结合实际案例,指导开发者高效使用和优化Java集合。
72 4
|
3月前
|
开发框架 Java 关系型数据库
Java哪个框架适合开发API接口?
在快速发展的软件开发领域,API接口连接了不同的系统和服务。Java作为成熟的编程语言,其生态系统中出现了许多API开发框架。Magic-API因其独特优势和强大功能,成为Java开发者优选的API开发框架。本文将从核心优势、实际应用价值及未来展望等方面,深入探讨Magic-API为何值得选择。
130 2
|
3月前
|
前端开发 Java 数据库连接
你不可不知道的JAVA EE 框架有哪些?
本文介绍了框架的基本概念及其在编程领域的应用,强调了软件框架作为通用、可复用的软件环境的重要性。文章分析了早期Java EE开发中使用JSP+Servlet技术的弊端,包括可维护性差和代码重用性低等问题,并阐述了使用框架的优势,如提高开发效率、增强代码规范性和可维护性及提升软件性能。最后,文中详细描述了几种主流的Java EE框架,包括Spring、Spring MVC、MyBatis、Hibernate和Struts 2,这些框架通过提供强大的功能和支持,显著提升了Java EE应用的开发效率和稳定性。
233 1
|
3月前
|
Java 数据库连接 API
Spring 框架的介绍(Java EE 学习笔记02)
Spring是一个由Rod Johnson开发的轻量级Java SE/EE一站式开源框架,旨在解决Java EE应用中的多种问题。它采用非侵入式设计,通过IoC和AOP技术简化了Java应用的开发流程,降低了组件间的耦合度,支持事务管理和多种框架的无缝集成,极大提升了开发效率和代码质量。Spring 5引入了响应式编程等新特性,进一步增强了框架的功能性和灵活性。
76 0