为什么需要 database migration
上学的时候我们可能会遇到一些web相关的大作业或者project,那时候我们一个人或者两三人一组完成全部的前端后端以及数据库,从头到尾只有一个github repo,也不会去根据dev,test,product区分不同的版本。但是当我们面对工业级项目的时候就不是这样了,一方面根据项目的大小会有更多的developer参与其中,另一方面会有CI test,同时还需要有separate的production branch。如果仅对于code本身,git等version control的工具就可以满足需求,但是很多时候我们还需要database,而database的schema甚至其中的test用数据是很难通过git之类寻常的工具来track version的。例如下图的例子:
每次当我们merge代码的时候,我们也希望可以同时将 DB 更新到相应的state。
How Flyway works
Flyway的基本原理是从最初create db开始,用户需要对于每次db schema的更新操作都为之create一个migrate script,这个script一般具有这样都命名规则:V<version_number>__<descritption>.sql
,其内容就是由sql写成的对于db操作的内容,例如CREATE TABLE users {...}
。而Flyway本身会在用户新建的db中另外create一个table叫做 flyway_schema_history
,这个表中会存有db version control所需要的各种meta-data,下面是一个例子:
mysql> select * from flyway_schema_history;
+----------------+---------+-------------+------+------------------+-------------+--------------+---------------------+----------------+---------+
| installed_rank | version | description | type | script | checksum | installed_by | installed_on | execution_time | success |
+----------------+---------+-------------+------+------------------+-------------+--------------+---------------------+----------------+---------+
| 1 | 1 | init | SQL | V1__init.sql | 1952043475 | root | 2018-03-06 11:25:58 | 16 | 1 |
| 2 | 2 | testdata | SQL | V2__testdata.sql | -1926058189 | root | 2018-03-06 11:25:58 | 6 | 1 |
+----------------+---------+-------------+------+------------------+-------------+--------------+---------------------+----------------+---------+
2 rows in set (0.00 sec)
(例如这里我们有两个migration script)
当运行 flyway migrate
的时候,它会作如下操作:
- 检查meta-data table,如果没有会新建一个;
- 扫描migration script所在的path;
- 对比所有script与meta-data table,如果script的version number比当前version小,则忽略;
- 将所有剩余的version number大于当前version的script标记为 pending migrations,按照version number排序;
- 依次apply每个script,同时更新meta-data table。
这样,当我们完成了一部分feature的开发,需要check in code的时候,如果我们也更改了db schema,我们就可以将新建的 migration scripts 随代码一同 check in,当其他developer pull了之后,Flyway 就可以根据新添加的 migration scripts 自动将该 developer 的本地 db 也更新到相应的状态。
Sprint Boot Integration
这里提供一个简单的例子来解释如何在SpringBoot中使用Flyway。
-
1. Use Sprint Boot CLI to generate application
spring init --name=flyway-demo --dependencies=web,mysql,data-jpa,flyway flyway-demo
新建好的application会具有如下目录结构:
-
2. Configure MySQL and Hibernate
在 src/main/application.properties
中添加如下 properties:
## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
spring.datasource.url = jdbc:mysql://localhost:3306/flyway_demo?useSSL=false
spring.datasource.username = root
spring.datasource.password = root
## Hibernate Properties
# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
## This is important
# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto = validate
需要注意这里的最后一个property ddl-auto = validate
,这会令SpringBoot来check当前的DB schema和Java中的entites是否match,而不是根据Java中的entities来修改database schema(vs update)。
-
3. Create a Flyway migration script
Flyway默认会从classpath:db/migration
目录扫描script。
mkdir -p src/main/resources/db/migration
touch src/main/resources/db/migration/V1__init.sql
- 在
V1__init.sql
中添加如下内容:CREATE TABLE users ( id bigint(20) NOT NULL AUTO_INCREMENT, username varchar(100) NOT NULL, first_name varchar(50) NOT NULL, last_name varchar(50) DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY UK_username (username) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
4. Run the application
mvn sprint-boot:run
Flyway 自动扫描
db/migration
目录并找到我们刚刚新建的 V1__init.sql
并 执行,db完成更新。