Files
his/docs/FLYWAY_USAGE_GUIDE.md
华佗 56ec755cf3 docs: 新增 Flyway 使用指南 + 铁律
- 新增 docs/FLYWAY_USAGE_GUIDE.md (326行完整使用指南)
- AGENTS.md 新增铁律: 数据库变更必须通过 Flyway 迁移
  - 禁止直接执行 DDL 而不创建迁移文件
  - 禁止修改已执行的迁移文件
  - 新表必须包含租户/审计/软删除字段
2026-06-04 14:46:37 +08:00

9.1 KiB
Raw Blame History

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 3Flyway 自动执行

启动日志中会看到:

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.javaTENANT_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.sqlV2__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 {表名};