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 ファイルを作成する
<?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
だけが適用される。
-
##データを登録する
<?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 を実行する。
SQL> select * from test_Table;
ID CODE VALUE
---------- -------- --------
1 hoge HOGE
mysql> select * from test_table;
+----+------+-------+
| ID | CODE | VALUE |
+----+------+-------+
| 1 | hoge | HOGE |
+----+------+-------+
1 row in set (0.00 sec)
データが登録されている。
##毎回入力するオプションをプロパティファイルにまとめる
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 に反映させる
<?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 間の差分を取る
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
<?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 を生成する
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
が出力される。
<?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 が無いか確認する
<?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 が同期できているかをチェックできる。
#参考