資料庫遷移
reference server 提供一套由人類明示使用、與既有開發/切版/部署 skills 解耦的 migration
能力。它由 Flyway versioned SQL、離線 Entity Diff、Spring Boot 外部設定與可選的品質驗證組成。
/release-app、/impl、/deploy 等既有 skills 不會建立、接受、執行或驗證 migration。
環境策略
環境不是由 framework 猜測,而是由團隊或 operator 透過
{appHome}/conf/{serviceName}.yml 明示。reference 初始建議如下:
| 環境 | JPA ddl-auto | Flyway | 用途 |
|---|---|---|---|
| dev / test(含 SIT、UAT) | update | 關閉 | 快速跟隨尚未穩定的 Entity |
| production | validate | application startup 啟用 | 先套用版本化 SQL,再驗證 Entity mapping |
這些是初始值,不是強制環境分類。/env-config 只協助產生普通 Spring Boot 設定;要不要採用、何時
啟動與如何協調節點,都由人類決定。classpath 保持安全的 spring.flyway.enabled: false,所以沒有外部
production 設定時不會意外 migration。
production 外部設定的核心形狀為:
spring:
jpa:
hibernate:
ddl-auto: validate
flyway:
enabled: true
baseline-on-migrate: false
locations: classpath:db/migration/mysql
Spring Boot 會在建立 JPA EntityManagerFactory 前執行 Flyway,因此啟動順序是
Flyway migrate → Hibernate validate → application ready。application runtime 需要足以執行該批 DDL
的權限;若安全政策不允許,就由 operator 改採外部 runner,但這不是 /deploy 的隱含步驟。
多節點同時啟動時 Flyway ledger 能避免同一 migration 被重複套用,但長時間、鎖表或不可逆變更仍應安排 維護窗口與節點啟動順序。
人工 Entity Diff
Entity 在日常開發期間可以持續因功能、bug fix 與 refactoring 改變,不必每次變動都建立 SQL。當團隊 決定比較兩個發行版本時,在各 server 模組由人類執行:
python3 .claude/skills/entity-diff/scripts/entity_diff.py status
python3 .claude/skills/entity-diff/scripts/entity_diff.py draft
Entity Diff 比較:
db/entity-model/mysql/entity-model.json中上一個由人類接受的 release snapshot;- 目前程式碼經 Hibernate 解析後的 effective relational metadata。
它不建立 JDBC connection、不讀任何環境資料庫。因此結果表達的是「兩個 Entity model 版本之間的 差異」,不是某個 SIT/UAT/production database 的現況。rename、drop、型別縮窄、nullability、非 JPA tables、seed、reference data 與 backfill 都需要人類判斷並完成 SQL。
草稿只寫入 build/reports/entity-diff/。完成並審閱 Flyway SQL 後,再手動接受新的 release baseline:
python3 .claude/skills/entity-diff/scripts/entity_diff.py accept \
--release 2026.2.0 \
--migration src/main/resources/db/migration/mysql/V6__add_example.sql
首次導入使用 bootstrap;若只需更正尚未產生 metadata drift 的 release 標籤,使用 relabel。目標
release 是操作者輸入,不從 /release-app 推導,也不由 /release-app 驗證或觸發。
Flyway SQL
MySQL versioned migration 放在:
src/main/resources/db/migration/mysql/V{N}__{snake_case}.sql
V1__baseline.sql建立 fresh production schema;後續版本只向前追加。- 一旦某版本被任何環境套用,就不可修改、重用或重新編號;修正使用新版本。
- data migration、非 JPA table 與 constraint 不會由 Entity Diff 自動推導,必須人工納入。
- 大型 backfill 宜做成可 checkpoint、可停止續跑的獨立操作;不要綁在 application startup 的單一交易中。
- rename/drop、NOT NULL、UNIQUE/FK、型別縮窄與資料改義應先處理相容性和資料衝突,再收縮 schema。
既有非空資料庫
如果資料庫已由人工建立,而且 schema 符合預定的 Flyway 版本,application-startup Flyway 可以正常使用,
但第一次必須先做明示 adoption。baseline-on-migrate 維持 false,避免啟動時猜錯版本:
- 備份並盤點現有 schema、constraints、indexes、資料量與 writers。
- 在隔離 clone 驗證它實際符合哪個 migration version;差異先以新的 reconciliation migration 處理。
- owner 核准後,以 Flyway
baseline明示 adoption version。 - 再啟動 application 或執行
migrate,套用 adoption version 之後的 migrations。 - 檢查
validate、pending migrations、資料 invariant 與 JPA validation。
baseline record 只表示「從這個版本開始由 Flyway 管理」,不代表早期 scripts 曾經執行;adoption version
也不一定是 1。不得手改 flyway_schema_history。
人工品質驗證
需要 MySQL dialect 證據時,可由人類或品質管制程序明示執行:
./gradlew migrationEvidenceTest
它使用 Testcontainers 驗證 fresh replay、JPA validation 與既有版本升級情境;一般 check、Entity Diff、
切版與部署不會自動呼叫。Docker 不可用時此 task 會直接失敗。
Flyway ledger 可用 info/validate 診斷,或唯讀查詢:
SELECT installed_rank, version, description, type, checksum, installed_on, success
FROM flyway_schema_history
ORDER BY installed_rank;