Spring Boot 3 + Flyway 全流程教程
Spring Boot 3 + Flyway 全流程教程(支持 MySQL / Oracle)
目标:从零到上线,完整覆盖 Flyway 在 Spring Boot 3 中的集成、配置、迁移文件书写、MySQL/Oracle 兼容要点、CI/CD 实践、常见问题与排查。
1. 总览(架构与流程)
A[开发者编写迁移脚本(SQL/Java)] --> B[版本控制系统(Git)]B --> C[CI:构建 + 运行 Flyway (可选)]C --> D[部署到环境(容器/虚拟机)]D --> E[Spring Boot 应用启动]E --> F[Flyway 自动执行迁移]F --> G[数据库模式更新(MySQL/Oracle)]G --> H[应用开始提供新功能]
说明:Flyway 的典型位置可以是【应用启动时自动执行】或【CI/CD 中在部署前单独执行】。两者都常见,生产环境建议在 CI/CD 中先执行(或确保应用用户有足够权限)。
2. 依赖与项目配置
2.1 Maven(推荐)
<!-- pom.xml 片段 -->
<dependencies><!-- Spring Boot JDBC starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><!-- Flyway core --><dependency><groupId>org.flywaydb</groupId><artifactId>flyway-core</artifactId></dependency><!-- MySQL 驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-j</artifactId></dependency><!-- Oracle 驱动(注意许可证与版本) --><!-- 常用镜像/仓库提供 ojdbc 或使用官方 Maven 坐标 --><dependency><groupId>com.oracle.database.jdbc</groupId><artifactId>ojdbc8</artifactId><scope>runtime</scope></dependency>
</dependencies>
2.2 Gradle (Kotlin DSL)
dependencies {implementation("org.springframework.boot:spring-boot-starter-jdbc")implementation("org.flywaydb:flyway-core")runtimeOnly("mysql:mysql-connector-j")runtimeOnly("com.oracle.database.jdbc:ojdbc8")
}
3. Spring Boot 自动配置(application.yml)
示例 application.yml,包含 MySQL 与 Oracle 的可切换配置(通过 profile 切换):
spring:datasource:url: jdbc:mysql://localhost:3306/demo?useSSL=false&serverTimezone=UTCusername: demopassword: demodriver-class-name: com.mysql.cj.jdbc.Driverflyway:enabled: truebaseline-on-migrate: true # 当已有旧库时建议开启 baselinelocations: classpath:db/migrationplaceholders:app_name: demo_appplaceholder-replacement: true# 如果使用多个 schema,可以指定:# schemas: schema1, schema2# 如果要允许乱序迁移:# out-of-order: false---# Oracle profile 示例(application-oracle.yml)
spring:datasource:url: jdbc:oracle:thin:@//oracle-host:1521/ORCLCDBusername: APP_USERpassword: secretdriver-class-name: oracle.jdbc.OracleDriverflyway:enabled: truebaseline-on-migrate: truelocations: classpath:db/migration/oracle,classpath:db/migration/commonschemas: APP_USERplaceholder-replacement: true
注意:
locations可以按数据库类型分目录组织(如db/migration/common+db/migration/oracle),这样可以放置数据库专用 SQL。
4. 迁移脚本的书写规范与示例
4.1 文件命名(Flyway 默认约定)
- 版本化迁移:
V{version}__{description}.sql,例如V1__init_schema.sql。 - 可重复迁移:
R__{description}.sql,用于视图或数据修正类操作。 - 推荐使用下划线或双下划线分隔描述,避免空格。
- 版本号使用点分或下划线,如
1,1.1,20251001_01。
4.2 简单初始化(MySQL)
src/main/resources/db/migration/V1__create_tables.sql
-- MySQL 示例 (V1__create_tables.sql)
CREATE TABLE IF NOT EXISTS users (id BIGINT AUTO_INCREMENT PRIMARY KEY,username VARCHAR(100) NOT NULL UNIQUE,email VARCHAR(255),created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;CREATE TABLE IF NOT EXISTS roles (id INT AUTO_INCREMENT PRIMARY KEY,name VARCHAR(50) NOT NULL UNIQUE
) ENGINE=InnoDB;CREATE TABLE IF NOT EXISTS user_roles (user_id BIGINT NOT NULL,role_id INT NOT NULL,PRIMARY KEY (user_id, role_id),FOREIGN KEY (user_id) REFERENCES users(id),FOREIGN KEY (role_id) REFERENCES roles(id)
);
4.3 Oracle 特定示例(V1__create_tables_oracle.sql)
-- Oracle 示例
CREATE TABLE users (id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,username VARCHAR2(100) NOT NULL,email VARCHAR2(255),created_at TIMESTAMP DEFAULT SYSTIMESTAMP
);CREATE UNIQUE INDEX ux_users_username ON users(username);CREATE TABLE roles (id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,name VARCHAR2(50) NOT NULL
);CREATE TABLE user_roles (user_id NUMBER NOT NULL,role_id NUMBER NOT NULL,CONSTRAINT pk_user_roles PRIMARY KEY (user_id, role_id),CONSTRAINT fk_ur_user FOREIGN KEY (user_id) REFERENCES users(id),CONSTRAINT fk_ur_role FOREIGN KEY (role_id) REFERENCES roles(id)
);
Oracle 注意点:
- Oracle 在 DDL 时会自动提交,且事务语义与 MySQL 不完全相同;因此要谨慎使用
BEGIN/COMMIT等。- 建议使用
IDENTITY(12c+)或序列+触发器来实现自增。
4.4 升级与可重复脚本
- 当需要同步视图、存储过程、函数或数据修复,优先使用
R__(repeatable)脚本。 - 使用
V前缀做 schema/结构变更,并保持可追溯的版本历史。
5. 多数据库支持策略
-
目录分离:
db/migration/common(所有 DB 公共 SQL) +db/migration/mysql+db/migration/oracle。在application-<profile>.yml中设置spring.flyway.locations对应的目录顺序。 -
使用占位符(placeholders)来替换数据库差异处:
- 例如
{{placeholder}}在 SQL 中可用来替换表名/引擎配置等。
- 例如
-
避免数据库特性耦合:如果功能能用 ANSI SQL 完成,应优先使用 ANSI SQL。
示例目录结构:
src/main/resources/db/migration/common/V1__create_common_tables.sqlmysql/V2__mysql_specific_index.sqloracle/V2__oracle_specific_seq.sql
在 application-oracle.yml:
spring:flyway:locations: classpath:db/migration/common,classpath:db/migration/oracle
6. Docker Compose 示例(本地测试)
version: '3.8'
services:mysql:image: mysql:8.0environment:MYSQL_ROOT_PASSWORD: rootMYSQL_DATABASE: demoMYSQL_USER: demoMYSQL_PASSWORD: demoports:- "3306:3306"oracle:image: gvenzl/oracle-xe:21-slimenvironment:ORACLE_PASSWORD: oracleports:- "1521:1521"
注意:Oracle 镜像体积较大,且授权问题需要注意。用于临时本地开发或 CI 测试通常使用开源镜像(如 gvenzl)或官方提供的方法。
7. Flyway CLI / Maven 插件(可选)
如果你想在 CI 中单独运行 Flyway,而不是依赖应用启动,可以使用 Flyway Maven 插件或 Flyway CLI。
Maven 插件示例(pom.xml 片段)
<build><plugins><plugin><groupId>org.flywaydb</groupId><artifactId>flyway-maven-plugin</artifactId><version>9.0.0</version> <!-- 请根据当前最新版本调整 --><configuration><url>${db.url}</url><user>${db.user}</user><password>${db.password}</password><schemas>${db.schema}</schemas><locations>classpath:db/migration</locations></configuration></plugin></plugins>
</build>
CI 步骤(示例):
- Checkout
- Build
mvn -Ddb.url=... -Ddb.user=... -Ddb.password=... flyway:migrate- 部署应用
8. 常见操作与维护命令
baseline-on-migrate=true:当已有旧表(未使用 Flyway 管理)时进行基线化,避免重复执行历史 SQL。flyway.migrate():执行迁移(自动在 Spring Boot 应用启动时触发)。flyway.info():查看迁移状态。flyway.repair():修复校验和或删除错误的历史记录(谨慎使用)。flyway.clean():清空数据库(仅用于开发/测试!生产禁用)。
9. 注意事项与最佳实践
权限与安全
- 生产环境中禁止应用使用
DBA或超级管理员权限去执行迁移。建议为 Flyway 创建专用数据库用户,拥有创建表/索引/序列等最小权限。 - 将数据库凭证保存在秘密管理器(Vault、K8s Secret、CI secret)中,不要硬编码在源码中。
回滚策略
迁移的原子性与事务
- MySQL:大多数 DDL 是隐式提交,注意事务边界。
- Oracle:DDL 会自动提交,且在某些情况下不支持回滚。
- 结论:不要依赖数据库在 DDL 上提供可回滚事务,设计迁移时要小心分步执行并测试。
测试策略
- 在本地与 CI 中使用干净 DB 对迁移进行测试(包括顺序、乱序、重复运行)。
- 在 PR 合并前运行
flyway:validate或flyway:info检查校验和冲突。
命名与注释
- 迁移文件名与内容必须保持可读性,描述性强,便于回溯问题来源。
校验和变更
-
如果你修改了已经执行过的迁移文件,会导致 checksum 不匹配。常见解决方案:
- 不修改已发布的 migration,改为新增 migration 来修正问题。
- 如确需修改,在受控环境使用
flyway repair修复校验和(谨慎)。
10. 进阶:Java-based Migrations 与 回调
Java Migration 示例
// src/main/java/com/example/migration/V2__add_sample_data.java
import org.flywaydb.core.api.migration.BaseJavaMigration;
import org.flywaydb.core.api.migration.Context;public class V2__add_sample_data extends BaseJavaMigration {@Overridepublic void migrate(Context context) throws Exception {try (var stmt = context.getConnection().createStatement()) {stmt.executeUpdate("INSERT INTO roles(name) VALUES('ROLE_USER')");}}
}
回调(callbacks)
- 可以编写
beforeMigrate,afterMigrate等回调脚本(db/migration旁的callbacks目录或以*__beforeMigrate.sql命名),用于启动前检查或迁移后初始化数据。
11. 示例:完整流程(从新项目到上线)
- 新建 Spring Boot 项目,添加
flyway-core与 JDBC driver 依赖。 - 在
src/main/resources/db/migration/common写入V1__init.sql。 - 在
application.yml配置数据源与spring.flyway.locations。 - 本地启动数据库(Docker Compose),本地
mvn spring-boot:run验证迁移是否自动运行并成功。 - 在 CI 中加入
flyway:migrate步骤(或直接在部署前由管理员运行迁移)。 - 在 Staging 环境验证应用与 DB 的一致性。
- 正式生产环境:先在 DB 备份/快照后执行迁移,再部署应用。
12. 常见故障排查清单
- 迁移未运行:确认
spring.flyway.enabled=true,并检查spring.datasource是否正确。 - checksum 不匹配:不要修改已执行的 migration;若确实需要变更,使用
flyway repair(仅在受控场景)。 - 权限不足:查看 Flyway 用户是否有
CREATE TABLE/ALTER等权限。 - 数据库方言差异导致失败:把数据库专有 SQL 放在 db/migration/ 下,并在配置中按 profile 加载。
- 重复运行但未生效:检查 migration 文件名是否以
R__(repeatable)或V加版本号为准。
13. 附录:快速参考表
| 场景 | 推荐做法 |
|---|---|
| 旧数据库接入 Flyway | 使用 baseline-on-migrate: true 并设置 baselineVersion |
| 需要数据库可回滚 | 使用备份 + 补偿迁移,不依赖自动回滚 |
| 多 DB 支持 | 目录分层 + profiles + placeholders |
| CI 中执行迁移 | 使用 flyway-maven-plugin 或 flyway CLI |
