文档站点
Skip to content

迁移(Flyway / Liquibase) ​

Laravel 用 php artisan make:migration + php artisan migrate 管理表结构版本;Spring Boot 的标配是 Flyway 或 Liquibase。这篇主推 Flyway(更接近 Laravel 的「SQL 文件 + 版本号」心智模型)。

为什么需要迁移 ​

Java 项目(尤其团队协作)不允许手动去数据库建表。理由和 Laravel 一样:

  • 表结构随代码入库,可审查、可回滚
  • 新同事 clone 项目 → 一条命令建出完整库
  • 多环境(dev/staging/prod)结构一致
  • 部署时自动升级表结构

Flyway 的工作方式 ​

Flyway 扫描 src/main/resources/db/migration/ 下的 SQL 文件,按版本号顺序执行一次,并在 flyway_schema_history 表记录执行历史(类似 Laravel 的 migrations 表)。

db/migration/
├── V1__create_posts_table.sql
├── V2__add_status_to_posts.sql
└── V3__create_comments_table.sql

文件名规范:V{版本号}__{描述}.sql

  • 数字版本号:V1__xxx.sql、V1_1__xxx.sql、V2__xxx.sql
  • 每个文件只会执行一次(已执行的版本号不会重跑)

快速上手 ​

1. 依赖 ​

xml
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-core</artifactId>
</dependency>
<!-- 不同数据库还要 flyway-mysql 等,见下文 -->
xml
<!-- MySQL 8 需要额外加数据库模块 -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-mysql</artifactId>
</dependency>

2. 写第一个迁移文件 ​

src/main/resources/db/migration/V1__create_posts_table.sql:

sql
CREATE TABLE posts (
    id         BIGINT AUTO_INCREMENT PRIMARY KEY,
    title      VARCHAR(100)  NOT NULL,
    content    TEXT          NOT NULL,
    user_id    BIGINT        NOT NULL,
    status     TINYINT       NOT NULL DEFAULT 1,
    created_at TIMESTAMP     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_posts_user_id (user_id)
);

3. 启动即自动迁移 ​

配置好数据源后,应用启动时 Flyway 自动执行未跑过的迁移。也可以手动:

bash
./mvnw spring-boot:run   # 启动时自动跑

对照 Laravel

LaravelFlyway
database/migrations/2026_01_01_create_posts_table.phpV1__create_posts_table.sql
php artisan migrate应用启动自动执行
migrations 表flyway_schema_history 表
版本号按时间戳版本号 V1、V2……
迁移文件用 PHP 类(可执行逻辑)迁移文件就是纯 SQL

4. 修改已有表 ​

改表不能改旧文件,要新增 V 文件(和 Laravel 一样):

sql
-- V2__add_status_to_posts.sql
ALTER TABLE posts ADD COLUMN published_at TIMESTAMP NULL;

常用配置 ​

yaml
spring:
  flyway:
    enabled: true
    locations: classpath:db/migration
    baseline-on-migrate: true      # 对已有数据的库,允许以当前状态为基线

TIP

baseline-on-migrate: true 非常实用:已有的生产库(表是手动建的、没有 flyway 历史)第一次启用 Flyway 时,不会因为版本对不上而报错,而是以当前库状态为基线继续往后迁移。

回滚怎么办 ​

Laravel 有 migrate:rollback;Flyway 的哲学是 「只前进,不回头」:

  • 已经执行过的 V 文件不允许修改(校验和会变,启动直接报错)
  • 回滚靠反向 SQL 文件(U 版本)或新迁移
sql
-- V2__add_status_to_posts.sql(如果已经发布)
-- 修正方式是 V3 再改,而不是改 V2

WARNING

永远不要修改已经跑过的迁移文件。Flyway 会校验文件 checksum,改了就报 Migration checksum mismatch。正确做法:新增 V 版本文件。这和 Laravel「不要改动已发布的迁移」是同一纪律。

测试环境用什么 ​

  • 单元测试:H2 + Flyway 自动建 schema,秒级
  • 本地开发:MySQL 本地实例 + Flyway
  • CI:起个 MySQL 容器,Flyway 建表 → 跑测试
yaml
# application-test.yml
spring:
  datasource:
    url: jdbc:h2:mem:testdb;MODE=MySQL
  flyway:
    locations: classpath:db/migration

H2 兼容模式

MODE=MySQL 让 H2 尽量兼容 MySQL 语法,很多迁移 SQL 能在 H2 上跑。但复杂 SQL(如某些索引、字符集)可能不兼容,测试环境理想还是用真实 MySQL 容器。

Flyway vs Liquibase ​

维度FlywayLiquibase
迁移格式纯 SQL 文件XML / YAML / SQL(框架能解析变更集)
上手简单直接学习成本高
回滚不支持自动回滚,需反向 SQL支持 changeset 回滚
心智模型接近 Laravel更「框架化」
选谁多数项目选它需要精细变更管理的大型团队

总结

新项目直接 Flyway。db/migration 放 SQL 文件,和 Laravel database/migrations 的用法几乎一样,唯一差别是「启动自动执行,不用手动跑 artisan 命令」。

最佳实践清单 ​

  1. 文件名:V版本__驼峰描述.sql,版本号递增
  2. 已执行文件永不修改,改表加新 V 文件
  3. 一个迁移文件只做一件事
  4. baseline-on-migrate: true 兼容已有库
  5. 生产部署前 review 迁移 SQL,DDL 加 IF NOT EXISTS 防御

面向 PHP / Laravel 开发者的 Spring Boot 中文文档