# Flyway × Spring Boot:整合與使用指南(含版本管理觀念與實務流程)
Flyway 是一套 **資料庫版本控管(Schema Migration)** 工具。
你可以把它想成「DB 的 Git log + 部署腳本管理」:
* 每次 DB 結構變更(DDL)與必要資料(部分 DML)都用一個 **migration 檔**記錄
* Flyway 在啟動或部署時自動執行「尚未執行過」的 migration
* DB 內會有一張歷史表追蹤版本與 checksum,避免環境漂移
---
## 1. Flyway 解決什麼問題?
### 1.1 確保環境一致
同一套 migrations 在 dev/sit/uat/prod 跑出相同的 DB 結構(schema)。
### 1.2 變更可追蹤、可審核
每個版本變更都是一個檔案,可 code review、可回溯原因。
### 1.3 部署更可靠
部署時只要跑 Flyway,就能把 DB 從「目前版本」補到「最新版本」。
> Flyway 的設計偏向 **forward-only**:用新版本修正問題,而不是自由切換版本。
---
## 2. Flyway 版本管理核心概念
### 2.1 Migration 檔 = DB 版本
* `V1__init.sql`
* `V2__add_user_table.sql`
* `V3__add_index_on_user.sql`
命名規則(最常用的版本化 migration):
```
V<版本號>__<描述>.sql
```
### 2.2 Flyway 會在 DB 建一張歷史表
預設表名:`flyway_schema_history`
表裡會記錄:
* version / description
* installed_on
* checksum(避免你偷偷改已執行過的檔)
* success/failure
### 2.3 啟動時會不會每次都從 V1 跑?
* **第一次(全新 DB)**:會從 V1 跑到最新
* **之後每次啟動**:只跑「新加的版本」
---
## 3. 在 Spring Boot 中整合 Flyway
### 3.1 加入依賴
**Maven**
```xml
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
```
**Gradle**
```gradle
implementation 'org.flywaydb:flyway-core'
```
> 你的 JDBC driver(PostgreSQL/SQL Server/Oracle)通常本來就會有。
---
### 3.2 放置 migrations
預設路徑:
```
src/main/resources/db/migration
```
範例:
`src/main/resources/db/migration/V1__init.sql`
```sql
CREATE TABLE app_user (
id BIGINT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP NOT NULL
);
```
`src/main/resources/db/migration/V2__add_last_login_at.sql`
```sql
ALTER TABLE app_user ADD last_login_at TIMESTAMP NULL;
```
---
### 3.3 application.yml 基本設定
```yaml
spring:
flyway:
enabled: true
locations: classpath:db/migration
```
只要你有設定 `spring.datasource.*`,Spring Boot 會在啟動時自動跑 Flyway。
---
## 4. 常見進階設定(實務很常用)
### 4.1 舊資料庫接管(baseline)
如果你的 DB 已經有很多表,但以前不是 Flyway 管的,直接 migrate 可能被擋(schema 非空)。
```yaml
spring:
flyway:
baseline-on-migrate: true
baseline-version: 1
```
含義:
* 建立 `flyway_schema_history`
* 把「現在」當作 baseline 起點
* 後續從更高版本開始管理
---
### 4.2 指定只跑到某個版本(target)
你可以控制「最多 migrate 到哪個版本」,例如只跑到 V10:
```yaml
spring:
flyway:
target: 10
```
使用情境:
* 分階段上線(SIT/UAT 先追新,Prod 暫時停在某版)
---
### 4.3 Repeatable migrations(R__)
適合用在 View / Stored Procedure / Function 等會反覆更新的物件。
命名:
```
R__<描述>.sql
```
例:`R__create_or_replace_views.sql`
特性:
* 內容有變(checksum 變)→ 下次 migrate 會再跑
---
### 4.4 多 schema / 自訂歷史表
```yaml
spring:
flyway:
schemas: app_schema
table: flyway_schema_history
```
---
## 5. 正常團隊流程:誰要動 DB 該怎麼做?
### 5.1 DDL(建表/加欄位/索引/約束)
**一律新增 migration 檔**(不要手改正式 DB,也不要回頭改舊 migration)。
正確:新增 `V12__add_xxx.sql`
錯誤:改 `V1__init.sql` 或改已上線跑過的版本(checksum 會炸)。
---
### 5.2 DML(資料)要不要用 migration 管?
分兩類:
#### A) 測試資料 / 個人資料
* dev 手動增刪改 OK
* 但不保證別人環境也有
#### B) 系統必要資料(seed data)
例如:SYS_SETTING、權限角色、字典資料、必要參數
* 建議用 migration 管
* 否則會發生「我本機正常、SIT/UAT 缺資料爆掉」
範例:`V5__seed_sys_setting.sql`
```sql
INSERT INTO sys_setting (k, v) VALUES ('TOKEN_EXPIRE_MIN', '15');
```
---
## 6. 開發環境手動改 DB 會怎樣?(重要)
### 6.1 手動改 DDL 的風險
* 環境漂移:你本機 DB 跟別人不同
* 之後 migration 會撞到「欄位已存在」導致啟動失敗
**建議:DDL 不要手改,直接寫 migration。**
### 6.2 手動改資料的影響
* 測試資料:OK
* 必要 seed:最好寫進 migration
### 6.3 本機亂掉的最乾淨解法
* 砍掉 dev DB(或清空 schema)重建
* 讓 Flyway 從 V1 跑到最新
> 正式環境通常不允許 clean;dev 可以用「重建 DB」解決。
---
## 7. Flyway vs Hibernate ddl-auto
* `ddl-auto=create/update`:方便但不可控、難審核、容易飄移
* Flyway:可控、可審核、可重播,較適合正式環境
常見配置:
* 正式:Flyway 管 schema
* JPA:`ddl-auto=validate` 或 `none`
---
## 8. 實務建議(讓團隊跑得穩)
1. **所有 DDL 都走 migration**(禁止手改正式 DB)
2. migration 一旦進環境就不要改內容(避免 checksum 問題)
3. 需要回滾:
* 正式環境以備份還原最可靠
* 或新增修復 migration 往前修
4. View/SP 用 `R__*.sql` 管理
5. 必要的系統參數/字典資料用 seed migrations 管理
---
## 9. 快速檢查清單
### 9.1 基本必備
* [ ] **依賴已加入**:`org.flywaydb:flyway-core`
* [ ] **JDBC Driver 已加入**:PostgreSQL / SQL Server / Oracle 對應 driver(專案能正常連線 DB)
* [ ] **Datasource 設定正確**:`spring.datasource.url / username / password`(與環境一致)
* [ ] **migrations 路徑正確**:預設 `classpath:db/migration`(或 `spring.flyway.locations` 已正確指定)
* [ ] **檔名規則正確**:`V<版本>__<描述>.sql`(注意雙底線 `__`,版本號建議用遞增整數或語意化序列)
* [ ] **第一次啟動**:DB 會建立 `flyway_schema_history`(代表 Flyway 有在跑)
* [ ] **之後啟動**:只會執行「新增且尚未執行」的版本(不會每次從 V1 重跑)
### 9.2 團隊流程必備(避免環境漂移)
* [ ] **已跑過的 migration 檔不要修改**(避免 checksum 不一致導致啟動失敗)
* [ ] **所有 DDL 變更都用新增 migration**(不要手動改正式 DB;dev 也盡量不要手改 DDL)
* [ ] **PR/Code Review 檢查**:migration 是否可重播、是否破壞相容(例如欄位改型別、drop 欄位)
* [ ] **部署順序固定**:先跑 Flyway(migrate)→ 再部署應用程式(避免程式先上但 DB 還沒更新)
### 9.3 常見設定檢查(視情況)
* [ ] **既有 DB 接管**:若 schema 非空且之前不是 Flyway 管的,考慮:`spring.flyway.baseline-on-migrate=true`
* [ ] **分階段上線**:需要控制只跑到某版時,設定:`spring.flyway.target=<version>`
* [ ] **Repeatable**:View / SP / Function 等需要可重複更新者,用 `R__*.sql`
* [ ] **多 schema**:需要時設定 `spring.flyway.schemas=...`(並確認權限足夠)
* [ ] **禁止危險操作**:正式環境避免開 `clean`(若你有用到 Flyway 的 clean 機制)
### 9.4 與 JPA/Hibernate 搭配(建議)
* [ ] **正式環境**:`spring.jpa.hibernate.ddl-auto` 建議 `validate` 或 `none`(避免 Hibernate 直接改 schema)
* [ ] **開發環境**:仍建議 Flyway 管 schema;`ddl-auto` 只用 `validate` 輔助檢查(避免 update 自動改表造成不可控差異)
* [ ] **啟動順序**:確保 Flyway 在 JPA 初始化前完成 migrate(Spring Boot 預設通常會幫你處理)
---
如果你願意貼你的 `application.yml`(datasource + flyway + jpa),我可以幫你對照這份清單逐項檢查哪裡少了/可能踩雷。