FlywayGuard:IDEA插件解决Flyway SQL冲突
摘要:在团队协作中,Flyway 版本冲突是数据库迁移最常见的痛点——版本号重复、乱序、已推送脚本被修改,往往导致合并冲突甚至启动失败。FlywayGuard 是一款开源的 IDEA 插件,在 commit、push、merge 前自动检测并拦截违规脚本,把 Flyway 版本规则前移到日常操作中,纯只读、不植入任何代码,帮助团队从源头规避数据库迁移冲突。
引言:Flyway 迁移的痛点
在微服务架构中,数据库版本管理是持续交付的关键环节。Flyway 作为流行的数据库迁移工具,通过版本化的 SQL 脚本确保数据库结构的一致性。然而,在实际团队协作中,我们经常遇到以下问题:
- 脚本冲突,版本号重复、没有递增:多个开发人员同时修改数据库结构,导致
V1.2__add_user_table.sql等脚本版本号重复或乱序 - 多人协作造成冲突:merge、commit、push 时产生版本冲突
- 版本号命名不规范:推荐使用时间戳 + DML/DDL + 描述 的命名方式
- 冲突后需要调整 history 表:版本冲突后需要手动修改 Flyway 的
flyway_schema_history表 - 已提交的 SQL 被修改也会报错:Flyway 的 history 表记录了脚本的 md5 校验码,已推送的脚本被修改后校验失败Flyway 是一款开源的数据库版本控制工具,通过版本化的 SQL 脚本(如
V1.1__create_table.sql)确保数据库结构的一致性。其核心是严格按版本号顺序执行迁移,并在数据库中记录执行历史。
问题分析:SQL 冲突的根源
冲突场景示例
假设团队中有两位开发者同时工作:
开发者 A创建了:V1.3__add_user_role.sql
ALTERTABLEusersADDCOLUMNroleVARCHAR(30);开发者 B创建了:V1.3__add_user_department.sql
ALTERTABLEusersADDCOLUMNdepartment_idBIGINT;两者都使用了版本号1.3,当合并代码时就会产生冲突。传统解决方案需要:
- 手动重命名其中一个文件
- 调整依赖关系
- 通知团队成员
- 可能还需要修改已部署环境的脚本
解决方案:FlywayGuard IDEA 插件
FlywayGuard 是一款开源的 IntelliJ IDEA 插件(Apache License 2.0),定位为Flyway 迁移脚本哨兵:在提交、推送、合并这些日常操作前,通过纯 IDE 级的行为检测与拦截,把 Flyway 的版本规则前移,而不是等 CI 或跑数据库时才报错。它不向用户仓库植入任何代码——不写 git hook、不改core.hooksPath、不写任何文件进项目,纯只读检测 + IDE 拦截。
源码:https://gitee.com/my_cctest/flyway-guard
插件初心与设计原则
Flyway 迁移脚本是团队共享的数据库变更契约,一旦推送(push)并被他人执行,就不可修改、版本号不可重复、版本号只能递增。违反这些规则会造成:
- 修改已推送的脚本→ 别人库里已经跑过,改不生效或产生脏数据
- 版本号重复→ Flyway 报
Found more than one migration with version X,启动即失败 - 版本号乱序(新增版本低于已推送的最高版本)→ Flyway 默认
outOfOrder=false拒绝执行
FlywayGuard 的初心就是在问题发生前拦住它,靠 IDE 自动兜底。两条铁律:
- 不植入:不往仓库写 git hook、不改
core.hooksPath、不写任何文件进项目,纯只读检测 + IDE 拦截。 - 轻量化:版本规则、上游分支等一律遵循 Flyway/git 标准自动探测,尽量不设配置项。
功能总览
| 功能 | 触发场景 | 行为 |
|---|---|---|
| 提交拦截 | commit 前 | 硬拦违规,弹「仍然提交 / 取消」 |
| 推送拦截 | Push 对话框确认前 | 硬拦违规,弹「仍然推送 / 取消」 |
| 违规脚本编辑器横幅 | 打开文件时 | 违规脚本(重复/乱序/已提交被改)在编辑器顶部显示警示横幅 |
| 文件变化实时刷新图标 | SQL 内容/改名/增删时 | 立即重算非法集合,项目树红✕/绿✓即时更新 |
| 文件状态图标 | 项目树 | .sql数据库圆柱图标 + 合规绿✓/非法红✕ 角标 |
| 总开关 | 工具窗口 | 勾选启用(默认);取消后不再检测 commit/push/merge 异常 SQL |
| 版本冲突实时标红 | 编辑器 | 同版本重复的脚本标红 |
| 合并/推送后冲突通知 | merge / pull / push / fetch 后 | 弹通知:重复版本 + 乱序 |
| 工具窗口 | 手动查看 | 只显示版本号+文件名,重复/乱序/被改标红,可切换中英文,双击打开 |
界面截图
工具窗口(脚本列表、合规/违规角标、状态汇总与「启用」开关):
文件树角标(合规脚本绿✓、违规脚本红✕):
编辑器顶部警示横幅(已提交脚本被修改 / 违规脚本打开时):
提交拦截弹窗(违规项列表 + 「仍然提交 / 取消」):
合并前检查(预判把分支合并进来会引入的版本冲突):
拦截规则(commit 与 push 均生效)
- 修改 / 删除已提交的迁移脚本 →禁止
- 新增脚本版本号重复(与项目内已有脚本或上游冲突)→禁止
- 新增脚本版本号乱序(低于项目内最高版本或上游最高版本,Flyway 默认
outOfOrder=false)→禁止
插件安装与配置
1. 安装方式
IDEA 插件市场:Settings → Plugins → Marketplace → 搜索 “FlywayGuard”,安装后重启
手动安装:Settings → Plugins → ⚙️ → Install Plugin from Disk,选择
flyway-guard-4.0.0.jar,重启#### 2. 环境要求IntelliJ IDEA 2024.2+(含 Git 插件,默认内置)
系统可执行 git 命令(在 PATH 中)
项目为 git 仓库,且当前分支有上游(
@{u}或origin/master/origin/main之一)
插件仅在 git 仓库项目中生效;非 git 项目不产生任何拦截与提示,工具窗口会显示提示。上游缺失时仍会做项目内重复/乱序校验。
插件核心原理
FlywayGuard 基于 IntelliJ Platform(IDEA 2024.2+,Java 17)开发,通过平台扩展点实现各类拦截与检测:
| 扩展点 | 实现类 | 作用 |
|---|---|---|
checkinHandlerFactory | FlywayCheckinHandler | 提交前跑 FlywayCommitChecker,违规可 CANCEL |
prePushHandler | FlywayPrePushHandler | 收集待推送提交的变更,跑同一检查器,违规返回 ABORT |
fileIconProvider | FlywayGuardIconProvider | .sql数据库圆柱图标 + 合规绿✓/非法红✕ 角标 |
localInspection | FlywayVersionInspection | 编辑器实时标红同版本重复 |
postStartupActivity | FlywayGuardStartupActivity | 项目打开时刷新状态集合、订阅冲突通知 |
toolWindow | FlywayGuardPanel | 脚本列表(版本倒序、重复/乱序标红、双击打开) |
notificationGroup | FlywayConflictNotifier | merge/push/fetch 后扫描并弹通知 |
git 操作只读(git rev-parse、git ls-tree),不依赖 git4idea 的写操作;依赖 git4idea(GitRepository.GIT_REPO_CHANGE事件)与 DVCS push 框架(PrePushHandler)扩展点。
Flyway 标准版本规则(核心)
- 版本化迁移 = 文件名以
V<数字版本>__<描述>.sql开头(前缀V,分隔符__) - 版本号为纯数字,可用
.或_分段、可补零(V1、V1.0.1、V1_1、V001.002、V20220824) - 比较:按数字逐段比较、忽略前导零(
V011 > V007;1.0与1视为相同;缺失段视为0) - 唯一性:版本不能重复
- 递增:新增版本必须高于已推送的最高版本
上游(已推送)基准自动探测,无需配置
@{u}→origin/<当前分支>→ 最近共同祖先远端分支(当前分支未推送时)- push 拦截用只读
git ls-tree -r <upstream>取已推送脚本集做对比 - 状态图标、工具窗口、通知则用 IDEA 文件状态判定「已提交」为基准(无色=已提交、蓝色=已提交被改、绿色=新增),不依赖上游解析
日常使用
- 新增迁移脚本:按 Flyway 命名
V<更高版本号>__<描述>.sql创建即可。若版本号与上游重复、或低于上游最高版本,提交时会被拦截。 - 提交 / 推送被拦:IDEA 弹窗列出违规项。选择「取消」回到编辑器修复;确认必须执行时选「仍然提交 / 仍然推送」放行。
- 已提交脚本:可编辑但会被警示——IDEA 将改动标为蓝色(已提交被改),文件树显示红✕、编辑器顶部横幅提示;提交/推送仍会被拦截(如需修改,应新增更高版本脚本承载变更)。
- 实时标红:同版本号的两个脚本在编辑器中直接标红。
- 合并 / 拉取后:若引入重复版本或乱序,插件自动弹通知提示。
- 工具窗口(右侧 FlywayGuard):只展示版本号 + 文件名,按版本号倒序;重复版本、乱序、已提交被改的脚本红色显示;顶部可切换中文 / 英文(作用于整个插件);双击任意脚本跳转打开;「刷新」按钮手动重新扫描。
例外通道
所有拦截均为 IDE 级提示,可通过弹窗中的「仍然提交 / 仍然推送」显式确认后放行,不改动任何代码。若确实需要修改已提交脚本,请在弹窗提示基础上额外遵循团队约定(如--no-verify例外精神)。
已知限制
- 仅 git 仓库项目生效:非 git 项目不拦截、不提示(工具窗口显示提示)。
- 只拦截 IDEA 内的提交与推送;在终端用
git commit/git push不会被拦截(插件不植入 git hook)。 - 编辑器实时标红仅覆盖同版本重复;乱序靠工具窗口标红、合并后通知、以及 commit/push 硬拦兜底。
- 单仓库假设:一个 IDEA 项目打开多个 git 仓库时,只检测项目根所在仓库。
- 上游对比依赖本地已 fetch 到的远程状态(
git ls-tree <upstream>),协作者刚推送的新版本需先git fetch才能感知。
