Spring Boot 整合 Flyway 實現資料庫版本控制
阿新 • • 發佈:2020-05-07
在專案迭代開發中,難免會有更新資料庫 Schema 的情況,比如新增新表、在表中增加欄位或者刪除欄位等,那麼當我對資料庫進行一系列操作後,如何快速地在其他同事的電腦上同步?如何在測試/生產伺服器上快速同步?
![](https://img-blog.csdnimg.cn/20200507113604959.png)
每次發版的時候,由於大家都可能有 sql 更改情況,這樣就會有以下痛點:
- 忘記某些 sql 修改
- 每個開發人員的 sql 的執行順序問題
- 重複更新
- 需要手動去資料庫執行指令碼
以上問題以及痛點可以通過 Flyway 工具來解決,Flyway 可以實現自動化的資料庫版本管理,並且能夠記錄資料庫版本更新記錄。
## Flyway 簡介
Flyway 是獨立於資料庫的應用、管理並跟蹤資料庫變更的資料庫版本管理工具。用通俗的話講,Flyway 可以像 Git 管理不同人的程式碼那樣,管理不同人的 sql 指令碼,從而做到資料庫同步,更多的資訊可以在 Flyway 的官網上進行閱讀學習。
另外 Flyway 支援很多關係資料庫,具體如下所示:
![](https://img-blog.csdnimg.cn/20200507155532955.png)
下面我們在 Spring Boot 中整合 Flyway 來實現資料庫版本控制。
## Spring Boot 整合 Flyway
首先建立一個 SpringBoot 專案,然後在 `pom.xml` 加入如下依賴整合 Flyway:
```
org.flywaydb
flyway-core
5.2.4
```
然後在 `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 指令碼命名規範如下:
![](https://img-blog.csdnimg.cn/20200507112235596.png)
- Prefix 字首:V 代表版本遷移,U 代表撤銷遷移,R 代表可重複遷移
- Version 版本號:版本號通常 `.` 和整陣列成
- Separator 分隔符:固定由兩個下劃線 `__` 組成
- Description 描述:由下劃線分隔的單片語成,用於描述本次遷移的目的
- Suffix 字尾:如果是 SQL 檔案那麼固定由 `.sql` 組成,如果是基於 Java 類則預設不需要字尾
那麼,我們按照命名規範在 `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-Learn 的 `database-version-control` 目錄下。
**最好的關係就是互相成就**,大家的**在看、轉發、留言**三連就是我創作的最大動力。
> 參考
>
> https://flywaydb.org/
>
> https://github.com/wupeixuan/SpringBo