如何将 Flyway 添加到现有的应用程序中(附实例)

436 阅读5分钟

在我在JavaLand会议上关于结合Flyway、Hibernate和jOOQ的演讲后,一位与会者问我如何将Flyway添加到已经部署在生产中的现有应用程序。这是一个常见的问题,因为数据库迁移经常被新项目所忽视。这也是可以理解的。你在第一次安装时不需要它,而且还有很多更紧急的任务需要你去做。

但是,一开始就忽视数据库迁移会给以后带来新的问题。对于你的第一次更新,你不仅需要将Flyway添加到你的项目中,而且你还需要定义一个更新流程,与你现有的数据库一起使用,并可以从头设置一个新的数据库。幸运的是,Flyway为此提供了一个简单的解决方案。

  1. 你首先要生成一个你现有数据库的DDL脚本。这将是所有新安装数据库的第一个迁移脚本。
  2. 之后,你需要对你现有的数据库进行基准测试,这样Flyway就知道它不需要执行第一个迁移脚本。

之后,你的项目和一开始就使用Flyway的项目就没有区别了。你可以将接下来的迁移步骤定义为SQL脚本Java类,Flyway会自动执行这些脚本来迁移现有的数据库或创建一个新的数据库。

内容

生成你的数据库的DDL脚本

这个脚本的目的是在Flyway执行所有其他迁移脚本之前,重新创建你当前数据库的整个结构。这包括所有的数据库模式、表、序列、约束、函数、存储过程等。如果你的应用程序需要一组预定义的参考数据,你也应该把它们包括在这个脚本中。

在这里,你可以看到一个简单的脚本例子,它创建了本月编码挑战中使用的数据库,在持久性中心

CREATE TABLE public.chess_game (
    id bigint NOT NULL,
    date date,
    round integer NOT NULL,
    version integer NOT NULL,
    chess_tournament_id bigint,
    player_black_id bigint,
    player_white_id bigint
);
ALTER TABLE public.chess_game OWNER TO postgres;

CREATE TABLE public.chess_player (
    id bigint NOT NULL,
    birth_date date,
    first_name character varying(255),
    last_name character varying(255),
    version integer NOT NULL
);
ALTER TABLE public.chess_player OWNER TO postgres;

CREATE TABLE public.chess_tournament (
    id bigint NOT NULL,
    end_date date,
    name character varying(255),
    start_date date,
    version integer NOT NULL
);
ALTER TABLE public.chess_tournament OWNER TO postgres;

CREATE TABLE public.chess_tournament_games (
    chess_tournament_id bigint NOT NULL,
    games_id bigint NOT NULL
);
ALTER TABLE public.chess_tournament_games OWNER TO postgres;

CREATE TABLE public.chess_tournament_players (
    tournaments_id bigint NOT NULL,
    players_id bigint NOT NULL
);
ALTER TABLE public.chess_tournament_players OWNER TO postgres;

CREATE SEQUENCE public.hibernate_sequence
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;
ALTER TABLE public.hibernate_sequence OWNER TO postgres;

CREATE SEQUENCE public.player_sequence
    START WITH 100
    INCREMENT BY 50
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;
ALTER TABLE public.player_sequence OWNER TO postgres;

CREATE SEQUENCE public.tournament_sequence
    START WITH 100
    INCREMENT BY 50
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;
ALTER TABLE public.tournament_sequence OWNER TO postgres;

ALTER TABLE ONLY public.chess_game
    ADD CONSTRAINT chess_game_pkey PRIMARY KEY (id);

ALTER TABLE ONLY public.chess_player
    ADD CONSTRAINT chess_player_pkey PRIMARY KEY (id);

ALTER TABLE ONLY public.chess_tournament_games
    ADD CONSTRAINT chess_tournament_games_pkey PRIMARY KEY (chess_tournament_id, games_id);

ALTER TABLE ONLY public.chess_tournament
    ADD CONSTRAINT chess_tournament_pkey PRIMARY KEY (id);

ALTER TABLE ONLY public.chess_tournament_players
    ADD CONSTRAINT chess_tournament_players_pkey PRIMARY KEY (tournaments_id, players_id);

ALTER TABLE ONLY public.chess_tournament_games
    ADD CONSTRAINT uk_aydrmqjva2yao3biowshrrcx8 UNIQUE (games_id);

ALTER TABLE ONLY public.chess_game
    ADD CONSTRAINT fk1kenmun90s7uhly5kkk9o6rsf FOREIGN KEY (player_black_id) REFERENCES public.chess_player(id);

ALTER TABLE ONLY public.chess_tournament_players
    ADD CONSTRAINT fk5ykqwib1neqhak6wwuhsusf5w FOREIGN KEY (tournaments_id) REFERENCES public.chess_tournament(id);

ALTER TABLE ONLY public.chess_tournament_players
    ADD CONSTRAINT fkfmhm06fi40ak53r6gofvoyr44 FOREIGN KEY (players_id) REFERENCES public.chess_player(id);

ALTER TABLE ONLY public.chess_tournament_games
    ADD CONSTRAINT fkhoasvgr0mq1tkj5308chmd97v FOREIGN KEY (games_id) REFERENCES public.chess_game(id);

ALTER TABLE ONLY public.chess_game
    ADD CONSTRAINT fkikaihvc8m29y7fqtk5brfwk48 FOREIGN KEY (player_white_id) REFERENCES public.chess_player(id);

ALTER TABLE ONLY public.chess_game
    ADD CONSTRAINT fkquj6n755j3k650vwhoabw44yu FOREIGN KEY (chess_tournament_id) REFERENCES public.chess_tournament(id);

ALTER TABLE ONLY public.chess_tournament_games
    ADD CONSTRAINT fkuqqdoorh4jhfx6mqe3wsy5ni FOREIGN KEY (chess_tournament_id) REFERENCES public.chess_tournament(id);

创建这个脚本的最好和最简单的方法是使用你的数据库所提供的备份工具。通常只需要点击几下或下一个简短的命令就可以导出当前的表结构。或者,如果你使用Hibernate或其他JPA实现,你可以使用其模式导出功能

在下一步,你需要重新命名脚本,以遵循Flyway的命名惯例 V__.sql,例如V1__initial_version.sql,并将其复制到Flyway的迁移文件夹。命令行客户端默认使用*./sql这个文件夹。而Spring Boot的Flyway集成希望这些文件在你项目的主/资源/数据库/迁移文件夹中。你可以通过在配置中设置flyway.location*属性来覆盖默认值。

基准数据库

正如我在介绍Flyway时解释的那样,Flyway为每个已执行的迁移步骤向flyway_schema_history表写一条记录。Flyway使用这些信息来确定当前的数据库版本,并将其与可用的迁移脚本进行比较,以找到它需要执行的脚本。

当你把Flyway添加到一个现有的应用程序和数据库中时,这个表是不存在的。如果没有这个表,Flyway会认为数据库是空的,并执行所有可用的迁移脚本。这显然会失败,因为你已经在没有使用Flyway的情况下创建了你的数据库。

因此,你需要告诉Flyway,数据库已经处于版本1。你可以通过在命令行客户端执行baseline 命令来做到这一点。Flyway会创建flyway_schema_history表并添加第一条记录。

基线命令需要你的数据库的连接信息以及当前数据库模式的版本和描述:

flyway -url=jdbc:postgresql://localhost:5432/codingChallenge-220404 
       -user=postgres 
       -password=postgres 
       -baselineVersion=1 
       -baselineDescription=initial_version 
       baseline

请确保提供的baselineVersionbaselineDescription与你的第1个迁移脚本的文件名一致。在我的例子中,该文件的名称是V1__initial_version.sql。因此,我将baselineVersion设置为1baselineDescription设置为initial_version

当你执行baseline命令时,Flyway会创建它的flyway_schema_version表并记录baseline命令的执行情况:

The flyway_schema_version table with 1 record documenting the execution of the baseline command.

基于这个记录,Flyway知道数据库处于版本1,并且不会执行迁移脚本V1__initial_version.sql

小结

正如你在这篇文章中看到的,你可以通过2个步骤将Flyway引入到现有的项目中:

  1. 你需要创建一个SQL脚本,重新创建你当前的数据库。最简单的方法是使用你的数据库提供的工具创建一个备份。
  2. 之后,你需要对你现有的数据库进行基线分析,以便Flyway跳过第一个迁移脚本的执行。

在执行完这2个步骤后,你已经成功地将Flyway数据库迁移添加到你现有的应用程序中。Flyway将以与使用第1个迁移脚本创建的新数据库相同的方式对待包含基线版本的现有数据库。