Skip to content

数据库迁移

加表 / 加列 / 建索引不要让插件代码自己建表,也不要手工执行 SQL。 把 .sql 放进插件目录的 migrations/,打包时自动进包,安装/升级时自动执行。

plugins/ruoyi-<name>/migrations/01-create-<name>-tables.sql   # 文件名排序即执行顺序

为什么表结构必须随应用包走

图床就是被这件事打回来的:它原本是宿主编译期模块,5 张表建在平台初始化 SQL 里。 改成插件应用之后,客户那台只导入平台 SQL 的部署装上图床会所有页面报表不存在, 而且卸载时平台也无从知道该删哪些表

一句话:建表脚本有两个用途 —— 安装时建表,卸载时被平台读出来当删表清单。

执行链路

环节谁做说明
打包package.ps1默认取插件目录的 migrations/*.sql-MigrationPath 可指定
落位落位服务解到 <pluginDir>/migrations/<appCode>/不清理,历史脚本留着可审计)
执行迁移服务扫描该目录 → 按文件名排序 → 逐个执行
记账app_schema_history(app_code, script),含 checksum / success / execution_ms
删表依据表名解析从脚本里解析 CREATE TABLE 的表名,供"彻底删除"用

四条硬规矩

1. 只做 up,不回滚

app.jsonmigrations(含 down)是预留字段,当前不参与执行。 要回退表结构,请自己写一个"反向"的迁移脚本。

2. 脚本必须幂等

MySQL 的 DDL 是隐式提交的,没法整体回滚。重试时已成功的会跳过, 失败的那个必须能重跑

sql
CREATE TABLE IF NOT EXISTS `gallery_image` ( ... );

MySQL 8 没有 ADD COLUMN IF NOT EXISTS

加列前要用 information_schema 判断,再动态执行。

3. 已执行过的脚本内容不能改

同一 script 名再来时比对 checksum,不一致直接报错拒绝执行

迁移脚本 <name> 的内容已被修改(摘要与首次执行时不一致),
为避免同一脚本产生两种结果,拒绝执行。请改用新的脚本名。

要变更就新增一个脚本。这是刻意的保护,不要绕过它

摘要按字节算,所以换行差异要单独容忍

工作区是 CRLF、Linux 上是 LF,语义相同但摘要不同。

平台的比对同时认 LF / CRLF 两种摘要(记账统一存 LF 归一化摘要),真正的改动照旧拒绝。

不要plugins/*/migrations/*.sqleol 归一化 —— 本目录的换行本来就是混合的(有的应用 LF、有的 CRLF),"统一"反而会让所有存量记录失效一次。

4. 去重判据不带租户

表结构在所有租户间共享,带上租户会让第二个租户重复执行 DDL。 读写账本一律忽略租户上下文;tenant_id 只记"谁最先应用"。

账本唯一键必须正好是 (app_code, script)

曾经 DDL 写的是 (tenant_id, app_code, version),后果是 一个版本里只要有第二个迁移脚本就装不上。详见 数据模型

为什么是"目录驱动"而不是读清单

执行方式是扫描目录,而不是解析 app.jsonmigrations 字段。这样:

  • 「清单声明了脚本却忘了打包」这类错误根本不会发生
  • 手工补一个修数据脚本,扔进目录即可,不必重新打包上架。

顺序保证

落位  →  变更表结构  →  切换代码

这是刻意做出来的(把"落位"与"加载"拆成两步)。 所以插件在启动时机直接读新加的列是安全的

改过已执行脚本怎么办

改了 01-*.sql(例如把里面的示例数据拆出去)之后,存量部署会因摘要不一致被挡住。 这是刻意的保护,正确做法是:

  1. 先跑对应的修复脚本(删掉那几行账本记录);
  2. 升级(或重新部署)该应用 —— 迁移脚本只在 install / upgrade 里跑

光把包传上去不会有任何变化

迁移只在 install / upgrade 触发。存量部署拿到新种子数据的唯一路径就是 「先跑修复脚本 → 再升级」。

相关阅读

基于 MIT 协议开源 · 文档与官网由源码生成