- 新增 docs/FLYWAY_USAGE_GUIDE.md (326行完整使用指南) - AGENTS.md 新增铁律: 数据库变更必须通过 Flyway 迁移 - 禁止直接执行 DDL 而不创建迁移文件 - 禁止修改已执行的迁移文件 - 新表必须包含租户/审计/软删除字段
9.1 KiB
9.1 KiB
Flyway 数据库迁移使用指南
项目: OpenHIS 医院管理系统 数据库: PostgreSQL 192.168.110.252:15432 (schema: hisdev) Flyway 版本: 8.5.x (Spring Boot 2.7 管理) 编制日期: 2026-06-04
一、当前配置
| 配置项 | 值 | 说明 |
|---|---|---|
spring.flyway.enabled |
true |
启用 Flyway |
spring.flyway.baseline-on-migrate |
true |
首次启用时对现有表建基线 |
spring.flyway.baseline-version |
0 |
基线版本号 |
spring.flyway.locations |
classpath:db/migration |
迁移文件目录 |
spring.flyway.validate-on-migrate |
true |
执行前校验 |
迁移文件目录:
openhis-server-new/openhis-application/src/main/resources/db/migration/
当前状态:
V0 << Flyway Baseline >> (自动基线,覆盖现有所有表)
V1 baseline_marker (空标记文件)
二、文件命名规范
V{版本号}__{描述}.sql
| 规则 | 示例 | 说明 |
|---|---|---|
| 版本号必须递增 | V2, V3, V4 |
整数,不可重复 |
| 双下划线分隔 | V2__add_column.sql |
单下划线会被当作版本号一部分 |
| 描述用下划线连接 | V2__add_user_avatar.sql |
不要用空格或中文 |
| 大小写敏感 | V2__Add_Column.sql |
建议全小写 |
✅ 正确示例:
V2__add_practitioner_avatar.sql
V3__create_nurse_station_table.sql
V4__modify_encounter_diagnosis_index.sql
V5__add_yb_catalog_fields.sql
❌ 错误示例:
v2__add_column.sql # 版本号必须大写 V
V2 add column.sql # 缺少双下划线
V2__Add Column.sql # 描述中有空格
V2.1__add_column.sql # 不支持小数版本号
三、新增表(完整示例)
场景:新建一个「手术排班统计」表
Step 1:创建迁移文件
-- 文件:db/migration/V2__create_surgery_schedule_stats.sql
CREATE TABLE IF NOT EXISTS surgery_schedule_stats (
id BIGINT PRIMARY KEY,
schedule_id BIGINT NOT NULL COMMENT '排程ID',
doctor_code VARCHAR(64) COMMENT '医生编码',
surgery_count INT DEFAULT 0 COMMENT '手术数量',
total_duration INT DEFAULT 0 COMMENT '总时长(分钟)',
tenant_id INT DEFAULT 1 COMMENT '租户ID',
create_by VARCHAR(64) DEFAULT 'system' COMMENT '创建人',
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_by VARCHAR(64) COMMENT '更新人',
update_time TIMESTAMP COMMENT '更新时间',
valid_flag INT DEFAULT 1 COMMENT '有效标志 1=有效 0=无效'
);
COMMENT ON TABLE surgery_schedule_stats IS '手术排班统计表';
CREATE INDEX idx_surgery_stats_schedule ON surgery_schedule_stats(schedule_id);
CREATE INDEX idx_surgery_stats_tenant ON surgery_schedule_stats(tenant_id);
Step 2:启动应用
cd openhis-server-new
mvn clean package -DskipTests
java -jar openhis-application/target/openhis-application.jar --spring.profiles.active=dev --server.port=18082
Step 3:Flyway 自动执行
启动日志中会看到:
Flyway 迁移完成,执行了 1 个迁移
数据库中 flyway_schema_history 表新增一条记录:
installed_rank | version | description | type | success
---------------+---------+--------------------------+------+---------
3 | 2 | create surgery schedule.. | SQL | t
四、修改表结构(ALTER)
场景:给 practitioners 表加一个 phone 字段
-- 文件:db/migration/V3__add_practitioner_phone.sql
-- PostgreSQL
ALTER TABLE practitioner ADD COLUMN IF NOT EXISTS phone VARCHAR(32) COMMENT '联系电话';
场景:给表加索引
-- 文件:db/migration/V4__add_encounter_index.sql
CREATE INDEX IF NOT EXISTS idx_encounter_patient ON adm_encounter(patient_id);
CREATE INDEX IF NOT EXISTS idx_encounter_tenant ON adm_encounter(tenant_id);
场景:修改字段类型
-- 文件:db/migration/V5__extend_charge_item_code.sql
-- PostgreSQL: 修改 varchar 长度
ALTER TABLE adm_charge_item ALTER COLUMN charge_item_code TYPE VARCHAR(128);
五、多租户表迁移
项目有 50+ 张多租户表(tenant_id 字段),新增的多租户表需要:
-- 文件:db/migration/V6__create_clinic_referral_table.sql
CREATE TABLE IF NOT EXISTS clinic_referral (
id BIGINT PRIMARY KEY,
encounter_id BIGINT NOT NULL,
referral_reason TEXT,
tenant_id INT DEFAULT 1,
create_by VARCHAR(64) DEFAULT 'system',
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
valid_flag INT DEFAULT 1
);
COMMENT ON TABLE clinic_referral IS '转诊记录表';
然后在 MybatisPlusConfig.java 的 TENANT_TABLES 集合中添加表名:
private static final Set<String> TENANT_TABLES = new HashSet<>(Arrays.asList(
// ... 现有表 ...
"clinic_referral" // 新增
));
六、开发规范
必须遵守
| 规则 | 原因 |
|---|---|
| 不要修改已执行的迁移文件 | Flyway 会校验 checksum,修改后启动报错 |
| 版本号只能递增 | 不能回退版本号 |
| 每次只改一个表 | 方便回滚和排查 |
使用 IF NOT EXISTS |
防止重复执行报错 |
| 迁移文件加入 Git | 全团队共享迁移历史 |
推荐做法
| 做法 | 说明 |
|---|---|
| 先在测试环境验证 | 生产部署前确认迁移无误 |
| 一个迁移文件改一张表 | 便于追踪和回滚 |
| 文件名描述清晰 | V2__add_yb_catalog_drug_name.sql 比 V2__update.sql 好 |
| DDL 和 DML 分开 | 建表用 V2__create_xxx.sql,数据初始化用 V3__init_xxx_data.sql |
七、回滚方案
Flyway 不支持自动回滚,需要手动处理:
情况 1:迁移刚执行,还没提交代码
# 1. 删除 flyway_schema_history 中的记录
PGPASSWORD=Jchl1528 psql -h 192.168.110.252 -p 15432 -U postgresql -d postgresql \
-c "SET search_path TO hisdev; DELETE FROM flyway_schema_history WHERE version = '6';"
# 2. 手动撤销 DDL
PGPASSWORD=Jchl1528 psql -h 192.168.110.252 -p 15432 -U postgresql -d postgresql \
-c "SET search_path TO hisdev; DROP TABLE IF EXISTS clinic_referral;"
# 3. 删除迁移文件
rm openhis-server-new/openhis-application/src/main/resources/db/migration/V6__create_clinic_referral_table.sql
# 4. 重启应用
情况 2:已提交代码,需要紧急回滚
-- 手动执行逆向 SQL
DROP TABLE IF EXISTS clinic_referral;
DELETE FROM flyway_schema_history WHERE version = '6';
八、常用排查命令
-- 查看所有已执行的迁移
SELECT installed_rank, version, description, type, success, installed_on
FROM flyway_schema_history
ORDER BY installed_rank;
-- 查看是否有失败的迁移
SELECT * FROM flyway_schema_history WHERE success = false;
-- 查看当前最新版本
SELECT MAX(version) AS current_version FROM flyway_schema_history;
-- 手动标记某版本为成功(紧急修复用)
-- UPDATE flyway_schema_history SET success = true WHERE version = '6';
九、文件清单
openhis-server-new/openhis-application/src/main/resources/db/migration/
├── README.md # 使用说明
├── V1__baseline_marker.sql # 基线标记(空文件)
├── V2__xxx.sql # 你的第一个迁移
├── V3__xxx.sql # 第二个迁移
└── ...
十、注意事项(HIS 系统特有)
| 场景 | 处理方式 |
|---|---|
| 新功能开发 | 建表/改表时创建 V{n}__xxx.sql |
| 代码生成器生成的表 | 生成后把 DDL 放入迁移文件 |
| Flowable 工作流表 | 由 Flowable 自己管理,不要用 Flyway 管 |
| 多租户字段 | 新表必须加 tenant_id INT DEFAULT 1 |
| 逻辑删除字段 | 新表必须加 valid_flag INT DEFAULT 1 |
| 审计字段 | 新表必须加 create_by, create_time, update_by, update_time |
十一、PostgreSQL 常用 DDL 速查
建表模板
CREATE TABLE IF NOT EXISTS {表名} (
id BIGINT PRIMARY KEY,
{字段} {类型} {默认值} COMMENT '{说明}',
tenant_id INT DEFAULT 1 COMMENT '租户ID',
create_by VARCHAR(64) DEFAULT 'system' COMMENT '创建人',
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_by VARCHAR(64) COMMENT '更新人',
update_time TIMESTAMP COMMENT '更新时间',
valid_flag INT DEFAULT 1 COMMENT '有效标志 1=有效 0=无效'
);
COMMENT ON TABLE {表名} IS '{表说明}';
CREATE INDEX idx_{表名}_{字段} ON {表名}({字段});
加字段
ALTER TABLE {表名} ADD COLUMN IF NOT EXISTS {字段} {类型} COMMENT '{说明}';
加索引
CREATE INDEX IF NOT EXISTS idx_{表名}_{字段} ON {表名}({字段});
改字段类型
ALTER TABLE {表名} ALTER COLUMN {字段} TYPE {新类型};
删字段
ALTER TABLE {表名} DROP COLUMN IF EXISTS {字段};
删表
DROP TABLE IF EXISTS {表名};