より実践的なLiquiBase使用例

ここからは、実際のシステム開発でLiquiBaseを使用することを想定したシナリオを実践することで、さらに知識を深めていこう。

まずシナリオの前提を以下に挙げる。

  • DB内に定義するテーブルは、先ほど作成したpersonテーブルのみ
  • DBスキーマが開発者ごとに用意されており、自由に変更できる
  • 複数人で同時に開発を進めており、バージョン管理システムでソースの管理を行っている
  • 既に本番稼動しているシステムがあり、現在はバージョンアップの開発中である

シナリオ1 (タグ付けとロールバックについて)

こうした前提の下、まずは以下のようなシナリオを想定する。

  1. 開発者「shiraishi」はpersonテーブルに、論理削除用のカラム「delete_flag」を追加して、開発を進めようとした

  2. しかし、バージョン管理システムからチェックアウトしたチェンジログファイルを見ると、開発者「satou」が既に同じ目的のカラム「is_deleted」を追加していた

  3. shiraishiはコミットされている方を優先するため、自分のスキーマを一旦変更前に戻し、その後「satou」の変更をスキーマに反映する

いささか作為的なシナリオな上に、「リファクタリング」という趣旨からは外れているが、説明のしやすさを優先したということでご理解いただきたい。

スキーマへのタグ付け

開発者shiraishiは、データベースの変更を伴うコード修正を行う。開発時は何が起きるかわからないため、いつでもスキーマを元に戻せるように、現在のスキーマにタグを付けておくことにする。

タグをつけるために使用するサブコマンドは「tag」だ。以下のように、「liquibase tag <タグ名>」と指定することで、現在のスキーマの状態にタグを付けておき、後で参照することができる (JDBC関連のオプションは省略)。

liquibase tag before_update_person

ここでは、「before_update_person」というタグを指定している。

delete_flg列の追加

さて次に開発者shiraishiは、personテーブルに対してdelete_flgという列を追加するためにチェンジログを修正する。前半の説明で使用したファイルと同じ「db_changelog.xml」に、以下の記述を追加するだけだ。

<!-- 新しいチェンジログを追加 -->
<changeSet id="2" author="shiraishi">
    <!-- カラムの追加 -->
    <addColumn tableName="person">
        <column name="delete_flg" type="boolean" defaultValue="0">
            <constraints nullable="false"/>
        </column>
    </addColumn>
</changeSet>

新しいchangeSetを定義し、addColumnタグを使用して列を追加する。ファイルをこのように編集した後、以前と同じようにmigrateサブコマンドを実行すればスキーマの変更は完了だ。

> liquibase migrate

MySQLクライアントで確認する。

mysql> desc person;
+------------+-------------+------+-----+---------+-------+
| Field      | Type        | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+-------+
| id         | int(11)     | NO   | PRI |         |       |
| first_name | varchar(20) | NO   |     |         |       |
| last_name  | varchar(20) | NO   |     |         |       |
| age        | int(11)     | YES  |     | NULL    |       |
| delete_flg | tinyint(1)  | NO   |     | 0       |       |
+------------+-------------+------+-----+---------+-------+
5 rows in set (0.00 sec)

問題なくdelete_flg列が追加されている。

変更のロールバック

その後shiraishiは、バージョン管理システムとローカルファイルの同期をとってみると、チェンジログファイルへの変更が衝突していることに気づく。開発者satouがファイルの同一箇所を編集しており、しかも、自分が追加しようとしていた論理削除用のフラグがsatouによって追加されている (致命的なコミュニケーション不足だ)。

リスト: satouによって追加された論理削除フラグ「is_deleted」

<changeSet id="1" author="satou">
    <addColumn tableName="person">
        <column name="is_deleted" type="boolean" defaultValue="0">
            <constraints nullable="false"/>
        </column>
    </addColumn>
</changeSet>

こうなったら、当然コミットされている方の変更を優先しなくてはならない。shiraishiは自分がまた開発をほとんど進めておらず、取り返しがきくことにほっとしながら、一旦自分のスキーマをタグ「before_update_person」までロールバックする

ロールバックするためのサブコマンドは「rollback」だ。以下のようにして、タグ名を指定してコマンドを実行すれば、スキーマを以前の状態に戻すことができる。

> liquibase rollback before_update_person

こうしてロールバックした後に、最新バージョンのチェンジログファイルを基にDBのマイグレーション(migrationサブコマンド)を行うことで、プロジェクト内における最新のスキーマと自分のスキーマを同期させることができた。

ここで説明したように、スキーマの「ロールバック」が可能なのはLiquiBaseの非常に強力な機能だ。プログラムコードのリファクタリング同様、「いつでも元に戻せる」という安心さえあれば、DBも大幅にリファクタリングできるというものだ。

とはいえ、実際にはあらゆるスキーマ操作がロールバックできるわけではない。例えばDROP TABLEやMODIFY COLUMNなどは元に戻せない。

「rollback」以外にも、ロールバックを行うためのサブコマンドが存在し、タグ以外の方法で「どこまで戻すか」を指定することもできる。

  • rollbackToDate - 指定した時点のスキーマまでロールバックする
  • rollbackCount - 指定した数ぶん、changeSetをロールバックする