跳到主内容
创建时间: 2026-09-16最后更新: 2026-09-16

「数据库初始化与表结构」使用 schema.sql 和 data.sql 从空数据库创建表,但这种方式不会记录数据库已经执行过哪些结构变更。「事务」章节又增加了历史表,接下来只要修改列、约束或索引,就必须知道数据库当前处于哪个版本。

Flyway 会按版本执行迁移,并在数据库中保存执行记录。本章把已有结构交给 Flyway 管理,再用第二个迁移为历史查询增加索引。Spring Boot 对 Flyway 的集成方式可在 Database Initialization 中核对。

1. 迁移与初始化

初始化脚本描述的是「数据库最终应该有什么」,迁移脚本描述的是「数据库怎样从一个版本变成下一个版本」。两者解决的问题不同:

方式执行依据适合场景主要限制
schema.sql应用每次启动时尝试执行空数据库、简单原型不记录历史,无法可靠升级已有结构
Flyway 版本迁移只执行尚未登记的版本持续演进的数据库已执行脚本必须保持不变

假设数据库已经执行过 V1,应用发布时又包含 V2 和 V3,Flyway 会先校验已有记录,再依次执行缺失的版本。应用实例看到的不是一份无法追踪的最终 SQL,而是一条明确的演进路径:

migration-sequence.txt
1
空数据库
2
-> V1:创建当前状态表和历史表
3
-> V2:创建历史查询索引
4
-> V3:以后新增的结构变化

迁移脚本应该与应用代码一起进入版本控制。部署新版本时,数据库结构和依赖它的代码才能沿着同一条发布历史前进。

2. 添加依赖

在已有 JDBC 和 PostgreSQL 驱动依赖旁边增加 Flyway Starter 与 PostgreSQL 模块:

pom.xml
1
<dependency>
2
<groupId>org.springframework.boot</groupId>
3
<artifactId>spring-boot-starter-flyway</artifactId>
4
</dependency>
5
6
<dependency>
7
<groupId>org.flywaydb</groupId>
8
<artifactId>flyway-database-postgresql</artifactId>
9
</dependency>

spring-boot-starter-flyway 提供 Flyway 与 Spring Boot 的启动集成,flyway-database-postgresql 提供 PostgreSQL 数据库支持。项目使用 Spring Boot Parent 管理兼容版本,因此这里不单独填写 <version>。

PostgreSQL JDBC 驱动仍然需要保留:Flyway 负责迁移流程,真正建立 JDBC 连接的仍然是数据库驱动。spring-boot-starter-jdbc 也继续为 JdbcClient、事务和连接池提供支持。

应用启动时,Spring Boot 会使用主 DataSource 自动配置 Flyway,并在依赖数据库结构的 Repository 开始工作前执行迁移。不需要在启动类中手动调用 Flyway.migrate()。

3. 创建初始版本

Flyway 默认从 src/main/resources/db/migration 读取迁移。创建 V1__create_health_tables.sql,把原有的两张表和初始数据放入第一个版本:

src/main/resources/db/migration/V1__create_health_tables.sql
01
create table health_status (
02
service varchar(50) primary key,
03
status varchar(20) not null,
04
checked_at timestamptz not null default current_timestamp
05
);
06
07
create table health_check_history (
08
id bigint generated always as identity primary key,
09
service varchar(50) not null,
10
status varchar(20) not null,
11
checked_at timestamptz not null default current_timestamp
12
);
13
14
insert into health_status (service, status)
15
values ('spring', 'UP');

文件名分为三个部分:

部分示例含义
前缀V这是一次有版本的迁移
版本1决定执行顺序,后续也可以使用 1_1
描述create_health_tables双下划线之后的人类可读说明

版本与描述之间必须是两个下划线。描述中的下划线会在迁移记录中显示为空格,不影响执行顺序。

迁移中不使用 IF NOT EXISTS 和 ON CONFLICT DO NOTHING。Flyway 保证同一版本只执行一次;如果目标对象意外提前存在,应让迁移失败并调查数据库状态,而不是跳过冲突后留下无法确认的结构。

4. 增加第二个版本

历史查询通常按检查时间倒序排列,并且可能只查询某个服务。为这两种访问方式建立索引,新建第二个迁移:

src/main/resources/db/migration/V2__add_history_query_indexes.sql
1
create index idx_health_history_checked_at
2
on health_check_history (checked_at desc, id desc);
3
4
create index idx_health_history_service_checked_at
5
on health_check_history (service, checked_at desc, id desc);

第一个索引服务于全部历史的时间倒序查询,第二个索引先按 service 定位,再按时间和 id 排序。id 作为第二排序键,可以在多条记录时间相同时保持稳定顺序。

不要回到 V1 直接补写索引。某些数据库已经执行过 V1,另一些数据库还没有;修改旧文件会让同一个版本在不同环境代表不同内容。正确做法是始终增加新的、更高版本迁移。

迁移目录此时包含:

V1__create_health_tables.sql创建表和初始数据
V2__add_history_query_indexes.sql增加历史查询索引

5. 切换管理机制

Flyway 和 Spring Boot 基础 SQL 初始化不应该同时管理相同表。删除原有 src/main/resources/schema.sql 与 data.sql,并把配置调整为:

src/main/resources/application.yaml
1
spring:
2
sql:
3
init:
4
mode: never
5
flyway:
6
enabled: true

spring.flyway.enabled 在依赖存在时默认就是 true,这里显式写出是为了说明当前由谁管理结构。spring.sql.init.mode: never 则明确关闭旧机制。

本教程前面已经使用 schema.sql 创建过本地表,而这个数据库还没有 Flyway 历史。对可丢弃的本地学习数据,最清晰的切换方式是重建 Docker 数据卷:

terminal.bash
1
docker compose down --volumes
2
docker compose up -d
3
DB_PASSWORD='spring' ./mvnw spring-boot:run

docker compose down --volumes 会永久删除当前 Compose 项目的数据库卷及其中数据,只能用于确认可以丢弃的本地环境。执行前应先停止应用,并确认没有需要保留的数据。

已经保存业务数据的数据库不能这样处理,也不应该直接开启 baseline-on-migrate 碰运气。应先备份并核对实际结构,再设计与现状匹配的基线版本,或通过受控的 Flyway 基线操作登记起点;从哪一个版本开始登记取决于数据库已经具备哪些结构。

6. 执行记录与校验

启动应用后,Flyway 会创建 flyway_schema_history 表。进入 psql 查询:

psql.sql
1
select installed_rank, version, description, type, success
2
from flyway_schema_history
3
order by installed_rank;

应该看到 1 / create health tables 和 2 / add history query indexes 两条成功记录。再次启动应用时,这两个版本不会重复执行,Flyway 只进行校验并查找更高版本。

Flyway 会为版本迁移保存校验和。如果已经执行过的脚本后来被修改,启动校验会失败。这不是需要绕过的障碍,而是在提醒当前代码中的迁移历史与数据库登记的历史不一致。

此时应先判断原因:开发者是否错误修改了旧脚本、数据库是否连接到了错误环境,或迁移记录是否遭到人工改动。正常的结构变化应放入新的迁移;repair 会修改历史记录,只能在已经理解并修复实际数据库状态后谨慎使用,不能作为通用的启动失败处理命令。

PostgreSQL 支持在事务中执行大多数常见 DDL,Flyway 可以在失败时回滚相应迁移,但不同数据库和特殊语句的事务能力并不完全相同。迁移脚本仍要保持小而明确,并在发布前使用与生产相同的数据库类型验证。

下一章会让测试自动启动独立的 PostgreSQL 容器。测试连接建立后,Flyway 将对这个全新的数据库执行同一组迁移,从而验证测试环境和实际运行环境使用的是同一份表结构来源。