kagamihogeの日記

kagamihogeの日記です。

Flywayのチュートリアル読んだ

flyway使ってるの初めて見て良く分からん感じだったのでとりあえずチュートリアル https://flywaydb.org/getstarted/ を読んで訳した。

※画像はすべて本家のものです。

Why database migrations?

まず最初にShinyというプロジェクトを仮定し、ここでの成果物の一つはShiny SoftというソフトウェアでこれはShiny DBというDBに接続します。

これを簡単に図示すると以下のようになります。

https://flywaydb.org/assets/balsamiq/SimpleView.png

他にもソフトウェアとDBが存在します。ここまでは良いでしょう。

しかし大半のプロジェクトにおいて、そのシンプルな世界観は急速に以下のように変貌します。

https://flywaydb.org/assets/balsamiq/Environments.png

環境のコピーを一回した程度では収まらず複数管理する必要が出てきます。このことは様々な課題を生み出します。

ソースコードの世界ではこれに上手く対処してきました。

  • 今日のバージョン管理は普遍的なツールになっています。
  • 繰り返し実行可能なビルドと継続的なインテグレーションが可能です。
  • 優れたリリースとデプロイ処理を行えます。

https://flywaydb.org/assets/balsamiq/SoftGreen.png

データベースの世界ではどうでしょうか?

https://flywaydb.org/assets/balsamiq/DbRed.png

今のところこれを上手くやることは出来ていません。たいていのプロジェクトでは未だに手動でSQLスクリプトを適用しています。時にはそれすらありません(問題を修正するためのその場限りのSQLが散在するなど)。ここで色々な疑問が浮かんできます。

  • あるマシン上のDBはどうなっているのか?
  • このスクリプトは適用済みなのか?そうでないのか?
  • 本番環境で流した緊急修正は後でテスト環境にも適用したのか?
  • データベースのインスタンスのセットアップ手順は?

基本的にはこれらの問いに対して我々は回答を持ち合わせていません。

データベースマイグレーションとはこうした混乱した状況の制御を取り戻すための有効な手段です。

これにより以下が可能となります。

  • クラッチからデータベースを再作成。
  • データベースの状態が常に明確となる。
  • DBの現行バージョンから新バージョンへ決定論的な方法で移行する。

How Flyway works

最もシンプルなシナリオでは空のDBにFlywayを立てる場合です。

https://flywaydb.org/assets/balsamiq/EmptyDb.png

Flywayはschema history tableを参照しようとします。データベースは空なのでFlywayは参照できず代わりに生成します。

デフォルトではflyway_schema_historyという一つの空のテーブルがDBに作られます。

https://flywaydb.org/assets/balsamiq/EmptySchemaVersion.png

このテーブルがDBの状態をトラッキングするのに使われます。

この後Flywayはファイルシステムかアプリケーションのクラスパスをマイグレーションのためにスキャンします。マイグレーションJavaSQLのどちらかで書きます。

マイグレーションバージョン番号ソートされており順番に適用されます。

https://flywaydb.org/assets/balsamiq/Migration-1-2.png

個々のマイグレーションが適用されるたびに、schema history tableもそれに応じて更新されます。

以下はflyway_schema_historyの例です。

installed_rank version description type script checksum installed_by installed_on execution_time success
1 1 Initial Setup SQL V1__Initial_Setup.sql 1996767037 axel 2016-02-04 22:23:00.0 546 true
2 2 First Changes SQL V2__First_Changes.sql 1279644856 axel 2016-02-06 09:18:00.0 127 true

メタデータと初期状態を配置し終えたので、次に新バージョンへのマイグレーションについて説明します。

Flywaryは再度ファイルシステムかアプリケーションのクラスパスをマイグレーションのためにスキャンします。マイグレーションはschema history tableとの照らし合わしを行います。バージョン番号が現行と等しいか低い場合、そのマイグレーションは無視されます。

それ以外のマイグレーションpending migrationsになります。これは、適用可能ではあるがまだ適用されていない、という意味です。

https://flywaydb.org/assets/balsamiq/PendingMigration.png

マイグレーションバージョン番号でソートされて順に実行されます。

https://flywaydb.org/assets/balsamiq/Migration21.png

schema history tableはそれに応じて更新されます。

flyway_schema_historyは以下のようになります。

installed_rank version description type script checksum installed_by installed_on execution_time success
1 1 Initial Setup SQL V1__Initial_Setup.sql 1996767037 axel 2016-02-04 22:23:00.0 546 true
2 2 First Changes SQL V2__First_Changes.sql 1279644856 axel 2016-02-06 09:18:00.0 127 true
3 2.1 Refactoring JDBC V2_1__Refactoring axel 2016-02-10 17:45:05.4 251 true

以上が一連の流れになります。データベースを更新する場合、DDLでもDMLでも、新規のマイグレーションを作成してバージョン番号を現行よりも高いものにします。それからFlywayを起動すると、そのマイグレーションでDBを更新します。

First Steps: Command-line

以下のカンタンなチュートリアルではFlywayのCLIでの使い方を解説します。設定方法と、最初のDBマイグレーションの書き方と動かし方が書いてあります。

このチュートリアルの所要時間はおおむね5分程度です。

Downloading and extracting Flyway

プラットフォームに応じたdownloading the Flyway Command-line ToolをDLして解凍します。

出来たディレクトリに移動します。

cd flyway-5.0.7

cd flyway-5.0.7

次に、以下のような/conf/flyway.confを編集してFlywayを設定します。

flyway.url=jdbc:h2:file:./foobardb
flyway.user=SA
flyway.password=

Creating the first migration

/sqlディレクトリにV1__Create_person_table.sqlという一つ目のマイグレーションを作成します。

create table PERSON (
    ID int not null,
    NAME varchar(100) not null
);

Migrating the database

DBをマイグレーションするためにFlywayを実行します。

flyway-5.0.7> flyway migrate

実行後、以下のような出力が得られます。

Database: jdbc:h2:file:./foobardb (H2 1.4)
Successfully validated 1 migration (execution time 00:00.008s)
Creating Schema History table: "PUBLIC"."flyway_schema_history"
Current version of schema "PUBLIC": << Empty Schema >>
Migrating schema "PUBLIC" to version 1 - Create person table
Successfully applied 1 migration to schema "PUBLIC" (execution time 00:00.033s)

Adding a second migration

次に、二つ目のマイグレーションV2__Add_people.sql/sqlディレクトリに追加します。

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');

これを発行するためにFlywayを実行します。

flyway-5.0.7> flyway migrate

以下のような実行結果になります。

Database: jdbc:h2:file:./foobardb (H2 1.4)
Successfully validated 2 migrations (execution time 00:00.018s)
Current version of schema "PUBLIC": 1
Migrating schema "PUBLIC" to version 2 - Add people
Successfully applied 1 migration to schema "PUBLIC" (execution time 00:00.016s)

Summary

このチュートリアルでは以下について触れました。

マイグレーションが正常に実行されたのを確認できました。

First Steps: API

Prerequisites

Creating the project

以下のコマンドでMaven Archetype Pluginを使用してプロジェクトを生成します。

> 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

Adding the dependencies

pom.xmlにFlywayとH2を追加します。

<project ...>
    ...
    <dependencies>
        <dependency>
            <groupId>org.flywaydb</groupId>
            <artifactId>flyway-core</artifactId>
            <version>5.0.7</version>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.3.170</version>
        </dependency>
        ...
    </dependencies>
    ...
</project>

Integrating Flyway

src/main/java/foobar/App.javaでFlywayとの連携をするためにDBと接続するコードを書きます。

package foobar;

import org.flywaydb.core.Flyway;

public class App {
    public static void main(String[] args) {
        // Flywayインスタンスの生成
        Flyway flyway = new Flyway();

        // DB接続
        flyway.setDataSource("jdbc:h2:file:./target/foobar", "sa", null);

        // マイグレーション開始
        flyway.migrate();
    }
}

Creating the first migration

src/main/resources/db/migrationというマイグレーションディレクトリを作成します。以下のようにsrc/main/resources/db/migration/V1__Create_person_table.sqlという最初のマイグレーションを作ります。

create table PERSON (
    ID int not null,
    NAME varchar(100) not null
);

Executing our program

以下コマンドによりプログラムを実行します。

bar> mvn package exec:java -Dexec.mainClass=foobar.App

実行後、以下のような出力が得られます(タイムスタンプは消してあります)

INFO: Creating schema history table: "PUBLIC"."flyway_schema_history"
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).

Adding a second migration

次のマイグレーションを追加するのにsrc/main/resources/db/migration/V2__Add_people.sqlを作ります。

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');

以下で実行します。

bar> mvn package exec:java -Dexec.mainClass=foobar.App

以下のようになります。

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).

このチュートリアルでは以下について解説しました。

  • プロジェクトとFlywayを連携させる
  • DBと接続するための設定
  • 一組のマイグレーションの作成

マイグレーションが正常に実行されたのを確認できました。

First Steps: Gradle

Prerequisites

  • Java 8 or 9
  • Gradle 3.0 or newer

Setting up the build file

FlywayとH2に接続する設定と連携するbuild.gradleを作成します。

buildscript {
    dependencies {
        classpath 'com.h2database:h2:1.4.191'
    }
}

plugins {
    id "org.flywaydb.flyway" version "5.0.7"
}

flyway {
    url = 'jdbc:h2:file:./target/foobar'
    user = 'sa'
}

Creating the first migration

src/main/resources/db/migration/V1__Create_person_table.sqlという最初のマイグレーションを作ります。

create table PERSON (
    ID int not null,
    NAME varchar(100) not null
);

Migrating the database

DBをマイグレートするためにFlywayを実行します。

> gradle flywayMigrate -i

実行後、以下のような出力になります。

Creating schema history table: "PUBLIC"."flyway_schema_history"
Current version of schema "PUBLIC": << Empty Schema >>
Migrating schema "PUBLIC" to version 1 - Create person table
Successfully applied 1 migration to schema "PUBLIC" (execution time 00:00.062s).

Adding a second migration

二つ目のマイグレーションsrc/main/resources/db/migration/V2__Add_people.sqlを追加します。

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');

以下で実行します。

> gradle flywayMigrate -i

以下のような出力になります。

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

このチュートリアルでは以下について解説しました。

マイグレーションが正常に実行されたのを確認できました。

First Steps: Maven

(似たような内容なんで省略)