2012年12月07日
Javaのデータベースマイグレーションツール「Flyway」 #JJUG
Java Advent Calendar 2012 7日目です。
昨日は@megascusさんの「Java Bug Database 参照していますか?」でした。
先日、弊社サービスにJavaのデータベースマイグレーションツール「Flyway」を導入してみたところ、なかなか便利だったので紹介します。
事前準備
まず、マイグレーションの対象になるデータベースを用意します。
このエントリーでは、MySQL 5.5.28を使っています。
CREATE DATABASE flywayexample DEFAULT CHARACTER SET UTF8;
Flywayをプロジェクトに組み込む
ドキュメントによるとFlywayを利用するには以下の3つの手段があるようです。
今回は、Maven Pluginを使った利用方法を紹介します。
MavenでFlywayを利用するには、flyway-maven-pluginが必要です。pom.xmlの設定は以下の通りです。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.ryu22e.flywayexample</groupId>
<artifactId>flywayexample</artifactId>
<version>0.0.1-SNAPSHOT</version>
<description>Example for flyway(http://flywaydb.org/).</description>
<build>
<plugins>
<!-- flyway-maven-pluginの設定 -->
<plugin>
<groupId>com.googlecode.flyway</groupId>
<artifactId>flyway-maven-plugin</artifactId>
<version>2.0.2</version>
<configuration>
<driver>com.mysql.jdbc.Driver</driver>
<url>jdbc:mysql://localhost:3306</url>
<initialDescription>Base Migration</initialDescription>
<schemas>
<schema>flywayexample</schema>
</schemas>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.googlecode.flyway</groupId>
<artifactId>flyway-core</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.21</version>
</dependency>
</dependencies>
</project>
データベースに接続するためのユーザー名・パスワードはpom.xmlに書いていません。
書くこともできるのですが、今回は、$M2_HOME/.m2/settings.xml に書いたユーザー名・パスワードを参照する方法でやってみます。
$M2_HOME/.m2/settings.xml の設定は以下の通りです。
<settings xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<servers>
<server>
<id>flyway-db</id>
<username>username</username>
<password>yourpassword</password>
</server>
</servers>
</settings>
デフォルトではID「flyway-db」のサーバーを参照します。IDを変更したい場合はpom.xmlを
<plugins>
<plugin>
<groupId>com.googlecode.flyway</groupId>
<artifactId>flyway-maven-plugin</artifactId>
<version>2.0.2</version>
<configuration>
<!-- ここにIDを入れる -->
<serverId>example-db</serverId>
</configuration>
</plugin>
</plugins>
のように設定します。
動作確認
まず最初に、Flywayの動作確認をしてみましょう。mvn flyway:info で以下のメッセージが出力されれば、Flywayが正常に動作しています。
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building flywayexample 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- flyway-maven-plugin:2.0.2:info (default-cli) @ flywayexample ---
[INFO] +-------------+------------------------+---------------------+---------+
[INFO] | Version | Description | Installed on | State |
[INFO] +-------------+------------------------+---------------------+---------+
[INFO] | No migrations found |
[INFO] +-------------+------------------------+---------------------+---------+
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.511s
[INFO] Finished at: Sun Dec 02 21:18:09 JST 2012
[INFO] Final Memory: 4M/81M
[INFO] ------------------------------------------------------------------------
Flywayの初期設定
次にFlywayの初期設定を行います。mvn flyway:init を実行します。
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building flywayexample 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- flyway-maven-plugin:2.0.2:init (default-cli) @ flywayexample ---
[INFO] Creating Metadata table: `flywayexample`.`schema_version`
[INFO] Schema initialized with version: 0
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.148s
[INFO] Finished at: Sun Dec 02 21:20:20 JST 2012
[INFO] Final Memory: 4M/81M
[INFO] ------------------------------------------------------------------------
schema_versionというテーブルが作成され、最初のバージョン番号(デフォルトでは0)が記録されます。
もう一度mvn flyway:info を実行すると、現在のバージョンが確認できます。
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building flywayexample 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- flyway-maven-plugin:2.0.2:info (default-cli) @ flywayexample ---
[INFO] +-------------+------------------------+---------------------+---------+
[INFO] | Version | Description | Installed on | State |
[INFO] +-------------+------------------------+---------------------+---------+
[INFO] | 0 | Base Migration | 2012-12-02 21:20:20 | Future |
[INFO] +-------------+------------------------+---------------------+---------+
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.625s
[INFO] Finished at: Sun Dec 02 21:23:01 JST 2012
[INFO] Final Memory: 4M/81M
[INFO] ------------------------------------------------------------------------
最初のSQLを作成する
では、バージョン1として、テーブルを作成してみましょう。
クラスパスが通っている場所に、db/migration フォルダを作成し、以下の内容でV1__Create_book.sql ファイルを作成します。
CREATE TABLE `book`(
`id` mediumint NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
作成したら、mvn compile flyway:migrate を実行します。
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building flywayexample 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- flyway-maven-plugin:2.0.2:migrate (default-cli) @ flywayexample ---
[INFO] Current schema version: 0
[INFO] Migrating to version 1
[INFO] Successfully applied 1 migration (execution time 00:00.148s).
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.756s
[INFO] Finished at: Sun Dec 02 21:32:29 JST 2012
[INFO] Final Memory: 4M/81M
[INFO] ------------------------------------------------------------------------
これで、新しいテーブルが作成されたはずです。mvn flyway:info で確認してみましょう。
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building flywayexample 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- flyway-maven-plugin:2.0.2:info (default-cli) @ flywayexample ---
[INFO] +-------------+------------------------+---------------------+---------+
[INFO] | Version | Description | Installed on | State |
[INFO] +-------------+------------------------+---------------------+---------+
[INFO] | 0 | Base Migration | 2012-12-02 21:20:20 | Missing |
[INFO] | 1 | Create book | 2012-12-02 21:32:29 | Success |
[INFO] +-------------+------------------------+---------------------+---------+
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.567s
[INFO] Finished at: Sun Dec 02 21:34:35 JST 2012
[INFO] Final Memory: 4M/81M
[INFO] ------------------------------------------------------------------------
バージョンが1に変更されています。MySQL上に、新しいテーブルbookが作成されました。
Flywayでは、ファイル名を以下の命名規約に従ったものにすると、マイグレーション対象のSQL文として自動認識してくれます。

Placefolder
「Placefolder」でSQL文に動的に値を埋め込むこともできます。
pom.xml に
<plugins>
<!-- flyway-maven-pluginの設定 -->
<plugin>
<groupId>com.googlecode.flyway</groupId>
<artifactId>flyway-maven-plugin</artifactId>
<version>2.0.2</version>
<configuration>
<placeholders>
<tableName>magazine</tableName>
</placeholders>
</configuration>
</plugin>
</plugins>
と書いて、SQLに
CREATE TABLE `magazine`(
`id` mediumint NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
INSERT INTO `${tableName}` (`title`) VALUES ('test1');
と書くと、mvn compile flyway:migrate 実行時に${tableName}をmagazineに変換してくれます。
また、以下のようにコマンドの引数としてPlacefolderを指定することもできます。
mvn compile flyway:migrate -Dflyway.placeholders.Placefolderの名前=埋め込む値
一旦全ての定義をDROPしたい時は
mvn flyway:clean で全ての定義がDROPされます。
実行すると、問答無用でDROPします。
実行するかどうか確認するオプションでもないのかと調べてみたのですが、見当たりませんでした。
本番環境でこれを実行すると死ぬので、絶対に使わないでください。
(まあ、SSHで直接マイグレーションを実行するような運用にしないでCIに組み込むのが理想なのでしょうが…)
Javaクラスでマイグレーション
SQLファイルだけでなく、Javaクラスでマイグレーションすることもできます。
パッケージdb.migrationの下にSQLファイルと同じ命名規約で(com.googlecode.flyway.core.api.migration.jdbc.JdbcMigrationをimplementした)Javaクラスを作ると、マイグレーション対象として認識してくれます。
package db.migration;
import java.sql.Connection;
import java.sql.PreparedStatement;
import com.googlecode.flyway.core.api.migration.jdbc.JdbcMigration;
public class V2__Insert_into_magazine implements JdbcMigration {
public void migrate(Connection connection) throws Exception {
PreparedStatement statement =
connection
.prepareStatement("INSERT INTO `magazine` (`title`) VALUES ('sample')");
try {
statement.execute();
} finally {
statement.close();
}
}
}
Javaはヒアドキュメントがないので、ちょっと複雑なSQL文になると非常に見難くて、個人的にはあまり使いたいとは思わないのですが、for文で大量にデータを挿入する場合などは便利かもしれません。
最後に
冒頭でも触れたように、Flywayは弊社サービスに最近導入したのですが、特定のフレームワーク・O/Rマッパーに依存したものではないので、特に大きな嵌りどころもありませんでした。
今回紹介したのはほんの触りで、もっと高機能なツールなのですが、ちょっと試しに触ってみる程度なら簡単に理解できるツールです。データベースマイグレーションツールの導入を検討されている方は試してみてはいかがでしょうか。
明日は、@empressiaさんです。