- 新增 docs/FLYWAY_USAGE_GUIDE.md (326行完整使用指南) - AGENTS.md 新增铁律: 数据库变更必须通过 Flyway 迁移 - 禁止直接执行 DDL 而不创建迁移文件 - 禁止修改已执行的迁移文件 - 新表必须包含租户/审计/软删除字段
327 lines
9.1 KiB
Markdown
327 lines
9.1 KiB
Markdown
# 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:创建迁移文件**
|
||
|
||
```sql
|
||
-- 文件: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:启动应用**
|
||
|
||
```bash
|
||
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 字段
|
||
|
||
```sql
|
||
-- 文件:db/migration/V3__add_practitioner_phone.sql
|
||
|
||
-- PostgreSQL
|
||
ALTER TABLE practitioner ADD COLUMN IF NOT EXISTS phone VARCHAR(32) COMMENT '联系电话';
|
||
```
|
||
|
||
### 场景:给表加索引
|
||
|
||
```sql
|
||
-- 文件: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);
|
||
```
|
||
|
||
### 场景:修改字段类型
|
||
|
||
```sql
|
||
-- 文件: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` 字段),新增的多租户表需要:
|
||
|
||
```sql
|
||
-- 文件: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` 集合中添加表名:
|
||
|
||
```java
|
||
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:迁移刚执行,还没提交代码
|
||
|
||
```bash
|
||
# 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
|
||
-- 手动执行逆向 SQL
|
||
DROP TABLE IF EXISTS clinic_referral;
|
||
DELETE FROM flyway_schema_history WHERE version = '6';
|
||
```
|
||
|
||
---
|
||
|
||
## 八、常用排查命令
|
||
|
||
```sql
|
||
-- 查看所有已执行的迁移
|
||
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 速查
|
||
|
||
### 建表模板
|
||
|
||
```sql
|
||
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 {表名}({字段});
|
||
```
|
||
|
||
### 加字段
|
||
|
||
```sql
|
||
ALTER TABLE {表名} ADD COLUMN IF NOT EXISTS {字段} {类型} COMMENT '{说明}';
|
||
```
|
||
|
||
### 加索引
|
||
|
||
```sql
|
||
CREATE INDEX IF NOT EXISTS idx_{表名}_{字段} ON {表名}({字段});
|
||
```
|
||
|
||
### 改字段类型
|
||
|
||
```sql
|
||
ALTER TABLE {表名} ALTER COLUMN {字段} TYPE {新类型};
|
||
```
|
||
|
||
### 删字段
|
||
|
||
```sql
|
||
ALTER TABLE {表名} DROP COLUMN IF EXISTS {字段};
|
||
```
|
||
|
||
### 删表
|
||
|
||
```sql
|
||
DROP TABLE IF EXISTS {表名};
|
||
```
|