摘要:CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵', `name` varchar(20) NOT NULL COMMENT '姓名', `age` int(11) DEFAULT NULL COMMENT '年齡', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8。從啓動日誌中可以看出,Flyway 監測到需要運行版本腳本來初始化數據庫,因此執行了 V1.0__init_db.sql 腳本,從而創建了 user 表,另外還自動創建了 flyway_schema_history 表,用於記錄所有版本演化和狀態,其表結構如下(以 MySQL 爲例):。

在項目迭代開發中,難免會有更新數據庫 Schema 的情況,比如添加新表、在表中增加字段或者刪除字段等,那麼當我對數據庫進行一系列操作後,如何快速地在其他同事的電腦上同步?如何在測試/生產服務器上快速同步?

每次發版的時候,由於大家都可能有 sql 更改情況,這樣就會有以下痛點:

  • 忘記某些 sql 修改
  • 每個開發人員的 sql 的執行順序問題
  • 重複更新
  • 需要手動去數據庫執行腳本

以上問題以及痛點可以通過 Flyway 工具來解決,Flyway 可以實現自動化的數據庫版本管理,並且能夠記錄數據庫版本更新記錄。

Flyway 簡介

Flyway 是獨立於數據庫的應用、管理並跟蹤數據庫變更的數據庫版本管理工具。用通俗的話講,Flyway 可以像 Git 管理不同人的代碼那樣,管理不同人的 sql 腳本,從而做到數據庫同步,更多的信息可以在 Flyway 的官網上進行閱讀學習。

另外 Flyway 支持很多關係數據庫,具體如下所示:

下面我們在 Spring Boot 中集成 Flyway 來實現數據庫版本控制。

Spring Boot 集成 Flyway

首先創建一個 SpringBoot 項目,然後在 pom.xml 加入如下依賴集成 Flyway:

<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-core</artifactId>
    <version>5.2.4</version>
</dependency>

然後在 application.yml 中寫入 mysql 的配置及 Flyway 的相關配置(Flyway locations 默認讀取當前項目下的 resources/db/migration 目錄)

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123

spring.flyway.locations=classpath:/db/migration/

接下來,在 resources/db/migration 目錄下創建需要執行的 SQL 腳本即可。

其中,SQL 腳本命名規範如下:

.
__
.sql

那麼,我們按照命名規範在 resources/db/migration 目錄下,創建 V1.0__init_db.sql SQL 遷移腳本,具體內容如下:

DROP TABLE IF EXISTS `user` ;

CREATE TABLE `user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `name` varchar(20) NOT NULL COMMENT '姓名',
  `age` int(11) DEFAULT NULL COMMENT '年齡',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `user` (`id`, `name`, `age`) VALUES ('1', 'wupx', '18');

最後啓動項目,執行日誌如下所示:

2020-05-07 12:41:29.126  INFO 13732 --- [           main] o.f.c.internal.license.VersionPrinter    : Flyway Community Edition 5.2.4 by Boxfuse
2020-05-07 12:41:29.236  INFO 13732 --- [           main] o.f.c.internal.database.DatabaseFactory  : Database: jdbc:mysql://localhost:3306/test (MySQL 5.5)
2020-05-07 12:41:29.287  INFO 13732 --- [           main] o.f.core.internal.command.DbValidate     : Successfully validated 1 migration (execution time 00:00.009s)
2020-05-07 12:41:29.330  INFO 13732 --- [           main] o.f.c.i.s.JdbcTableSchemaHistory         : Creating Schema History table: `test`.`flyway_schema_history`
2020-05-07 12:41:29.479  INFO 13732 --- [           main] o.f.core.internal.command.DbMigrate      : Current version of schema `test`: << Empty Schema >>
2020-05-07 12:41:29.480  INFO 13732 --- [           main] o.f.core.internal.command.DbMigrate      : Migrating schema `test` to version 1.0 - init db
2020-05-07 12:41:29.481  WARN 13732 --- [           main] o.f.c.i.s.DefaultSqlScriptExecutor       : DB: Unknown table 'user' (SQL State: 42S02 - Error Code: 1051)
2020-05-07 12:41:29.631  INFO 13732 --- [           main] o.f.core.internal.command.DbMigrate      : Successfully applied 1 migration to schema `test` (execution time 00:00.301s)

從啓動日誌中可以看出,Flyway 監測到需要運行版本腳本來初始化數據庫,因此執行了 V1.0__init_db.sql 腳本,從而創建了 user 表,另外還自動創建了 flyway_schema_history 表,用於記錄所有版本演化和狀態,其表結構如下(以 MySQL 爲例):

Field Type Null Key Default
version_rank int(11) NO MUL NULL
installed_rank int(11) NO MUL NULL
version varchar(50) NO PRI NULL
description varchar(200) NO NULL
type varchar(20) NO NULL
script varchar(1000) NO NULL
checksum int(11) YES NULL
installed_by varchar(100) NO NULL
installed_on timestamp NO CURRENT_TIMESTAMP
execution_time int(11) NO NULL
success tinyint(1) NO MUL NULL

查詢 flyway_schema_history 表,發現增加了一條版本號爲 1.0 的,使用 V1.0__init_db.sql 遷移腳本的記錄。

mysql> SELECT * FROM flyway_schema_history;
+----------------+---------+-------------+------+-------------------+------------+--------------+---------------------+----------------+---------+
| installed_rank | version | description | type | script            | checksum   | installed_by | installed_on        | execution_time | success |
+----------------+---------+-------------+------+-------------------+------------+--------------+---------------------+----------------+---------+
|              1 | 1.0     | init db     | SQL  | V1.0__init_db.sql | 1317299633 | root         | 2020-05-07 12:41:29 |             97 |       1 |
+----------------+---------+-------------+------+-------------------+------------+--------------+---------------------+----------------+---------+

再去查詢 user 表,發現 sql 腳本中的數據也插入成功了。

mysql> SELECT * FROM user;
+----+------+-----+
| id | name | age |
+----+------+-----+
|  1 | wupx |  18 |
+----+------+-----+

接下來再次運行項目,結果如下:

2020-05-07 15:34:49.843  INFO 41880 --- [           main] o.f.c.internal.license.VersionPrinter    : Flyway Community Edition 5.2.4 by Boxfuse
2020-05-07 15:34:49.981  INFO 41880 --- [           main] o.f.c.internal.database.DatabaseFactory  : Database: jdbc:mysql://localhost:3306/test (MySQL 5.5)
2020-05-07 15:34:50.036  INFO 41880 --- [           main] o.f.core.internal.command.DbValidate     : Successfully validated 1 migration (execution time 00:00.013s)
2020-05-07 15:34:50.043  INFO 41880 --- [           main] o.f.core.internal.command.DbMigrate      : Current version of schema `test`: 1.0
2020-05-07 15:34:50.043  INFO 41880 --- [           main] o.f.core.internal.command.DbMigrate      : Schema `test` is up to date. No migration necessary.

從日誌中可以看出,Flyway 發現一個遷移腳本,也就是 V1.0__init_db.sql ,經過判斷已經到達最新版本 1.0,無需執行遷移。

接下來,我們在 V1.0__init_db.sql 遷移腳本中添加一條 INSERT 操作: INSERT INTO user ( id , name , age ) VALUES ('2', 'huxy', '18'); ,再次啓動項目,會報如下錯誤:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'flywayInitializer' defined in class path resource [org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration$FlywayConfiguration.class]: Invocation of init method failed; nested exception is org.flywaydb.core.api.FlywayException: Validate failed: Migration checksum mismatch for migration version 1.0
-> Applied to database : 1317299633
-> Resolved locally    : -1582367361

這個錯誤的原因就是 Flyway 會給腳本計算一個 checksum 保存在數據庫中,用於在之後運行過程中對比 sql 文件是否有變化,如果發生了變化,則會報錯,也就防止了誤修改腳本導致發生問題。

總結

Flyway 可以有效改善數據庫版本管理方式,並且是一款 Java 開源的數據庫遷移管理工具,具有輕便小巧的特點,可以無門檻快速集成到項目中,如果項目中還未使用,不防嘗試一下,想了解更多的可以去官網查看文檔學習。

本文的完整代碼在 https://github.com/wupeixuan/SpringBoot-Learndatabase-version-control 目錄下。

參考

https://flywaydb.org/

https://github.com/wupeixuan/SpringBoot-Learn

相關文章