MyBatis Migrations 3.3.0 の hook が便利

at 2016-11-17 08:37 (UTC)

MyBatis Migrations を利用しているけど、環境別に初期データを登録する術がなかった。

  • 開発時には最低限のデータは入っていたほうが便利
  • 顧客に確認してもらうデータは個人情報をマスクしたりもする
  • テスト環境では冪等性のために初期データはいらない

仕方ないので migration を実行した後に、別の方法を取らないと上記のようなことが出来ない。

しかし、3.3.0-SNAPSHOT で含まれた Migration hooks を使えばこのあたりが解決できる。

なにが出来るのか??

hook には以下の種類がある。名前を見れば、まあだいたいわかるはず。

  • hook_before_up
  • hook_before_each_up
  • hook_after_each_up
  • hook_after_up
  • hook_before_down
  • hook_before_each_down
  • hook_after_each_down
  • hook_after_down

まず development.propertiestest.properties で実行するスクリプトファイルを指定するため、環境ごとに実行する SQL を切り替えることが出来る。
またスクリプトは JSR-223 に対応していればよいので、Java のクラス郡を利用することが出来る。

つまり hook_before_up で実行ログをとって、hook_after_each_up で初期データを登録し、hook_after_up でサーバーを再起動する。なんてシナリオができる。環境ごとに!!

サンプル

-- db/scripts/20161117175523_create_users.sql
CREATE TABLE users (
    id SERIAL NOT NULL PRIMARY KEY
  , login TEXT NOT NULL
  , email TEXT NOT NULL
  , name TEXT NOT NULL
);

CREATE UNIQUE INDEX users_uniq1 ON users(login);

-- //@UNDO
DROP TABLE users;

こんなマイグレーションファイルがあったとする。

まず、設定ファイルに使用するスクリプトの設定を追加する。

# db/environments/development.properties
hook_after_each_up=JavaScript:dev_after_each_up.js

hooks ディレクトリを作成し、スクリプトファイルを作成する。

// db/hooks/dev_after_each_up.js
switch (hookContext.getChange().getId()) {
  case 20161117175523: // create_users.sql
    hookContext.executeSql("INSERT INTO users (login, email, name) VALUES ('admin', 'admin@example.com', 'システム管理者');");
    break;
}

これで、マイグレーションを実行すると各step毎に dev_after_each_up.js が実行されて初期データが無事登録できた。

さらに

  • このままだと、初期データ登録が一つの巨大なファイルになってしまう
  • SQL が文字列なのでエディタのハイライトなどの恩恵が受けられない

といったことが考えられるので、せっかく Java の資産が利用できるのだからもう少しに便利にしよう。

// db/hooks/dev_after_each_up.js
var path = java.nio.file.Paths.get(migrationPaths.getHookPath() + "/after_each_up/development/" + hookContext.getChange().getId() + ".sql");
if (java.nio.file.Files.exists(path)) {
  var query = java.lang.String.join("\n", java.nio.file.Files.readAllLines(path, java.nio.charset.StandardCharsets.UTF_8));
  hookContext.executeSql(query);
}

これで、初期データが存在する場合には db/hook/after_each_up/development/20161117175523.sql のようにバージョンに合わせて SQL を置いておけば実行されるようになる。
マイグレーションと連動してくれるので、現在のバージョンにあった初期データが登録される。少し古いバージョンで開発している人が最新のデータ登録でテーブルがなくてエラーなんてこともない。

万歳!!