关于 flyway
为使用 Flyway 管理数据库的开发者提供迁移参考,涵盖 SQL 与 Java 迁移、配置和常见故障处理。
主要功能
- 01版本化、可重复迁移命名
- 02Spring Boot 配置示例
- 03SQL 与 Java 迁移示例
- 04Maven 迁移与验证命令
- 05常见问题排查建议
适用场景
- 01规划数据库架构变更
- 02编写数据迁移脚本
- 03排查迁移校验或版本错误
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ claude-dev-suite/ claude-dev-suite --skill flyway安装后在 Claude Code 中输入/flyway确认已加载。
详细介绍
Flyway 数据库迁移
深入了解:使用
mcp__documentation__fetch_docs,并将技术指定为:flyway,获取全面的文档。
Maven 配置
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-database-postgresql</artifactId>
</dependency>
<plugin>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-maven-plugin</artifactId>
<version>${flyway.version}</version>
<configuration>
<url>jdbc:postgresql://localhost:5432/mydb</url>
<user>myuser</user>
<password>mypass</password>
</configuration>
</plugin>
应用配置
spring:
flyway:
enabled: true
baseline-on-migrate: true
locations: classpath:db/migration
validate-on-migrate: true
out-of-order: false
clean-disabled: true # Prevent clean in production!
迁移命名约定
V{version}__{description}.sql # Versioned migrations
U{version}__{description}.sql # Undo migrations (Teams/Enterprise)
R__{description}.sql # Repeatable migrations
示例:
V1__create_users_table.sqlV1.1__add_email_index.sqlV2__create_departments_table.sqlR__create_views.sql
初始架构迁移
-- V1__init_schema.sql
-- Users table
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
role VARCHAR(20) NOT NULL DEFAULT 'USER',
status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE',
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP,
created_by VARCHAR(255),
updated_by VARCHAR(255)
);
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_status ON users(status);
CREATE INDEX idx_users_role ON users(role);
-- Roles table
CREATE TABLE roles (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(50) NOT NULL UNIQUE,
description VARCHAR(255)
);
-- Insert default roles
INSERT INTO roles (name, description) VALUES
('ADMIN', 'System administrator'),
('MANAGER', 'Department manager'),
('USER', 'Regular user');
添加列的迁移
-- V2__add_phone_to_users.sql
ALTER TABLE users
ADD COLUMN phone VARCHAR(20);
CREATE INDEX idx_users_phone ON users(phone);
添加外键的迁移
-- V3__create_departments.sql
CREATE TABLE departments (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL UNIQUE,
code VARCHAR(10) NOT NULL UNIQUE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
ALTER TABLE users
ADD COLUMN department_id BIGINT;
ALTER TABLE users
ADD CONSTRAINT fk_users_department
FOREIGN KEY (department_id) REFERENCES departments(id);
CREATE INDEX idx_users_department ON users(department_id);
数据迁移
-- V4__migrate_legacy_data.sql
-- Update existing data
UPDATE users
SET role = 'USER'
WHERE role IS NULL;
-- Migrate data from legacy format
INSERT INTO departments (name, code)
SELECT DISTINCT department_name, UPPER(SUBSTRING(department_name, 1, 3))
FROM legacy_employees
WHERE department_name IS NOT NULL;
可重复迁移(视图)
-- R__create_user_summary_view.sql
DROP VIEW IF EXISTS user_summary;
CREATE VIEW user_summary AS
SELECT
u.id,
u.name,
u.email,
u.role,
u.status,
d.name AS department_name,
u.created_at
FROM users u
LEFT JOIN departments d ON u.department_id = d.id;
基于 Java 的迁移
@Component
public class V5__ComplexDataMigration implements JavaMigration {
@Override
public void migrate(Context context) throws Exception {
try (Statement stmt = context.getConnection().createStatement()) {
// Complex migration logic
ResultSet rs = stmt.executeQuery("SELECT id, data FROM legacy_table");
while (rs.next()) {
// Process and migrate data
}
}
}
@Override
public Integer getChecksum() { return null; }
@Override
public MigrationVersion getVersion() {
return MigrationVersion.fromVersion("5");
}
@Override
public String getDescription() {
return "Complex data migration";
}
}
用于日志记录的回调
@Component
public class FlywayCallback implements Callback {
private static final Logger log = LoggerFactory.getLogger(FlywayCallback.class);
@Override
public boolean supports(Event event, Context context) {
return event == Event.AFTER_EACH_MIGRATE ||
event == Event.AFTER_MIGRATE_ERROR;
}
@Override
public boolean canHandleInTransaction(Event event, Context context) {
return true;
}
@Override
public void handle(Event event, Context context) {
if (event == Event.AFTER_EACH_MIGRATE) {
MigrationInfo info = context.getMigrationInfo();
log.info("Migrated: {} - {} ({}ms)",
info.getVersion(),
info.getDescription(),
info.getExecutionTime());
} else if (event == Event.AFTER_MIGRATE_ERROR) {
log.error("Migration failed!");
}
}
@Override
public String getCallbackName() {
return "LoggingCallback";
}
}
Maven 命令
# Run migrations
mvn flyway:migrate
# Show migration info
mvn flyway:info
# Validate migrations
mvn flyway:validate
# Repair checksum mismatches
mvn flyway:repair
# Clean database (careful!)
mvn flyway:clean
# Baseline existing database
mvn flyway:baseline
最佳实践
| 实践 | 说明 |
|---|---|
| 切勿编辑已应用的迁移 | 改为创建新的迁移 |
| 测试迁移 | 在测试中使用 H2 |
| 迁移前备份 | 尤其是在生产环境中 |
| 使用事务 | 将 DDL 包装在事务中 |
| 版本控制 | 将迁移文件保存在 Git 中 |
| 命名约定 | 使用描述性名称 |
不应使用此技能的情况
- 通用迁移策略 - 使用
migrations技能了解相关概念 - Liquibase - 使用 Liquibase 专属文档
- Prisma 迁移 - 使用
prisma技能 - TypeORM 迁移 - 使用
typeorm技能
反模式
| 反模式 | 问题 | 解决方案 |
|---|---|---|
| 修改已应用的迁移 | 校验和验证失败 | 改为创建新的迁移 |
| 现有数据库未设置基线 | 迁移时失败 | 使用 baseline-on-migrate: true |
| 在 SQL 迁移中编写复杂逻辑 | 难以测试和调试 | 使用基于 Java 的迁移 |
| 忽略验证 | 各环境之间不一致 | 部署前始终进行验证 |
| 在生产环境中设置 clean-disabled: false | 有数据丢失风险 | 在生产环境中始终禁用 clean |
| 迁移顺序错乱 | 版本冲突 | 使用连续的版本号 |
快速故障排除
| 问题 | 诊断 | 修复方法 |
|---|---|---|
| 校验和不匹配 | 将文件与 flyway_schema_history 对比 | 使用 flyway repair 或创建新的迁移 |
| 迁移顺序错误 | 检查版本号 | 修正版本号,或设置 out-of-order: true |
| 迁移失败 | 检查 flyway_schema_history.success | 修复问题后重试 |
| 缺少迁移 | 检查 locations 配置 | 验证 classpath:db/migration 路径 |
| 基线冲突 | 检查 baseline-version | 设置正确的基线版本 |
参考文档
评论
支持 Markdown