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(推荐)

xml 复制代码
<!-- 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)

kotlin 复制代码
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 切换):

yaml 复制代码
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/demo?useSSL=false&serverTimezone=UTC
    username: demo
    password: demo
    driver-class-name: com.mysql.cj.jdbc.Driver

  flyway:
    enabled: true
    baseline-on-migrate: true   # 当已有旧库时建议开启 baseline
    locations: classpath:db/migration
    placeholders:
      app_name: demo_app
    placeholder-replacement: true
    # 如果使用多个 schema,可以指定:
    # schemas: schema1, schema2
    # 如果要允许乱序迁移:
    # out-of-order: false

---

# Oracle profile 示例(application-oracle.yml)
spring:
  datasource:
    url: jdbc:oracle:thin:@//oracle-host:1521/ORCLCDB
    username: APP_USER
    password: secret
    driver-class-name: oracle.jdbc.OracleDriver

  flyway:
    enabled: true
    baseline-on-migrate: true
    locations: classpath:db/migration/oracle,classpath:db/migration/common
    schemas: APP_USER
    placeholder-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

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)

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. 多数据库支持策略

  1. 目录分离:db/migration/common(所有 DB 公共 SQL) + db/migration/mysql + db/migration/oracle。在 application-<profile>.yml 中设置 spring.flyway.locations 对应的目录顺序。

  2. 使用占位符(placeholders)来替换数据库差异处:

    • 例如 {``{placeholder}} 在 SQL 中可用来替换表名/引擎配置等。
  3. 避免数据库特性耦合:如果功能能用 ANSI SQL 完成,应优先使用 ANSI SQL。

示例目录结构:

复制代码
src/main/resources/db/migration/
  common/
    V1__create_common_tables.sql
  mysql/
    V2__mysql_specific_index.sql
  oracle/
    V2__oracle_specific_seq.sql

application-oracle.yml

yaml 复制代码
spring:
  flyway:
    locations: classpath:db/migration/common,classpath:db/migration/oracle

6. Docker Compose 示例(本地测试)

yaml 复制代码
version: '3.8'
services:
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: demo
      MYSQL_USER: demo
      MYSQL_PASSWORD: demo
    ports:
      - "3306:3306"

  oracle:
    image: gvenzl/oracle-xe:21-slim
    environment:
      ORACLE_PASSWORD: oracle
    ports:
      - "1521:1521"

注意:Oracle 镜像体积较大,且授权问题需要注意。用于临时本地开发或 CI 测试通常使用开源镜像(如 gvenzl)或官方提供的方法。


7. Flyway CLI / Maven 插件(可选)

如果你想在 CI 中单独运行 Flyway,而不是依赖应用启动,可以使用 Flyway Maven 插件或 Flyway CLI。

Maven 插件示例(pom.xml 片段)

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)中,不要硬编码在源码中。

回滚策略

Flyway 不提供自动回滚。对于必须可回滚的变更:

  1. 编写补偿迁移(V{n+1}__undo_change.sql)并在回滚窗口中手动执行
    1. 在部署前对 DB 做备份(快照/导出)

迁移的原子性与事务

  • MySQL:大多数 DDL 是隐式提交,注意事务边界。
  • Oracle:DDL 会自动提交,且在某些情况下不支持回滚。
  • 结论:不要依赖数据库在 DDL 上提供可回滚事务,设计迁移时要小心分步执行并测试。

测试策略

  • 在本地与 CI 中使用干净 DB 对迁移进行测试(包括顺序、乱序、重复运行)。
  • 在 PR 合并前运行 flyway:validateflyway:info 检查校验和冲突。

命名与注释

  • 迁移文件名与内容必须保持可读性,描述性强,便于回溯问题来源。

校验和变更

  • 如果你修改了已经执行过的迁移文件,会导致 checksum 不匹配。常见解决方案:

    • 不修改已发布的 migration,改为新增 migration 来修正问题。
    • 如确需修改,在受控环境使用 flyway repair 修复校验和(谨慎)。

10. 进阶:Java-based Migrations 与 回调

Java Migration 示例

java 复制代码
// 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 {
    @Override
    public 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. 示例:完整流程(从新项目到上线)

  1. 新建 Spring Boot 项目,添加 flyway-core 与 JDBC driver 依赖。
  2. src/main/resources/db/migration/common 写入 V1__init.sql
  3. application.yml 配置数据源与 spring.flyway.locations
  4. 本地启动数据库(Docker Compose),本地 mvn spring-boot:run 验证迁移是否自动运行并成功。
  5. 在 CI 中加入 flyway:migrate 步骤(或直接在部署前由管理员运行迁移)。
  6. 在 Staging 环境验证应用与 DB 的一致性。
  7. 正式生产环境:先在 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
相关推荐
隔壁阿布都2 小时前
Spring Boot中的Optional如何使用
开发语言·spring boot·python
Mintopia2 小时前
🧠 Next.js × GraphQL Yoga × GraphiQL:交互式智能之门
前端·后端·全栈
TDengine (老段)2 小时前
TDengine 数学函数 CRC32 用户手册
java·大数据·数据库·sql·时序数据库·tdengine·1024程序员节
林太白2 小时前
rust16-职位管理模块
后端·rust
心随雨下3 小时前
Tomcat日志配置与优化指南
java·服务器·tomcat
Kapaseker3 小时前
Java 25 中值得关注的新特性
java
wljt3 小时前
Linux 常用命令速查手册(Java开发版)
java·linux·python
撩得Android一次心动3 小时前
Android 四大组件——BroadcastReceiver(广播)
android·java·android 四大组件
canonical_entropy3 小时前
Nop平台到底有什么独特之处,它能用在什么场景?
java·后端·领域驱动设计