Commit a1f5a0f0d418675def32cf1c09a4148e8e014738
1 parent
769e5926
feat(infra): 增加可选的 ShardingSphere 分库分表集成材料
Showing
8 changed files
with
423 additions
and
0 deletions
script/sharding/README.md
0 → 100644
| 1 | +# 分库分表(可选接入) | |
| 2 | + | |
| 3 | +基于 [Apache ShardingSphere-JDBC](https://shardingsphere.apache.org/) 5.5.3 的可选接入材料。项目默认**不启用**:本目录不是 Maven 模块,不参与默认构建,主工程的依赖、配置、业务表都不受影响。 | |
| 4 | + | |
| 5 | +接入后,`master` 主库保持不变,只有 `@DS("sharding")` 的代码,才通过 ShardingSphereDriver 访问 2 库 × 2 表的示例逻辑表 `demo_sharding_order`。 | |
| 6 | + | |
| 7 | +安装、配置、启动验证及使用限制,请阅读项目文档《分库分表》,本文只列出材料及分支适用范围: | |
| 8 | + | |
| 9 | +- 单体项目 ruoyi-vue-pro:<https://doc.iocoder.cn/sharding/> | |
| 10 | +- 微服务项目 yudao-cloud:<https://cloud.iocoder.cn/sharding/> | |
| 11 | + | |
| 12 | +## 1. 材料清单 | |
| 13 | + | |
| 14 | +| 文件 | 说明 | 单体项目的目标位置 | 微服务项目的目标位置 | | |
| 15 | +| --- | --- | --- | --- | | |
| 16 | +| `sql/mysql/demo_sharding_order.sql` | 创建 2 个物理库、4 张物理表 | 在 MySQL 中执行 | 在 MySQL 中执行 | | |
| 17 | +| `dependencies.xml` | Maven 依赖片段 | `yudao-server/pom.xml` | 服务的 `pom.xml`,例如 `yudao-module-infra-server/pom.xml` | | |
| 18 | +| `dynamic-datasource.yaml` | `sharding` 数据源配置片段 | `yudao-server` 的 `application-{profile}.yaml` | 服务的 `application-{profile}.yaml` 或 Nacos 配置 | | |
| 19 | +| `src/main/resources/sharding/sharding.yaml` | ShardingSphere 分片规则 | `yudao-module-infra/src/main/resources` | `yudao-module-infra-server/src/main/resources` | | |
| 20 | +| `src/main/java/.../shardingdemo/` | 示例 DO、Mapper、Service | `yudao-module-infra/src/main/java` | `yudao-module-infra-server/src/main/java` | | |
| 21 | + | |
| 22 | +## 2. 分支适用范围 | |
| 23 | + | |
| 24 | +本目录维护在 `master-jdk17` 分支,`master`、`master-jdk25` 分支通过正常的分支同步获得同一份材料,三个分支的接入步骤相同。按各分支 Spring Boot BOM 管理的依赖版本,接入时的差异如下: | |
| 25 | + | |
| 26 | +| 分支 | JDK | Spring Boot | 差异 | | |
| 27 | +| --- | --- | --- | --- | | |
| 28 | +| `master` | 8 | 2.7.18 | 添加 `dependencies.xml` 第 2 段的 SnakeYAML 1.33;不能单独升级到 2.x,原因见该文件的注释 | | |
| 29 | +| `master-jdk17` | 17 | 3.5.15 | 添加 `dependencies.xml` 第 3 段的 SnakeYAML 2.7 | | |
| 30 | +| `master-jdk25` | 25 | 4.1.2 | 添加 `dependencies.xml` 第 3 段的 SnakeYAML 2.7 | | |
| 31 | + | |
| 32 | +`dependencies.xml` 的第 2、3 段 SnakeYAML 只能按分支选择其一,不能同时添加。 | |
| 33 | + | |
| 34 | +示例 Java 代码只依赖项目已有的 MyBatis Plus、Dynamic Datasource 和多租户组件,不直接引用 ShardingSphere 的类,三个分支无需修改。 | ... | ... |
script/sharding/dependencies.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | |
| 2 | +<!-- | |
| 3 | + 分库分表(ShardingSphere-JDBC 5.5.3)的 Maven 依赖片段,不是 Maven 工程,不参与项目构建。 | |
| 4 | + | |
| 5 | + 使用时,将下面的 <dependency> 复制到【最终打包启动】模块的 pom.xml 的 <dependencies> 中: | |
| 6 | + - 单体项目:yudao-server/pom.xml | |
| 7 | + - 微服务项目:分库分表所在服务的 pom.xml,例如说 yudao-module-infra/yudao-module-infra-server/pom.xml | |
| 8 | + | |
| 9 | + 复制「1. 所有分支都需要」,再按项目所在分支,从「2.」「3.」中【只选一段】SnakeYAML,两段不能同时添加。 | |
| 10 | + | |
| 11 | + ShardingSphere 5.5.x 是插件化的,shardingsphere-jdbc 只包含内核,以下功能需要显式引入。 | |
| 12 | + Groovy、Caffeine、Jackson、HikariCP 等传递依赖的版本,继续由项目的 Spring Boot BOM 管理,不要额外覆盖。 | |
| 13 | +--> | |
| 14 | +<dependencies> | |
| 15 | + | |
| 16 | + <!-- ========== 1. 所有分支都需要 ========== --> | |
| 17 | + <dependency> | |
| 18 | + <groupId>org.apache.shardingsphere</groupId> | |
| 19 | + <artifactId>shardingsphere-jdbc</artifactId> <!-- 内核,提供 ShardingSphereDriver --> | |
| 20 | + <version>5.5.3</version> | |
| 21 | + </dependency> | |
| 22 | + <dependency> | |
| 23 | + <groupId>org.apache.shardingsphere</groupId> | |
| 24 | + <artifactId>shardingsphere-sharding-core</artifactId> <!-- 数据分片功能,即 !SHARDING 规则 --> | |
| 25 | + <version>5.5.3</version> | |
| 26 | + </dependency> | |
| 27 | + <dependency> | |
| 28 | + <groupId>org.apache.shardingsphere</groupId> | |
| 29 | + <artifactId>shardingsphere-jdbc-dialect-mysql</artifactId> <!-- MySQL 的 SQL 解析、路由、改写 --> | |
| 30 | + <version>5.5.3</version> | |
| 31 | + </dependency> | |
| 32 | + <dependency> | |
| 33 | + <groupId>org.apache.shardingsphere</groupId> | |
| 34 | + <artifactId>shardingsphere-infra-url-classpath</artifactId> <!-- 支持 jdbc:shardingsphere:classpath: 加载规则文件 --> | |
| 35 | + <version>5.5.3</version> | |
| 36 | + </dependency> | |
| 37 | + <dependency> | |
| 38 | + <groupId>org.apache.shardingsphere</groupId> | |
| 39 | + <artifactId>shardingsphere-standalone-mode-repository-memory</artifactId> <!-- 单机模式的 Memory 存储 --> | |
| 40 | + <version>5.5.3</version> | |
| 41 | + </dependency> | |
| 42 | + <dependency> | |
| 43 | + <groupId>org.apache.shardingsphere</groupId> | |
| 44 | + <artifactId>shardingsphere-infra-data-source-pool-hikari</artifactId> <!-- 物理库使用 HikariCP 连接池 --> | |
| 45 | + <version>5.5.3</version> | |
| 46 | + </dependency> | |
| 47 | + <dependency> | |
| 48 | + <groupId>org.apache.shardingsphere</groupId> | |
| 49 | + <artifactId>shardingsphere-authority-simple</artifactId> <!-- 默认的 ALL_PERMITTED 权限实现,缺少时初始化报 SPI-00001 --> | |
| 50 | + <version>5.5.3</version> | |
| 51 | + </dependency> | |
| 52 | + | |
| 53 | + <!-- ========== 2. 仅 master 分支(JDK 8 + Spring Boot 2.7.18):SnakeYAML 1.33 ========== --> | |
| 54 | + <!-- | |
| 55 | + Spring Boot 2.7.18 管理的 SnakeYAML 是 1.30,缺少 ShardingSphere 调用的 LoaderOptions#setCodePointLimit 方法(1.32 起提供), | |
| 56 | + 需要显式升级。1.33 是 1.x 的最后一个版本,ShardingSphere 与 Spring Boot 2.7.18 管理的 jackson-dataformat-yaml 2.13.5 都能使用。 | |
| 57 | + 不能只把 SnakeYAML 升级到 2.x(包括 2.7):2.x 删除了 ParserImpl(StreamReader) 构造方法,jackson-dataformat-yaml 2.13.5 解析 YAML 时 | |
| 58 | + 会报 NoSuchMethodError;jackson-dataformat-yaml 从 2.15 起才改用 SnakeYAML 2.x,这需要整体升级项目的 Jackson 版本,不在本方案内。 | |
| 59 | + master-jdk17、master-jdk25 分支不要添加本段,使用下面的「3.」。 | |
| 60 | + --> | |
| 61 | + <dependency> | |
| 62 | + <groupId>org.yaml</groupId> | |
| 63 | + <artifactId>snakeyaml</artifactId> | |
| 64 | + <version>1.33</version> | |
| 65 | + </dependency> | |
| 66 | + | |
| 67 | + <!-- ========== 3. 仅 master-jdk17(Spring Boot 3.5.15)、master-jdk25(Spring Boot 4.1.2)分支:SnakeYAML 2.7 ========== --> | |
| 68 | + <!-- | |
| 69 | + Spring Boot BOM 管理的 SnakeYAML 分别是 2.4、2.6,可以直接使用;这里显式升级到 2.7,ShardingSphere 规则加载、 | |
| 70 | + Spring Boot 配置加载,以及 BOM 管理的新版 jackson-dataformat-yaml 都能使用。 | |
| 71 | + master 分支不要添加本段,使用上面的「2.」。 | |
| 72 | + --> | |
| 73 | + <dependency> | |
| 74 | + <groupId>org.yaml</groupId> | |
| 75 | + <artifactId>snakeyaml</artifactId> | |
| 76 | + <version>2.7</version> | |
| 77 | + </dependency> | |
| 78 | + | |
| 79 | +</dependencies> | ... | ... |
script/sharding/dynamic-datasource.yaml
0 → 100644
| 1 | +# 分库分表的 sharding 数据源配置片段,不能直接作为配置文件使用。 | |
| 2 | +# 将 sharding 合并到已有的 spring.datasource.dynamic.datasource 下,与 master、slave 同级,其它配置保持不变: | |
| 3 | +# - 单体项目:yudao-server 的 application-{profile}.yaml | |
| 4 | +# - 微服务项目:对应服务(例如说 yudao-module-infra-server)的 application-{profile}.yaml,或者 Nacos 中该服务的配置 | |
| 5 | +spring: | |
| 6 | + datasource: | |
| 7 | + dynamic: | |
| 8 | + datasource: | |
| 9 | + sharding: # 分库分表数据源,只有 @DS("sharding") 的 Mapper、Service 使用,不影响 master 主库 | |
| 10 | + lazy: false # 启动时初始化,规则或数据库有误时尽早失败;也避免在异步线程中首次加载 classpath 规则文件 | |
| 11 | + type: com.zaxxer.hikari.HikariDataSource # 不使用 Druid 连接池,ShardingSphere 的逻辑 SQL 不需要 Druid 解析 | |
| 12 | + driver-class-name: org.apache.shardingsphere.driver.ShardingSphereDriver | |
| 13 | + url: jdbc:shardingsphere:classpath:sharding/sharding.yaml?placeholder-type=environment # 规则文件中的 $${环境变量名::默认值},从环境变量读取 | |
| 14 | + hikari: | |
| 15 | + minimum-idle: 1 | |
| 16 | + maximum-pool-size: 10 # 逻辑连接数,不能大于 sharding.yaml 中每个物理库的 maximumPoolSize | ... | ... |
script/sharding/sql/mysql/demo_sharding_order.sql
0 → 100644
| 1 | +-- ---------------------------- | |
| 2 | +-- 分库分表示例:2 个物理库 × 每库 2 张物理表,对应逻辑表 demo_sharding_order | |
| 3 | +-- | |
| 4 | +-- 1. 分片规则见 src/main/resources/sharding/sharding.yaml: | |
| 5 | +-- 分库:ds_${user_id % 2},分表:demo_sharding_order_${user_id.intdiv(2) % 2} | |
| 6 | +-- 2. 本脚本使用「库名.表名」,可以在同一个 MySQL 实例上直接执行; | |
| 7 | +-- 如果 ds_0、ds_1 是两个不同的 MySQL 实例,则分别执行对应库的语句 | |
| 8 | +-- 3. 主键 id 由应用的雪花算法生成,所以不使用 AUTO_INCREMENT | |
| 9 | +-- 4. 不要在 master 主库创建同名的 demo_sharding_order 表,避免数据源切换失效时“静默”写入主库 | |
| 10 | +-- ---------------------------- | |
| 11 | + | |
| 12 | +CREATE DATABASE IF NOT EXISTS `yudao_sharding_0` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; | |
| 13 | +CREATE DATABASE IF NOT EXISTS `yudao_sharding_1` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; | |
| 14 | + | |
| 15 | +-- ---------------------------- | |
| 16 | +-- ds_0:yudao_sharding_0 | |
| 17 | +-- ---------------------------- | |
| 18 | +CREATE TABLE IF NOT EXISTS `yudao_sharding_0`.`demo_sharding_order_0` ( | |
| 19 | + `id` bigint NOT NULL COMMENT '编号', | |
| 20 | + `user_id` bigint NOT NULL COMMENT '用户编号(分片键)', | |
| 21 | + `name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '订单名称', | |
| 22 | + `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者', | |
| 23 | + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', | |
| 24 | + `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者', | |
| 25 | + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', | |
| 26 | + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', | |
| 27 | + `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', | |
| 28 | + PRIMARY KEY (`id`) USING BTREE, | |
| 29 | + KEY `idx_user_id` (`user_id`) USING BTREE | |
| 30 | +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '分库分表示例订单表'; | |
| 31 | + | |
| 32 | +CREATE TABLE IF NOT EXISTS `yudao_sharding_0`.`demo_sharding_order_1` ( | |
| 33 | + `id` bigint NOT NULL COMMENT '编号', | |
| 34 | + `user_id` bigint NOT NULL COMMENT '用户编号(分片键)', | |
| 35 | + `name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '订单名称', | |
| 36 | + `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者', | |
| 37 | + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', | |
| 38 | + `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者', | |
| 39 | + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', | |
| 40 | + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', | |
| 41 | + `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', | |
| 42 | + PRIMARY KEY (`id`) USING BTREE, | |
| 43 | + KEY `idx_user_id` (`user_id`) USING BTREE | |
| 44 | +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '分库分表示例订单表'; | |
| 45 | + | |
| 46 | +-- ---------------------------- | |
| 47 | +-- ds_1:yudao_sharding_1 | |
| 48 | +-- ---------------------------- | |
| 49 | +CREATE TABLE IF NOT EXISTS `yudao_sharding_1`.`demo_sharding_order_0` ( | |
| 50 | + `id` bigint NOT NULL COMMENT '编号', | |
| 51 | + `user_id` bigint NOT NULL COMMENT '用户编号(分片键)', | |
| 52 | + `name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '订单名称', | |
| 53 | + `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者', | |
| 54 | + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', | |
| 55 | + `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者', | |
| 56 | + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', | |
| 57 | + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', | |
| 58 | + `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', | |
| 59 | + PRIMARY KEY (`id`) USING BTREE, | |
| 60 | + KEY `idx_user_id` (`user_id`) USING BTREE | |
| 61 | +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '分库分表示例订单表'; | |
| 62 | + | |
| 63 | +CREATE TABLE IF NOT EXISTS `yudao_sharding_1`.`demo_sharding_order_1` ( | |
| 64 | + `id` bigint NOT NULL COMMENT '编号', | |
| 65 | + `user_id` bigint NOT NULL COMMENT '用户编号(分片键)', | |
| 66 | + `name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '订单名称', | |
| 67 | + `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者', | |
| 68 | + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', | |
| 69 | + `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者', | |
| 70 | + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', | |
| 71 | + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', | |
| 72 | + `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', | |
| 73 | + PRIMARY KEY (`id`) USING BTREE, | |
| 74 | + KEY `idx_user_id` (`user_id`) USING BTREE | |
| 75 | +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '分库分表示例订单表'; | ... | ... |
script/sharding/src/main/java/cn/iocoder/yudao/module/infra/shardingdemo/DemoShardingOrderDO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.infra.shardingdemo; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; | |
| 4 | +import com.baomidou.mybatisplus.annotation.IdType; | |
| 5 | +import com.baomidou.mybatisplus.annotation.TableId; | |
| 6 | +import com.baomidou.mybatisplus.annotation.TableName; | |
| 7 | +import lombok.*; | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * 分库分表示例订单 DO | |
| 11 | + * | |
| 12 | + * 逻辑表 demo_sharding_order,按 userId 分布到 2 个库 × 每库 2 张表,规则见 sharding/sharding.yaml | |
| 13 | + * | |
| 14 | + * @author 芋道源码 | |
| 15 | + */ | |
| 16 | +@TableName("demo_sharding_order") | |
| 17 | +@Data | |
| 18 | +@EqualsAndHashCode(callSuper = true) | |
| 19 | +@ToString(callSuper = true) | |
| 20 | +@Builder | |
| 21 | +@NoArgsConstructor | |
| 22 | +@AllArgsConstructor | |
| 23 | +public class DemoShardingOrderDO extends TenantBaseDO { | |
| 24 | + | |
| 25 | + /** | |
| 26 | + * 编号 | |
| 27 | + * | |
| 28 | + * 分库分表后无法使用数据库自增,由 MyBatis Plus 的雪花算法生成全局唯一编号 | |
| 29 | + */ | |
| 30 | + @TableId(type = IdType.ASSIGN_ID) | |
| 31 | + private Long id; | |
| 32 | + /** | |
| 33 | + * 用户编号,即分片键 | |
| 34 | + * | |
| 35 | + * 新增时必须有值,创建后不允许修改 | |
| 36 | + */ | |
| 37 | + private Long userId; | |
| 38 | + /** | |
| 39 | + * 订单名称 | |
| 40 | + */ | |
| 41 | + private String name; | |
| 42 | + | |
| 43 | +} | ... | ... |
script/sharding/src/main/java/cn/iocoder/yudao/module/infra/shardingdemo/DemoShardingOrderMapper.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.infra.shardingdemo; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 4 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 5 | +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | |
| 6 | +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | |
| 7 | +import com.baomidou.dynamic.datasource.annotation.DS; | |
| 8 | +import org.apache.ibatis.annotations.Mapper; | |
| 9 | + | |
| 10 | +import java.util.List; | |
| 11 | + | |
| 12 | +/** | |
| 13 | + * 分库分表示例订单 Mapper | |
| 14 | + * | |
| 15 | + * 在 Mapper 上添加 @DS 注解,保证逻辑表 demo_sharding_order 的 SQL 都使用 sharding 数据源 | |
| 16 | + * | |
| 17 | + * @author 芋道源码 | |
| 18 | + */ | |
| 19 | +@Mapper | |
| 20 | +@DS(DemoShardingOrderMapper.DATA_SOURCE) | |
| 21 | +public interface DemoShardingOrderMapper extends BaseMapperX<DemoShardingOrderDO> { | |
| 22 | + | |
| 23 | + /** | |
| 24 | + * 分库分表的数据源名,对应 spring.datasource.dynamic.datasource.sharding 配置项 | |
| 25 | + */ | |
| 26 | + String DATA_SOURCE = "sharding"; | |
| 27 | + | |
| 28 | + default List<DemoShardingOrderDO> selectListByUserId(Long userId) { | |
| 29 | + return selectList(new LambdaQueryWrapperX<DemoShardingOrderDO>() | |
| 30 | + .eq(DemoShardingOrderDO::getUserId, userId) | |
| 31 | + .orderByDesc(DemoShardingOrderDO::getId)); | |
| 32 | + } | |
| 33 | + | |
| 34 | + default PageResult<DemoShardingOrderDO> selectPage(PageParam pageParam, Long userId) { | |
| 35 | + return selectPage(pageParam, new LambdaQueryWrapperX<DemoShardingOrderDO>() | |
| 36 | + .eqIfPresent(DemoShardingOrderDO::getUserId, userId) // 有 userId 时只查询一个分片,否则查询全部分片后归并 | |
| 37 | + .orderByDesc(DemoShardingOrderDO::getId)); | |
| 38 | + } | |
| 39 | + | |
| 40 | +} | ... | ... |
script/sharding/src/main/java/cn/iocoder/yudao/module/infra/shardingdemo/DemoShardingOrderService.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.infra.shardingdemo; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 4 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 5 | +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | |
| 6 | +import com.baomidou.dynamic.datasource.annotation.DS; | |
| 7 | +import lombok.RequiredArgsConstructor; | |
| 8 | +import org.springframework.stereotype.Service; | |
| 9 | +import org.springframework.transaction.annotation.Transactional; | |
| 10 | +import org.springframework.util.Assert; | |
| 11 | + | |
| 12 | +import java.util.List; | |
| 13 | + | |
| 14 | +/** | |
| 15 | + * 分库分表示例订单 Service | |
| 16 | + * | |
| 17 | + * 1. 带 userId(分片键)的方法,只路由到一个物理库的一张物理表 | |
| 18 | + * 2. 不带 userId 的方法,会查询全部 4 个物理节点,再由 ShardingSphere 归并结果 | |
| 19 | + * | |
| 20 | + * @author 芋道源码 | |
| 21 | + */ | |
| 22 | +@Service | |
| 23 | +@DS(DemoShardingOrderMapper.DATA_SOURCE) // 先切换到 sharding 数据源,再开启 @Transactional 事务 | |
| 24 | +@RequiredArgsConstructor // 使用构造方法注入,避免 JDK 8 与 JDK 17+ 分支的 @Resource 包名不同 | |
| 25 | +public class DemoShardingOrderService { | |
| 26 | + | |
| 27 | + private final DemoShardingOrderMapper demoShardingOrderMapper; | |
| 28 | + | |
| 29 | + @Transactional(rollbackFor = Exception.class) // 同一个 userId 只在一个物理库,所以是单库的本地事务 | |
| 30 | + public Long createOrder(Long userId, String name) { | |
| 31 | + Assert.notNull(userId, "用户编号(分片键)不能为空"); | |
| 32 | + DemoShardingOrderDO order = DemoShardingOrderDO.builder().userId(userId).name(name).build(); | |
| 33 | + demoShardingOrderMapper.insert(order); | |
| 34 | + return order.getId(); | |
| 35 | + } | |
| 36 | + | |
| 37 | + @Transactional(rollbackFor = Exception.class) | |
| 38 | + public int updateOrderName(Long userId, Long id, String name) { | |
| 39 | + Assert.notNull(userId, "用户编号(分片键)不能为空"); | |
| 40 | + // 更新条件带上分片键;更新内容不包含 userId,分片键创建后不允许修改 | |
| 41 | + DemoShardingOrderDO updateObj = DemoShardingOrderDO.builder().name(name).build(); | |
| 42 | + return demoShardingOrderMapper.update(updateObj, new LambdaQueryWrapperX<DemoShardingOrderDO>() | |
| 43 | + .eq(DemoShardingOrderDO::getUserId, userId) | |
| 44 | + .eq(DemoShardingOrderDO::getId, id)); | |
| 45 | + } | |
| 46 | + | |
| 47 | + @Transactional(rollbackFor = Exception.class) | |
| 48 | + public int deleteOrder(Long userId, Long id) { | |
| 49 | + Assert.notNull(userId, "用户编号(分片键)不能为空"); | |
| 50 | + return demoShardingOrderMapper.delete(new LambdaQueryWrapperX<DemoShardingOrderDO>() | |
| 51 | + .eq(DemoShardingOrderDO::getUserId, userId) | |
| 52 | + .eq(DemoShardingOrderDO::getId, id)); | |
| 53 | + } | |
| 54 | + | |
| 55 | + public DemoShardingOrderDO getOrder(Long userId, Long id) { | |
| 56 | + Assert.notNull(userId, "用户编号(分片键)不能为空"); | |
| 57 | + return demoShardingOrderMapper.selectOne(DemoShardingOrderDO::getUserId, userId, | |
| 58 | + DemoShardingOrderDO::getId, id); | |
| 59 | + } | |
| 60 | + | |
| 61 | + public List<DemoShardingOrderDO> getOrderListByUserId(Long userId) { | |
| 62 | + Assert.notNull(userId, "用户编号(分片键)不能为空"); | |
| 63 | + return demoShardingOrderMapper.selectListByUserId(userId); | |
| 64 | + } | |
| 65 | + | |
| 66 | + /** | |
| 67 | + * 获得订单分页 | |
| 68 | + * | |
| 69 | + * @param pageParam 分页参数 | |
| 70 | + * @param userId 用户编号,可为空;为空时查询全部分片,页码越大,归并的数据越多 | |
| 71 | + * @return 订单分页 | |
| 72 | + */ | |
| 73 | + public PageResult<DemoShardingOrderDO> getOrderPage(PageParam pageParam, Long userId) { | |
| 74 | + return demoShardingOrderMapper.selectPage(pageParam, userId); | |
| 75 | + } | |
| 76 | + | |
| 77 | +} | ... | ... |
script/sharding/src/main/resources/sharding/sharding.yaml
0 → 100644
| 1 | +# ShardingSphere-JDBC 5.5.3 分片规则,由 sharding 数据源的 JDBC URL 加载: | |
| 2 | +# jdbc:shardingsphere:classpath:sharding/sharding.yaml?placeholder-type=environment | |
| 3 | +# | |
| 4 | +# 注意:本文件由 ShardingSphere 自己解析,不支持 Spring 的 ${ENV:default} 占位符。 | |
| 5 | +# 由于 URL 带有 placeholder-type=environment 参数,可以使用 $${环境变量名::默认值} 读取环境变量: | |
| 6 | +# 1. 名字与默认值之间的 :: 不能省略,没有默认值时写成 $${环境变量名::} | |
| 7 | +# 2. 值会原样替换到本文件中,密码不能包含 $、\、' 字符 | |
| 8 | +# 3. 不要把真实的账号密码写入本文件、提交到 Git | |
| 9 | + | |
| 10 | +databaseName: sharding_db # 逻辑库名,只在 ShardingSphere 内部使用 | |
| 11 | + | |
| 12 | +mode: | |
| 13 | + type: Standalone # 单机模式,规则只从本文件加载,不依赖注册中心 | |
| 14 | + repository: | |
| 15 | + type: Memory # 元数据保存在内存,不额外创建 H2 等存储 | |
| 16 | + | |
| 17 | +dataSources: | |
| 18 | + ds_0: | |
| 19 | + dataSourceClassName: com.zaxxer.hikari.HikariDataSource | |
| 20 | + driverClassName: com.mysql.cj.jdbc.Driver | |
| 21 | + jdbcUrl: $${YUDAO_SHARDING_DS0_URL::jdbc:mysql://127.0.0.1:3306/yudao_sharding_0?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true} | |
| 22 | + username: $${YUDAO_SHARDING_DS0_USERNAME::root} | |
| 23 | + password: '$${YUDAO_SHARDING_DS0_PASSWORD::}' | |
| 24 | + minimumIdle: 1 | |
| 25 | + maximumPoolSize: 10 # 不能小于 sharding 数据源的 hikari.maximum-pool-size | |
| 26 | + ds_1: | |
| 27 | + dataSourceClassName: com.zaxxer.hikari.HikariDataSource | |
| 28 | + driverClassName: com.mysql.cj.jdbc.Driver | |
| 29 | + jdbcUrl: $${YUDAO_SHARDING_DS1_URL::jdbc:mysql://127.0.0.1:3306/yudao_sharding_1?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true} | |
| 30 | + username: $${YUDAO_SHARDING_DS1_USERNAME::root} | |
| 31 | + password: '$${YUDAO_SHARDING_DS1_PASSWORD::}' | |
| 32 | + minimumIdle: 1 | |
| 33 | + maximumPoolSize: 10 # 不能小于 sharding 数据源的 hikari.maximum-pool-size | |
| 34 | + | |
| 35 | +rules: | |
| 36 | + - !SHARDING | |
| 37 | + tables: | |
| 38 | + demo_sharding_order: # 逻辑表,即 DO 上 @TableName 的表名 | |
| 39 | + actualDataNodes: ds_$->{0..1}.demo_sharding_order_$->{0..1} # 2 库 × 2 表,共 4 个物理节点 | |
| 40 | + databaseStrategy: | |
| 41 | + standard: | |
| 42 | + shardingColumn: user_id | |
| 43 | + shardingAlgorithmName: demo_sharding_order_database_inline | |
| 44 | + tableStrategy: | |
| 45 | + standard: | |
| 46 | + shardingColumn: user_id | |
| 47 | + shardingAlgorithmName: demo_sharding_order_table_inline | |
| 48 | + shardingAlgorithms: | |
| 49 | + demo_sharding_order_database_inline: | |
| 50 | + type: INLINE | |
| 51 | + props: | |
| 52 | + algorithm-expression: ds_$->{user_id % 2} # 分库:user_id 对 2 取余 | |
| 53 | + demo_sharding_order_table_inline: | |
| 54 | + type: INLINE | |
| 55 | + props: | |
| 56 | + algorithm-expression: demo_sharding_order_$->{user_id.intdiv(2) % 2} # 分表:user_id 整除 2 后对 2 取余,与分库错开,4 个节点均匀分布 | |
| 57 | + | |
| 58 | +props: | |
| 59 | + sql-show: false # 设置为 true 时,打印逻辑 SQL 与实际路由的 SQL(包含参数值),仅建议本地排查时开启 | ... | ... |