首页 / Spring Boot 入门教程 / 数据库迁移:Flyway

Spring Boot 入门教程

数据库迁移:Flyway

本教程共 48 篇 · 第 29 篇 · 更新于 2026-08-13 · 约 6 分钟阅读

Spring BootFlyway数据库迁移版本控制SQL脚本schema管理迁移

本节目标:理解为什么需要数据库迁移,会用 Flyway 管理版本化 SQL 脚本,并知道它和 schema.sql 的分工。

表结构怎么管:一个经典难题

代码有 Git 管版本,数据库结构没有。张三本地加了张表,李四不知道;线上库加了列,测试库还是旧的。最后每个环境的结构都不一样,代码一跑就炸。

手敲 SQL 改表更危险:今天改过的表,明天忘了在哪台机器执行过,再执行一遍就报错。更糟的是,线上库是团队共用资产,谁改过什么根本对不上账。

数据库迁移工具解决这个问题:所有结构变更写成脚本,按版本顺序执行,每个脚本只执行一次。结构变更从此有据可查、有版本可回,和代码一样进仓库。

Flyway 是什么

Flyway 是 Java 生态最流行的迁移工具,理念直白:把数据库当代码一样做版本控制。

它维护一张历史表(默认叫 flyway_schema_history),记录哪些脚本执行过。每次启动时扫描脚本目录,发现没执行过的新脚本就按版本号顺序执行,执行过的跳过。

工作流程拆开就三步:

  1. 扫描 db/migration 目录,列出所有脚本。
  2. 读历史表,对比出没执行过的脚本。
  3. 按版本号从小到大逐个执行,并写入历史表。

Spring Boot 对 Flyway 的集成是开箱即用的:依赖一加,配置一写,启动时自动迁移。

引入依赖

<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-core</artifactId>
</dependency>

用 MySQL 还要加数据库支持模块:

<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-mysql</artifactId>
</dependency>
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>
Note

不同数据库要不同的 flyway 模块。PostgreSQL 加 flyway-database-postgresql,SQL Server 加对应模块。只加 flyway-core 连接不上 MySQL,这是常见坑。

依赖不用写版本号,Spring Boot 的依赖管理(BOM)已经锁好兼容版本。

第一个迁移脚本

脚本放在 src/main/resources/db/migration/ 目录。命名有严格规范:

V1__create_book_table.sql
V2__add_price_column.sql

格式是 V<版本号>__<描述>.sql:大写 V、版本号、两个下划线、描述。版本号按数字递增,Flyway 按它排序执行。

V1__create_book_table.sql

create table books (
    id bigint auto_increment primary key,
    title varchar(100) not null,
    author varchar(50),
    publish_date date
);

启动应用,Flyway 自动执行。日志会显示:

Successfully applied 1 migration to schema, now at version v1

去数据库看,books 表建好了,flyway_schema_history 表里多了这条记录。

脚本里不止能建表,索引、约束、视图都能写:

V1__create_book_table.sql(完整版):

create table books (
    id bigint auto_increment primary key,
    title varchar(100) not null,
    author varchar(50),
    publish_date date
);

create index idx_books_title on books(title);

一个迁移脚本可以放多条语句,Flyway 按 ; 分隔依次执行。

脚本写作有两条约定俗成的规矩:

  • 一个脚本只做一件事:建表就建表,加列就加列。脚本越小,出错越好定位,回滚思路越清晰。
  • 描述写清楚意图V2__add_price_columnV2__update 有用一百倍。历史表里所有人靠描述理解这次变更。

版本号也不是越密越好。合并到主干时版本冲突了,把后合入的脚本版本号改大即可,Flyway 按最终版本排序执行。

升级:加第二个版本

需求来了,要加价格列。不能改 V1 的脚本,新建 V2:

V2__add_price_column.sql:

alter table books add column price decimal(10, 2);

重启应用,Flyway 发现 V2 没执行过,自动补上。团队其他人拉代码后启动,也会自动执行同样的脚本。

这就是版本化迁移的核心价值:结构变更跟着代码走,所有环境用同样的脚本达到同样的结构

配置项

application.yml 里按需配置:

spring:
  flyway:
    enabled: true            # 默认就是 true,可省略
    locations: classpath:db/migration   # 脚本目录,默认值
    baseline-on-migrate: true  # 数据库已有表时,跳过旧结构直接基线化
    baseline-version: 0        # 基线版本
    validate-on-migrate: true  # 校验已执行脚本是否被改过

baseline-on-migrate 是接盘神器:项目接手一个已有数据的数据库,没有历史表。设为 true 后,Flyway 把当前结构记为基线版本 0,然后从 V1 开始往后执行,不会碰已有表。

还有两个配置按需了解:

  • spring.flyway.locations:脚本目录,默认 classpath:db/migration,可以配多个目录用逗号分隔。
  • spring.flyway.out-of-order:允许乱序版本(比如开发分支合并后出现 V3 晚于 V4)。生产默认关闭,别轻易开。

已执行脚本不能改

Flyway 会记录每个脚本的校验和。执行过的脚本内容被改动,校验和不匹配,启动直接报错:

Migration checksum mismatch for migration version 1

这是故意的安全机制:防止有人偷偷改历史脚本,导致各环境结构不一致。

Warning

正确做法永远是「新增一个 V+N 脚本」,而不是修改旧的。改历史脚本等于篡改历史,测试环境也许能蒙混过关,生产环境迟早爆雷。

真改坏了(比如本地调试改过历史脚本),用 flyway repair 命令重算校验和,但这只是修复元数据,结构差异还得自己补迁移脚本。

与 schema.sql 的取舍

很多人问:Flyway 和 schema.sql 都能建表,用哪个?

定位完全不同:

方式定位适合
schema.sql/data.sql一次性初始化开发环境造数据、演示项目
Flyway持续演进的结构管理所有要长期维护的项目

schema.sql 每次启动都可能重跑,没有版本概念,无法表达「从 V1 演进到 V2」的过程。Flyway 则是正式的迁移工具,脚本只执行一次,可追溯、可回滚思路清晰。

同类的迁移工具还有 Liquibase,用 XML/YAML 描述变更而不是纯 SQL,团队喜欢代码评审的话会选它。二选一即可,Flyway 上手更轻。

Tip

用了 Flyway,就把 spring.jpa.hibernate.ddl-auto 设为 nonevalidate,结构交给 Flyway 管,别让 Hibernate 和迁移脚本抢着改表。两套机制同时管结构,早晚打架。

常见报错速查

报错原因处理
checksum mismatch改过已执行的脚本新增脚本替代,别改旧的
non-empty schema库里有表但无历史表baseline-on-migrate
failed to connect数据源配置不对检查 URL/账号/权限
找不到驱动模块只加了 flyway-core补对应数据库模块

小结

Flyway 把表结构变更变成版本化脚本:V1 建表、V2 加列,每个脚本只执行一次,历史表记录一切。脚本只能新增不能修改,与 schema.sql 定位不同,长期项目选 Flyway。

下一节看 schema.sql 和 data.sql 的完整用法。