DBリファクタリングツール「LiquiBase」の基本的な使い方をひとめぐり。
環境
OS
Windows7 64bit SP1
Java
1.7.0_55
データベース
MySQL
Ver 14.14 Distrib 5.5.28, for Win64 (x86)
Oracle
11.2.0.1.0
LiquiBase とは?
- DB をバージョン管理してしまうツール。
- DB へのパッチ当てや、マイグレーションなど色々利用できる。
- DB への変更を XML ファイル(ChangeLog)で管理する。
- 異なる DB 製品であっても、同じ XML ファイルで変更を反映することができる。
- シーケンスオブジェクトなど、特定の DB 製品でしかサポートされていないオブジェクトの作成も可能。
 
- タグ付けなどを行って、任意の状態に DB を ロールバック したりできる。
ひとめぐり
インストール
こちら から、バイナリをダウンロードする。
今回は、 liquibase-3.0.8-bin.zip をダウンロードした(2014/06/24 現在最新の 3.2.0 にはデグレがあって、タグの生成でぬるぽが発生する)。
ダウンロードした zip を解凍したフォルダにパスを通す。
以下のコマンドを実行して、インストールが完了したことを確認する。
>liquibase --version
Liquibase Version: 3.0.8
Changelog ファイルを作成する
sample.xml
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
  xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
         http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
  
  <changeSet id="1" author="opengl-8080">
    <createTable tableName="TEST_TABLE">
      <column name="ID" type="int">
        <constraints primaryKey="true" nullable="false"/>
      </column>
      <column name="CODE" type="varchar2(8)">
        <constraints nullable="false"/>
      </column>
      <column name="VALUE" type="varchar2(8)">
        <constraints nullable="false"/>
      </column>
    </createTable>
  </changeSet>
</databaseChangeLog>
実行する
>liquibase --driver=com.mysql.jdbc.Driver --classpath=.\mysql-connector-java-5.1.22-bin.jar --changeLogFile=sample.xml --url="jdbc:mysql://localhost/test" --username=test --password=test update
Liquibase Update Successful
>liquibase --driver=oracle.jdbc.driver.OracleDriver --classpath=.\ojdbc6.jar --changeLogFile=sample.xml --url="jdbc:oracle:thin:@localhost:1521:test" --username=test --password=test update
Liquibase Update Successful
確認する
>mysql -u test -ptest test
mysql> show tables;
+-----------------------+
| Tables_in_test        |
+-----------------------+
| databasechangelog     |
| databasechangeloglock |
| test_table            |
+-----------------------+
3 rows in set (0.00 sec)
mysql> desc test_table;
+-------+------------+------+-----+---------+-------+
| Field | Type       | Null | Key | Default | Extra |
+-------+------------+------+-----+---------+-------+
| ID    | int(11)    | NO   | PRI | NULL    |       |
| CODE  | varchar(8) | NO   |     | NULL    |       |
| VALUE | varchar(8) | NO   |     | NULL    |       |
+-------+------------+------+-----+---------+-------+
3 rows in set (0.02 sec)
>sqlplus test/test@test
SQL> select table_name from user_tables;
TABLE_NAME
------------------------------
DATABASECHANGELOGLOCK
DATABASECHANGELOG
TEST_TABLE
SQL> desc test_table;
 名前                                      NULL?    型
 ----------------------------------------- -------- ----------------------------
 ID                                        NOT NULL NUMBER(10)
 CODE                                      NOT NULL VARCHAR2(8)
 VALUE                                     NOT NULL VARCHAR2(8)
SQL> SELECT CONSTRAINT_NAME, CONSTRAINT_TYPE FROM USER_CONSTRAINTS WHERE TABLE_NAME='TEST_TABLE' AND CONSTRAINT_TYPE='P';
CONSTRAINT_NAME                C
------------------------------ -
PK_TEST_TABLE                  P
ここまでの説明
- ChangeLog という XML ファイルに、「テーブル作成」「データの挿入・変更」「テーブル定義の変更」などを記述する。
- ChangeLog ファイルを指定して liquibase を実行することで、定義した変更をデータベースに反映できる。
- ChangeLog ファイルには、 changeSetを1つの単位として変更を定義する。
- 
changeSetは1トランザクションで実行される。
 
- データベースには liquibase が使用するテーブルが自動で作成される(DATABASECHANGELOGとDATABASECHANGELOGLOCK)。- 
DATABASECHANGELOGには、適用したchangeSetの履歴が保存され、一度適用したchangeSetが繰り返し実行されないようになっている。
- 
DATABASECHANGELOGには記録されていない、新しいchangeSetだけが適用される。
 
- 
データを登録する
sample.xml
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
  xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
         http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
  
  <changeSet id="1" author="opengl-8080">
    <createTable tableName="TEST_TABLE">
      <column name="ID" type="int">
        <constraints primaryKey="true" nullable="false"/>
      </column>
      <column name="CODE" type="varchar2(8)">
        <constraints nullable="false"/>
      </column>
      <column name="VALUE" type="varchar2(8)">
        <constraints nullable="false"/>
      </column>
    </createTable>
  </changeSet>
  
  <changeSet id="2" author="opengl-8080">
    <insert tableName="TEST_TABLE">
      <column name="ID" value="1" />
      <column name="CODE" value="hoge" />
      <column name="VALUE" value="HOGE" />
    </insert>
  </changeSet>
</databaseChangeLog>
先ほどと同じ方法で liquibase を実行する。
oracle
SQL> select * from test_Table;
        ID CODE     VALUE
---------- -------- --------
         1 hoge     HOGE
mysql
mysql> select * from test_table;
+----+------+-------+
| ID | CODE | VALUE |
+----+------+-------+
|  1 | hoge | HOGE  |
+----+------+-------+
1 row in set (0.00 sec)
データが登録されている。
毎回入力するオプションをプロパティファイルにまとめる
liquibase.properties
driver=oracle.jdbc.driver.OracleDriver
changeLogFile=sample.xml
classpath=.\\ojdbc6.jar
url=jdbc:oracle:thin:@localhost:1521:test
username=test
password=test
- 
driverなどを毎回コマンドのオプションに設定するのは面倒なので、カレントフォルダにliquibase.propertiesを作成し、そこに設定を記載する。
- バックスラッシュは二重にする。
- url のダブルクォーテーションは不要なので取り除く。
これで以下のコマンドで実行できるようになる。
>liquibase update
タグを設定してロールバックする
タグを設定する
>liquibase tag hoge
Successfully tagged TEST@jdbc:oracle:thin:@localhost:1521:test
Liquibase 'tag' Successful
ChangeLog を更新して DB に反映させる
sample.xml
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
  xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
         http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
  
  <changeSet id="1" author="opengl-8080">
    <createTable tableName="TEST_TABLE">
      <column name="ID" type="int">
        <constraints primaryKey="true" nullable="false"/>
      </column>
      <column name="CODE" type="varchar2(8)">
        <constraints nullable="false"/>
      </column>
      <column name="VALUE" type="varchar2(8)">
        <constraints nullable="false"/>
      </column>
    </createTable>
  </changeSet>
  
  <changeSet id="2" author="opengl-8080">
    <insert tableName="TEST_TABLE">
      <column name="ID" value="1" />
      <column name="CODE" value="hoge" />
      <column name="VALUE" value="HOGE" />
    </insert>
  </changeSet>
  
  <changeSet id="3" author="opengl-8080">
    <addColumn tableName="TEST_TABLE">
      <column name="MEMO" type="varchar2(16)" />
    </addColumn>
  </changeSet>
  
</databaseChangeLog>
>liquibase update
Liquibase Update Successful
確認
>sqlplus test/test@test
SQL> desc test_table;
 名前                                      NULL?    型
 ----------------------------------------- -------- ----------------------------
 ID                                        NOT NULL NUMBER(10)
 CODE                                      NOT NULL VARCHAR2(8)
 VALUE                                     NOT NULL VARCHAR2(8)
 MEMO                                               VARCHAR2(16)
SQL> quit
>mysql -u test -ptest test
mysql> desc test_table;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| ID    | int(11)     | NO   | PRI | NULL    |       |
| CODE  | varchar(8)  | NO   |     | NULL    |       |
| VALUE | varchar(8)  | NO   |     | NULL    |       |
| MEMO  | varchar(16) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
4 rows in set (0.00 sec)
タグを設定したところまでロールバックする
>liquibase rollback hoge
Liquibase Rollback Successful
確認
>sqlplus test/test@test
SQL> desc test_table;
 名前                                      NULL?    型
 ----------------------------------------- -------- ----------------------------
 ID                                        NOT NULL NUMBER(10)
 CODE                                      NOT NULL VARCHAR2(8)
 VALUE                                     NOT NULL VARCHAR2(8)
SQL> quit
>mysql -u test -ptest test
mysql> desc test_table;
+-------+------------+------+-----+---------+-------+
| Field | Type       | Null | Key | Default | Extra |
+-------+------------+------+-----+---------+-------+
| ID    | int(11)    | NO   | PRI | NULL    |       |
| CODE  | varchar(8) | NO   |     | NULL    |       |
| VALUE | varchar(8) | NO   |     | NULL    |       |
+-------+------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
- 
liquibase tag <タグ名>で現在の状態でしるし(タグ)を付けることができる。
- 
liquibase rollback <タグ名>で、指定したタグまで DB の状態を戻すことができる。
異なる DB 間の差分を取る
liquibase.properties
driver=oracle.jdbc.driver.OracleDriver
changeLogFile=sample.xml
classpath=.\\ojdbc6.jar
url=jdbc:oracle:thin:@localhost:1521:test
username=test
password=test
referenceUrl=jdbc:oracle:thin:@localhost:1521:test
referenceUsername=test2
referencePassword=test2
>liquibase diff
Diff Results:
Reference Database: TEST2 @ jdbc:oracle:thin:@localhost:1521:test (Default Schema: TEST2)
Comparison Database: TEST @ jdbc:oracle:thin:@localhost:1521:test (Default Schema: TEST)
Product Name: EQUAL
Product Version: EQUAL
Missing Catalog(s):
     TEST2
Unexpected Catalog(s):
     TEST
Changed Catalog(s): NONE
Missing Column(s):
     HOGE.ID
     TEST_TABLE.MEMO
Unexpected Column(s): NONE
Changed Column(s): NONE
Missing Foreign Key(s): NONE
Unexpected Foreign Key(s): NONE
Changed Foreign Key(s): NONE
Missing Index(s): NONE
Unexpected Index(s): NONE
Changed Index(s): NONE
Missing Primary Key(s): NONE
Unexpected Primary Key(s): NONE
Changed Primary Key(s): NONE
Missing Schema(s): NONE
Unexpected Schema(s): NONE
Changed Schema(s): NONE
Missing Sequence(s): NONE
Unexpected Sequence(s):
     TEST_TABLE_SEQ
Changed Sequence(s): NONE
Missing Table(s):
     HOGE
Unexpected Table(s): NONE
Changed Table(s): NONE
Missing Unique Constraint(s): NONE
Unexpected Unique Constraint(s): NONE
Changed Unique Constraint(s): NONE
Missing View(s): NONE
Unexpected View(s): NONE
Changed View(s): NONE
Liquibase 'diff' Successful
- 比較する DB の設定を reference**で追加してdiffコマンドを実行すると、 DB 間の差分を取ることができる。
差分を ChangeLog 形式で出力する
>liquibase diffChangeLog
Liquibase 'diffChangeLog' Successful
sample.xml
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
  xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
         http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
  
  <changeSet id="1" author="opengl-8080">
    <createTable tableName="TEST_TABLE">
      <column name="ID" type="int">
        <constraints primaryKey="true" nullable="false"/>
      </column>
      <column name="CODE" type="varchar2(8)">
        <constraints nullable="false"/>
      </column>
      <column name="VALUE" type="varchar2(8)">
        <constraints nullable="false"/>
      </column>
    </createTable>
  </changeSet>
  
  <changeSet id="2" author="opengl-8080">
    <insert tableName="TEST_TABLE">
      <column name="ID" value="1" />
      <column name="CODE" value="hoge" />
      <column name="VALUE" value="HOGE" />
    </insert>
  </changeSet>
  
  <changeSet id="3" author="opengl-8080">
    <addColumn tableName="TEST_TABLE">
      <column name="MEMO" type="varchar2(16)" />
    </addColumn>
  </changeSet>
  
    <changeSet author="xxx (generated)" id="1403615872792-1">
        <createTable tableName="HOGE">
            <column name="ID" type="NUMBER(38)"/>
        </createTable>
    </changeSet>
    <changeSet author="xxx (generated)" id="1403615872792-2">
        <addColumn tableName="TEST_TABLE">
            <column name="MEMO" type="VARCHAR(16)"/>
        </addColumn>
    </changeSet>
    <changeSet author="xxx (generated)" id="1403615872792-3">
        <dropSequence sequenceName="TEST_TABLE_SEQ"/>
    </changeSet>
</databaseChangeLog>
- 
diffChangeLogコマンドを実行すると、差分を ChangeLog に追記させることができる。
データベースの状態を Javadoc 風の HTML で出力する
>liquibase dbDoc docs
Liquibase 'dbDoc' Successful
>dir /b docs
authors
authors.html
changelogs
changelogs.html
columns
currenttables.html
globalnav.html
index.html
overview-summary.html
pending
recent
stylesheet.css
tables
- 
liquibase dbDoc <出力先フォルダ>で DB の状態を Javadoc 風の HTML で出力できる。
- 各テーブルのカラム定義や、どこまで ChangeLog が適用されているかなどが確認できる。
既存の DB から ChangeLog を生成する
liquibase.properties
driver=oracle.jdbc.driver.OracleDriver
changeLogFile=generate.xml
classpath=.\\ojdbc6.jar
url=jdbc:oracle:thin:@localhost:1521:test
username=test
password=test
>liquibase generateChangeLog
Liquibase 'generateChangeLog' Successful
生成が成功すると、 liquibase.properties で指定していた changeLogFile=generate.xml が出力される。
generate.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.0.xsd">
    <changeSet author="xxx (generated)" id="1403616998655-1">
        <createSequence sequenceName="TEST_TABLE_SEQ"/>
    </changeSet>
    <changeSet author="xxx (generated)" id="1403616998655-2">
        <createTable tableName="TEST_TABLE">
            <column name="ID" type="NUMBER(10, 0)">
                <constraints nullable="false"/>
            </column>
            <column name="CODE" type="VARCHAR2(8)">
                <constraints nullable="false"/>
            </column>
            <column name="VALUE" type="VARCHAR2(8)">
                <constraints nullable="false"/>
            </column>
        </createTable>
    </changeSet>
    <changeSet author="xxx (generated)" id="1403616998655-3">
        <addPrimaryKey columnNames="ID" constraintName="PK_TEST_TABLE" tableName="TEST_TABLE"/>
    </changeSet>
</databaseChangeLog>
- 
generateChangeLogで、既存の DB の状態を ChangeLog ファイルで出力できる。
- これなら、 Liquibase を適用していない既存の DB でも途中から Liquibase を使い始められる? wktk
- ただし、現状以下のオブジェクトは出力できない。
- ストアドプロシージャ
- ファンクション
- パッケージ
- トリガー
 
DB に反映されていない ChangeSet を確認する
>liquibase status --verbose
1 change sets have not been applied to TEST@jdbc:oracle:thin:@localhost:1521:test
     sample.xml::3::opengl-8080
Liquibase 'status' Successful
- 
liquibase status --verboseで、まだ DB に反映されていない ChangeSet が分かる。
DB に適用されているのに ChangeLog に記載されていない変な ChangeSet が無いか確認する
sample.xml
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
  xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
         http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
  
  <changeSet id="1" author="opengl-8080">
    <createTable tableName="TEST_TABLE">
      <column name="ID" type="int">
        <constraints primaryKey="true" nullable="false"/>
      </column>
      <column name="CODE" type="varchar2(8)">
        <constraints nullable="false"/>
      </column>
      <column name="VALUE" type="varchar2(8)">
        <constraints nullable="false"/>
      </column>
    </createTable>
  </changeSet>
  
  <changeSet id="2" author="opengl-8080">
    <insert tableName="TEST_TABLE">
      <column name="ID" value="1" />
      <column name="CODE" value="hoge" />
      <column name="VALUE" value="HOGE" />
    </insert>
  </changeSet>
  
  <changeSet id="3" author="opengl-8080">
    <addColumn tableName="TEST_TABLE">
      <column name="MEMO" type="varchar2(16)" />
    </addColumn>
  </changeSet>
  
</databaseChangeLog>
>liquibase unexpectedChangeSets --verbose
1 unexpected changes were found in TEST@jdbc:oracle:thin:@localhost:1521:test
     sample.xml::4::opengl-8080
Liquibase 'unexpectedChangeSets' Successful
- DB には、 sample.xml の ID=4, author=opengl-8080 という ChangeSet が適用されたという記録が残っている。
- しかし、実際の sample.xml にはそんな ChangeSet は無い。
- つまり、 sample.xml が最新になっていない、もしくは誤って ChangeSet を消してしまったような状態。
- これをチェックするには、 liquibase unexpectedChangeSets --verboseを実行する。
- 前述の liquibase statusと合わせることで、 DB と ChangeLog が同期できているかをチェックできる。

