YOU'VE MADE A BRAVE DECISION, WELCOME.

每一个不曾起舞的日子都是对生命的辜负。

总结:Flyway-Baseline

Flyway

Q: Flyway 是什么?
 Flyway 是独立于数据库的应用、管理并跟踪数据库变更的数据库版本管理工具。官方文档

Q: Flyway 如何工作?
 假设将Flyway应用在一个空的数据库中,它会去寻找metadata table(元数据表),如果找不到这张metadata table,Flyway会去创建一张叫SCHEMA_VERSION的空表作为metadata table。这张表将会被Flyway用来追踪记录数据库的状态。

 Migration文件会按照他们的版本进行排序,每一个Migration文件被执行(应用),都会去更新metadata table。当再次扫描(执行)Migration文件时,会对metadata table进行检查,如果在metadata table里已经存在该Migration文件,则该Migration文件不会被执行。

 所以需要改变当前数据库的表信息或者表结构的时候,只需要添加一个高于当前版本的Migration文件,当再次执行Flyway的时候,数据库就会被改变(升级)。

Q: Flyway 怎么用?
 官网上介绍,Flyway的使用有六种方式,分别是Command-line、API、Maven、Gradle、Ant、SBT。
具体方式

Q: Flyway 支持的数据库?
Oracle、SQL Server、SQL Azure、DB2、MySQL、MariaDB、PostgreSQL、Redshift、Vertica、H2、Hsql、Derby、SQLite、SAP HANA、solidDB、Sybase ASE、Phoenix等。

Q: Flyway 用在哪些框架里?
Spring Boot、Play、Dropwizard、Ninja Framework、Jooby Framework、flyway-test-extensions、Chef cookbook、Griffon Plugin、Grunt Plugin、Spring Roo Add-on等。

Q: Flyway 常用语句有哪些,各自的作用?
Flyway的主要语句有六个,分别是Migrate、Clean、Info、Validata、Baseline、Repair。

  • migrate
    主要有两个作用:一是migrate数据库到最新的版本,二是如果没有metadata table会自动创建。
  • clean
    会清空数据库中所有的对象,包括metadata table。一定不要用在生产环境。
  • info
    会打印所有Migration文件的详细信息和状态信息。这些信息可以让我们知道,哪些文件被执行,哪些没有被执行。
  • validata
    会帮助验证Migration文件与本地数据库是否匹配。
  • baseline
    基线是引入Flyway到现有数据库的一个特点的版本。它会忽略包括基线在内的所有数据库的版本,对于新的Migration文件也适用。
  • repair
    主要用来修复metadata table,包括两类问题:删除失败的Migration文件;调整和检验可用的Migration文件。

Migration

Q: Migration 产生的背景?
 假设我们有一个项目叫Shiny,这个项目主要经营的是一个可持续交付的名叫Shiny Soft的软件,这个软件背后有一个数据库叫Shiny DB。

 在多人合作的时候,这种模式就会变成各自机器上的环境、不断集成的环境、测试环境、生产环境等等一系列不同的环境,与之对应的也就有了不同的Shiny DB和Shiny Soft版本。

 所以这个时候不仅要处理我们自己机器上的环境,同时也要解决其他环境出现的问题。如果是代码出现问题很容易解决,因为有版本管理工具、构建工具、持续集成、明确的发布和部署的过程。同样数据库也会出现很多和代码一样的问题,比如:这台机器上数据库的版本是什么?生产环境上数据库的版本是什么?怎么样在测试通过后应用在生产环境上?

 Database migration 就会解决这些问题。它可以从头创建一个数据库,在任何时候都清楚数据库处于什么状态。

 所以使用Migration对我们来说很方便。

Q: Migration 命名规则?

  • 包含至少一个数字
  • 由.或_分隔
  • 开始的0是被忽略的
1
2
3
4
5
6
例子:
1.2.3.4.5.6.7.8.9
205.68
20130115113556
2013.1.15.11.35.56
2013.01.15.11.35.56

Flyway的使用方法

创建一个项目

使用Maven Archetype Plugin创建项目,语句如下:

1
2
3
4
5
6
7
8
$ mvn archetype:generate -B \
-DarchetypeGroupId=org.apache.maven.archetypes \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DarchetypeVersion=1.1 \
-DgroupId=foo \
-DartifactId=bar \
-Dversion=1.0-SNAPSHOT \
-Dpackage=foobar

然后进入到项目目录:$ cd bar

添加Flyway

在pom.xml里添加Flyway和H2的插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<project ...>
...
<build>
<plugins>
<plugin>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-maven-plugin</artifactId>
<version>4.0.3</version>
<configuration>
<url>jdbc:h2:file:./target/foobar</url>
<user>sa</user>
</configuration>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.191</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>

创建Migration文件

进入到Migration目录:src/main/resources/db/migration,然后新建V1__Create_person_table.sql文件,内容为SQL语句。

1
2
3
4
create table PERSON (
ID int not null,
NAME varchar(100) not null
);

执行Migrate

在demo目录下创建数据库表,使用:$ mvn flyway:migrate。会有以下提示信息:

1
2
3
4
[INFO] Creating Metadata table: "PUBLIC"."schema_version"
[INFO] Current version of schema "PUBLIC": << Empty Schema >>
[INFO] Migrating schema "PUBLIC" to version 1 - Create person table
[INFO] Successfully applied 1 migration to schema "PUBLIC" (execution time 00:00.062s).

创建第二个Migration文件

进入到Migration目录:src/main/resources/db/migration,然后新建V2__Add_people.sql文件,内容为SQL语句。

1
2
3
insert into PERSON (ID, NAME) values (1, 'Axel');
insert into PERSON (ID, NAME) values (2, 'Mr. Foo');
insert into PERSON (ID, NAME) values (3, 'Ms. Bar');

执行$ mvn flyway:migrate,命令行里提示以下信息表示成功。

1
2
3
[INFO] Current version of schema "PUBLIC": 1
[INFO] Migrating schema "PUBLIC" to version 2 - Add people
[INFO] Successfully applied 1 migration to schema "PUBLIC" (execution time 00:00.090s).

使用其他命令

1
2
3
4
5
6
$ mvn flyway:clean
$ mvn flyway:migrate
$ mvn flyway:info
$ mvn flyway:validata
$ mvn flyway:baseline
$ mvn flyway:repair

项目上出现的问题

问题背景
问题出在Develop环境向QA环境上传时,最开始QA与develop环境的代码是相同的,具体如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
**db.migration/文件夹**
V1__Create_ProfitExcel_Table.sql
V3__Create_Client_Catalogue_Table.sql
V6__Create_Catalogues_Table.sql
V7__Add_Catalogues.sql
V8__Create_User_table.sql
V9__Add_User.sql
V10__Create_Authorities_Table.sql
V11__Add_Authorities.sql
V12__Create_Sub_Client_Table.sql
V13__Create_Project_Manager_Table.sql
**V1__Create_ProfitExcel_Table.sql的文件内容**
create table ProfitExcel (
id serial PRIMARY KEY,
profitYear INTEGER,
profitMonth INTEGER,
client varchar(40) not null,
subProject varchar(40) not null,
projectDescription varchar(140),
netRevenue FLOAT ,
wages FLOAT ,
totalTravel FLOAT ,
otherCosts FLOAT ,
grossProfitMargin FLOAT ,
grossProfitMarginPercent varchar (100)
);
CREATE INDEX profit_report_index ON ProfitExcel (profitYear, profitMonth, client, subProject);

因为要对V1__Create_ProfitExcel_Table.sql创建的ProfitExcel表新增属性,所以增加了V14__Alter_Profit_Cloumn.sql,具体内容如下:

1
2
3
4
5
6
7
ALTER TABLE ProfitExcel ADD professional FLOAT;
ALTER TABLE ProfitExcel ADD revenueprofessional FLOAT;
ALTER TABLE ProfitExcel ADD revenuebillable FLOAT;
ALTER TABLE ProfitExcel ADD revenuebaddebt FLOAT;
ALTER TABLE ProfitExcel ADD costtravelbillable FLOAT;
ALTER TABLE ProfitExcel ADD costtraveltravel FLOAT;
ALTER TABLE ProfitExcel ADD costtotalcosts FLOAT;

在本地环境里migrate完全没有任何问题,执行git add & git commit后,诡异的事情出现了,QA环境编译报错了。报错如下:

1
2
找不到报错信息了= =。
大概就是不存在V1的ProfitExcel表。

解决过程

  1. 认为V1__Create_ProfitExcel_Table.sql的名字写错了,更改为V1__Create_Profit_Table.sql之类的。
    当然不是这个原因。就在这个时候又出现了一个问题:Ci提示QA环境有两个V1文件

这就奇怪了,在本地执行时我会先执行mvn flyway:clean保证本地的数据库是空的,再去执行mvn flyway:migrate,在QA环境是不需要我去执行clean的命令的,因为每一次跑代码都是一个新的容器。

先去检查了QA环境上CI生成的war包,发现war包里只有一个V1的文件。所以问题就定位在了docker里,经过好长时间的检查,发现是因为JenkinsFile的配置文件里,在打包的时候没有清空之前的class文件。

解决方式:

1
2
3
4
5
6
7
8
9
10
11
修改前:
stage 'BUILD'
docker.image('maven:3.3.3-jdk-8').inside('-v/root/.m2:/root/.m2') {
sh 'mvn package'
}
修改后:
stage 'BUILD'
docker.image('maven:3.3.3-jdk-8').inside('-v/root/.m2:/root/.m2') {
sh 'mvn clean package'
}

  1. 推测V1没有执行,可以把V1的内容写在V2里。
    可是并没有起作用,又把V1的内容复制在V5里,依旧没有用。


  1. 经过很多次的试验之后,发现在配置文件里有flyway.baselineVersion=0是被注释掉的,执行baseline之后也没有什么卵用。

  2. 又把配置文件的flyway.baselineVersion=0改为flyway.baselineVersion=1,发现报错信息变了。提示已经存在ProfitExcel表,不能重复创建。到这里问题就解决了!WoW~

解决方法
按照道理来说QA环境每一次编译War包都是会新的database(空空的database,一张表也没有),但奇怪的是QA环境每一次都会有一张表存在在database里,这就是导致develop和QA环境不同的最根本的原因。设置Flyway的Baseline为1,将V1__Create_ProfitExcel_Table.sql里的文件写在V2__Create_ProfitExcel_Table.sql里,这样就能保证QA环境与Develop环境初始化时候的database是相同的。