跳至主要内容

資料庫遷移

本文檔說明如何使用 Flyway 管理花店系統的資料庫 Schema 變更。

Flyway 簡介

Flyway 是一個資料庫遷移工具,用於版本控制資料庫 Schema 變更。

優點

  • 版本控制:每個遷移腳本都有版本號
  • 可重複執行:確保所有環境的資料庫結構一致
  • 追蹤歷史:記錄已執行的遷移

遷移腳本位置

app-server/src/main/resources/db/migration/
├── V1__create_tenant_table.sql
├── V2__create_auth_tables.sql
├── V3__create_product_table.sql
├── V4__create_order_tables.sql
├── V5__create_customer_table.sql
└── ...

命名規範

遷移腳本必須遵循以下命名規範:

V{版本號}__{描述}.sql
  • V:表示版本化遷移(Versioned Migration)
  • 版本號:整數,依序遞增(1, 2, 3...)
  • __:雙底線分隔符
  • 描述:使用底線分隔的描述文字

範例

V1__create_tenant_table.sql
V2__create_auth_tables.sql
V3__add_product_sku_column.sql

撰寫遷移腳本

建立資料表

-- V3__create_product_table.sql
CREATE TABLE product (
id VARCHAR(36) PRIMARY KEY,
tenant_id VARCHAR(36) NOT NULL,
sku VARCHAR(30) NOT NULL,
name VARCHAR(200) NOT NULL,
category_code VARCHAR(36) NOT NULL,
description VARCHAR(2000),
price DECIMAL(10, 2) NOT NULL,
cost DECIMAL(10, 2),
stock INTEGER NOT NULL DEFAULT 0,
low_stock_threshold INTEGER NOT NULL DEFAULT 5,
stock_unit VARCHAR(20) NOT NULL DEFAULT '個',
status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE',
featured BOOLEAN NOT NULL DEFAULT FALSE,
new_arrival BOOLEAN NOT NULL DEFAULT FALSE,
-- 審計欄位(與 AuditableTenantEntity 一致)
created_by VARCHAR(100),
created_date TIMESTAMP,
last_modified_by VARCHAR(100),
last_modified_date TIMESTAMP,

CONSTRAINT fk_product_tenant FOREIGN KEY (tenant_id) REFERENCES tenant(id),
CONSTRAINT uk_product_tenant_sku UNIQUE (tenant_id, sku)
);

CREATE INDEX idx_product_tenant ON product(tenant_id);
CREATE INDEX idx_product_sku ON product(sku);
CREATE INDEX idx_product_category ON product(category_code);
CREATE INDEX idx_product_status ON product(status);

審計欄位說明

  • created_by / created_date:建立者與建立時間
  • last_modified_by / last_modified_date:最後修改者與修改時間
  • 這些欄位由 AuditableTenantEntity 基類透過 Spring Data JPA Auditing 自動管理

新增欄位

-- V10__add_product_featured_column.sql
ALTER TABLE product ADD COLUMN featured BOOLEAN NOT NULL DEFAULT FALSE;
ALTER TABLE product ADD COLUMN new_arrival BOOLEAN NOT NULL DEFAULT FALSE;

建立索引

-- V11__add_product_indexes.sql
CREATE INDEX idx_product_featured ON product(featured) WHERE featured = TRUE;
CREATE INDEX idx_product_new_arrival ON product(new_arrival) WHERE new_arrival = TRUE;

執行遷移

自動執行

Spring Boot 啟動時會自動執行未套用的遷移:

# application.yml
spring:
flyway:
enabled: true
locations: classpath:db/migration

手動執行

# 執行遷移
./gradlew flywayMigrate

# 查看遷移狀態
./gradlew flywayInfo

# 驗證遷移
./gradlew flywayValidate

最佳實踐

1. 遷移腳本不可修改

一旦遷移腳本已在任何環境執行,就不應該修改。如需變更,請建立新的遷移腳本。

2. 保持向後相容

新增欄位時使用 DEFAULT 值或允許 NULL

-- 好:有預設值
ALTER TABLE product ADD COLUMN discount DECIMAL(5, 2) DEFAULT 0;

-- 好:允許 NULL
ALTER TABLE product ADD COLUMN description VARCHAR(2000);

-- 避免:NOT NULL 且無預設值(會導致現有資料失敗)
ALTER TABLE product ADD COLUMN required_field VARCHAR(100) NOT NULL;

3. 大型資料表變更

對於大型資料表,考慮分批處理:

-- 分批更新資料
UPDATE product SET status = 'ACTIVE' WHERE status IS NULL LIMIT 1000;

4. 測試遷移

在本地或測試環境驗證遷移腳本:

# 重建資料庫並執行所有遷移
./gradlew flywayClean flywayMigrate

常見問題

遷移失敗

如果遷移失敗,Flyway 會記錄失敗狀態。修復後需要:

# 修復失敗的遷移記錄
./gradlew flywayRepair

版本衝突

多人開發時可能出現版本號衝突,建議:

  1. 使用較大的版本號間隔
  2. 合併前檢查 flyway_schema_history
  3. 協調團隊成員避免衝突

下一步