Flyway版本化管理資料庫腳本

假如我們有一個叫shiny的項目,它是由一個程式Shiny-Server 和一個資料庫 Shiny-DB組成的;

簡單結構圖如下:

image.png

但是很多時候,現實開發團隊是這樣的:

image.png

我們的項目shiny項目的運行環境是有多套的,

我們擅長解決程式碼層面的問題。

版本控制工具git非常普遍而且好用

我們有持續集成和持續構建的工具

我們很好的定義了測試和生產環境的發布流程

image.png

但是我們的資料庫的版本如何控制呢?

image.png

當前現狀

非常不幸的是我們還不能很好的處理資料庫的版本管理問題,

很多的項目依賴運維人員手動的執行SQL腳本,

有的時候甚至為了快速解決bug去快速的在命令行上執行SQL腳本,那麼問題來了。

通常這些問題的答案是:鬼知道。

引入目的

flyway解決了上面的這些問題。

目前Flyway支援的資料庫還是挺多的,包括:

Oracle, SQL Server, SQL Azure, DB2, DB2 z/OS,

MySQL(including Amazon RDS), MariaDB,

Google Cloud SQL, PostgreSQL(including Amazon RDS and Heroku),

Redshift, Vertica, H2, Hsql, Derby, SQLite, SAP HANA,

solidDB, Sybase ASE and Phoenix。

Flyway的執行流程

Flyway是一款開源的資料庫版本管理工具,

它更傾向於規約優於配置的方式。

Flyway可以獨立於應用實現管理並跟蹤資料庫變更,支援資料庫版本自動升級,

並且有一套默認的規約,不需要複雜的配置,

Migrations可以寫成SQL腳本,也可以寫在Java程式碼中,

不僅支援Command Line和Java API,還支援Build構建工具和Spring Boot等,

同時在分散式環境下能夠安全可靠地升級資料庫,同時也支援失敗恢復等。

Flyway工作流程.png

每次不管是資料庫的表結構或者表數據的變更,

你只需要把問題當成一次資料庫的升級,

簡單的創建一個比當前版本更高的版本的遷移SQL文件或者Java文件,

下次Flyway啟動的時候,他會找到這些腳本並把它更新到資料庫。

腳本或者Java遷移腳本的命名規則:

其中的文件名由以下部分組成,除了使用默認配置外,某些部分還可自定義規則。

  • prefix: 可配置,前綴標識,默認值V表示Versioned,R表示Repeatable
  • version: 標識版本號,由一個或多個數字構成,數字之間的分隔符可用點.或下劃線_
  • separator: 可配置,用於分隔版本標識與描述資訊,默認為兩個下劃線__
  • description: 描述資訊,文字之間可以用下劃線或空格分隔
  • suffix: 可配置,後續標識,默認為.sql

實現路徑

真實項目版本更新場景中,我們不可能再基於人力去做這件事情,我們選擇的是API的方式。

使用步驟如下:

API方式使用Flyway.png

一般大家都是寫SQL腳本,也支援通過寫Java程式碼的方式來實現。

Java方式寫遷移功能

使用步驟:

Java程式碼方式寫遷移程式碼.png

目前的集成方式

使用的是springboot的方式集成了Flyway;

COLA引入Flyway的流程.png

配置參數:可自行翻譯和參考選擇去配置

flyway.baseline-description= # The description to tag an existing schema with when executing baseline.
flyway.baseline-version=1 # Version to start migration.
flyway.baseline-on-migrate=false # Whether to execute migration against a non-empty schema with no metadata table
flyway.check-location=false # Check that migration scripts location exists.
flyway.clean-on-validation-error=false # will clean all objects. Warning! Do NOT enable in production!
flyway.enabled=true # Enable flyway.
flyway.encoding=UTF-8 # The encoding of migrations.
flyway.ignore-failed-future-migration=true # Ignore future migrations when reading the metadata table.
flyway.init-sqls= # SQL statements to execute to initialize a connection immediately after obtaining it.
flyway.locations=classpath:db/migration # locations of migrations scripts.
flyway.out-of-order=false # Allows migrations to be run "out of order".
flyway.placeholder-prefix=  # The prefix of every placeholder.
flyway.placeholder-replacement=true # Whether placeholders should be replaced.
flyway.placeholder-suffix=} # The suffix of every placeholder.
flyway.placeholders.*= # Placeholders to replace in Sql migrations.
flyway.schemas= # Default schema of the connection and updating
flyway.sql-migration-prefix=V # The file name prefix for Sql migrations
flyway.sql-migration-separator=__ # The file name separator for Sql migrations
flyway.sql-migration-suffix=.sql # The file name suffix for Sql migrations
flyway.table=schema_version # The name of Flyway's metadata table.
flyway.url= # JDBC url of the database to migrate. If not set, the primary configured data source is used.
flyway.user= # Login user of the database to migrate. If not set, use spring.datasource.username value.
flyway.password= # JDBC password if you want Flyway to create its own DataSource.
flyway.validate-on-migrate=true # Validate sql migration CRC32 checksum in classpath.
package db.migration;

/**
 * @author carter
 * create_date  2020/8/13 17:39
 * description     java資料庫變更模板程式碼
 */


import lombok.extern.slf4j.Slf4j;
import org.flywaydb.core.api.migration.BaseJavaMigration;
import org.flywaydb.core.api.migration.Context;

import java.sql.ResultSet;
import java.sql.Statement;

@Slf4j
public class V2__test extends BaseJavaMigration {
    @Override
    public void migrate(Context context) throws Exception {
        try (Statement select = context.getConnection().createStatement()) {
            try (ResultSet rows = select.executeQuery("SELECT 1")) {
                while (rows.next()) {
                    int id = rows.getInt(1);
                    String anonymizedName = "Anonymous" + id;
                    log.info("執行sql腳本:{}",anonymizedName);
                }
            }
        }
    }
}


資料來源

官網: //flywaydb.org/

實戰: //blog.waterstrong.me/flyway-in-practice/

如有問題,請留言。

原創不易,關注誠可貴,轉發價更高!轉載請註明出處,讓我們互通有無,共同進步,歡迎溝通交流。
我會持續分享Java軟體編程知識和程式設計師發展職業之路,歡迎關注,我整理了這些年編程學習的各種資源,關注公眾號『李福春持續輸出』,發送’學習資料’分享給你!