S2JDBC-Genでデータベースリファクタリング

このエントリでは、S2JDBC-Genを使って手軽にデータベースリファクタリングをする方法を紹介します。S2JDBC-Genは、Javaコード(S2JDBCのエンティティ)の修正をデータベーススキーマに反映させるツールです。
S2JDBC-Genの実行に必要な動作環境は、EclipseプラグインDoltengを使うと簡単に用意できます。Eclipseはバージョン3.4、Doltengは昨日リリースされたばかりの0.33.0を使います。

長くなりすぎたので見出しを。。。

Doltengのインストール

まずは、Doltengをインストールしましょう。Doltengの他に、便利な機能を持つDbLauncharとResourceSynchronizerもいっしょにインストールします。インストールするには、メニューの「Help」-「Software Updates」から行います。「Add Site」ボタンで http://eclipse.seasar.org/updates/3.2/http://eclipse.seasar.org/updates/3.3/ を追加し、インストールするプロダクトを選びます。



Doltengは両方のサイトに表示されますが、http://eclipse.seasar.org/updates/3.3/ の下に表示されるDoltengを選択します。

Doltengプロジェクトの作成

Doltengのインストールが終わったら、メニューの「File」-「New」-「Project」と選択し、プロジェクト作成のダイアログを開きます。



Dolteng Projectを選択します。すると次のダイアログが開きます。



Presentationに「SAStruts」、Persistenceに「S2JDBC」を選ぶのがポイント。「Finish」ボタンを押すとプロジェクトが生成されます。



プロジェクトの直下に「s2jdbc-gen-build.xml」が含まれていることに注目してください。S2JDBC-Genはこのビルドファイルから起動します。

Antのコンソールエンコーディングの設定 (Windows上でEclipse3.4を動かす場合にだけ必要)

WindowsのEclipse3.4上でAntを動かす場合のみですが、コンソールエンコーディングを設定しないと、日本語がコンソールに表示されません。文字化けして出力されるならまだいいのですが、何も出力されないので、動いているのかどうかわからなくなってしまうのです。困ったものです><。Windowsを使っていなかったりEclipse3.4を使っていなかったりする場合は読み飛ばしてください。

まず、「s2jdbc-gen-build.xml」を右クリックし、「Run as」-「External Tools Configurations」を選択します。



次に、左側のツリーの「Ant Build」をダブルクリックするか左上隅のアイコンを押し新規の設定を作成します。新規の設定ができたら、「Targets」タグを選び、「gen-entity」のみにチェックをつけ、一番上のNameの入力項目でわかりやすい名前に変更します。



それから、「Common」タグを選び、「Console Encoding」で「MS932」を選択します。



最後に「Apply」ボタンを押します。これで、「gen-entity」ターゲットについては、設定が完了です。
ここまでの作業を、すべてのターゲットに対して行います。左側のツリーの「Ant Build」をダブルクリックするか左上隅のアイコンを押すところから始めてください。「s2jdbc-gen-build.xml」には3つのターゲットが設定されているので、最終的にはこうなります。



H2の起動とデータの確認

DbLauncharでH2データベースを起動します。起動するには、プロジェクトを右クリックしメニューから「H2」-「Start H2 Server」を選びます。



コンソールに

TCP server running on tcp://localhost:9092 (only local connections)
Web server running on http://localhost:8082 (only local connections)

と表示されればOKです。
次にデータベースの中身を確認します。プロジェクトを右クリックしメニューから「H2」-「View Database Server」と選びます。



ブラウザが立ち上がりログイン画面が表示されます。ログインすると、EMPテーブルとDEPTテーブルがあらかじめ用意されていることがわかります。

データベースからエンティティの生成

では、S2JDBC-Genの機能をつかってデータベースからエンティティを生成しましょう。まずは、メニューから「Window」-「View」-「Ant」とし、Ant Viewを開きましょう。そして、Ant Viewに「s2jdbc-gen-build.xml」をドラッグ & ドロップします。



Ant Viewでgen-entityターゲットを選択し、緑の実行アイコンを押すと、コンソールにgen-entityターゲットを実行したことを示すログが出力されます。最終的に「BUILD SUCCESSFUL」と表示されれば成功です。

ソースフォルダを見るとエンティティやサービスのクラスが生成されていることがわかります。(ResourceSynchronizerをインストールしていない場合は、プロジェクトを選択しF5を押すなどしてリフレッシュしないと、表示されません)



ちなみに、S2JDBC-Genでは、データベースからのエンティティの生成は最初の一度だけのみ行うことを想定しています。そのため、Generation Gapパターンは採用していません。エンティティ生成後は、エンティティの修正をデータベースに反映させるということを繰り返すことになるので必要ないのです。

データベースの修正をエンティティに反映させることを繰り返しながら開発をしたい場合は、S2JDBC-GenよりもDBFluteを利用されることをお奨めします。DBFluteの開発者、久保さんの考えも参考になると思います。http://d.hatena.ne.jp/jflute/20080907/1220778306

エンティティからDDLとダンプデータの生成

エンティティを生成したら、とりあえずDDLも生成します。これから、DBリファクタリングをしていきますが、そのまえにDDLを生成しておくと、リファクタリングが終わったあとであっても必要であれば修正前の状態に戻ることができます
DDLとダンプデータを生成するには、Ant Viewからgen-ddlターゲットを実行します。実行すると、プロジェクトの直下にdbというディレクトリが作成され、その下にDDLやダンプデータが生成されます。



ここまでが、DBリファクタリングをするために必要な事前処理でした。以降では、リファクタリングを実際に行ってみます。

DBリファクタリング(新たなカラムの導入)

新しいカラムを追加してみましょう。ここでは、EMPテーブルにAGEという年齢を表すカラムを追加します。

EMPテーブルに対応するクラスはEmpですので、まずはEmpクラスに次のプロパティを追加します。

	@Column(precision = 3, nullable = true, unique = false)
	public Integer age;

次に、Ant Viewからgen-ddlターゲットを実行します。0002ディレクトリが新しく作成されます。



DDLを確認してみましょう。0002/create/010-table/emp.sqlを開きます。

create table EMP (
    ID bigint generated by default as identity,
    EMP_NO integer not null,
    EMP_NAME varchar(20),
    AGE integer, -- ここ!
    MGR_ID integer,
    HIREDATE date,
    SAL decimal(7,2),
    DEPT_ID integer,
    VERSION_NO integer,
    constraint EMP_PK primary key(ID)
);

ちゃんとAGEカラムが追加されています。
それから、データはどうでしょうか?0002/create/040-dump/emp.csvを開きます。



当然ながらAGEの列が空です。(上の画像はCSVエディタのCassava Editorcsvファイルを開いたところです。Cassava Editorは、CSVの閲覧、編集にとても便利です。S2JDBC-Genに適したCassava Editorのお奨めの設定方法については、ドキュメントを参照してください)

ここでは、Cassava Editorの機能を使って適当に連番を振ってしまいましょう。



DDLCSVのデータを実際にデータベースに反映させるには、Ant Viewからmigrateターゲットを実行します。migrateが終わったら、H2のコンソールで「SELECT * FROM EMP」を実行して、リファクタリングが成功しているか見てみましょう。



ちゃんとAGEカラムが追加され、データも入っています。どうでしょう?簡単ですね!

DBリファクタリングカラム名の変更)

今度は、カラム名を変更してみましょう。ここでは、EMPテーブルのEMP_NAMEカラムをNAMEカラムに変更します。
さっそく、EmpクラスのempNameプロパティをnameプロパティに修正します。

	@Column(length = 20, nullable = true, unique = false)
        //public String empName;
	public String name;

次に、Ant Viewからgen-ddlターゲットを実行します。0003ディレクトリが新しく作成されます。



DDLを確認してみましょう。0003/create/010-table/emp.sqlを開きます。

create table EMP (
    ID bigint generated by default as identity,
    EMP_NO integer not null,
    NAME varchar(20), -- ここ!
    AGE integer,
    MGR_ID integer,
    HIREDATE date,
    SAL decimal(7,2),
    DEPT_ID integer,
    VERSION_NO integer,
    constraint EMP_PK primary key(ID)
);

ちゃんとEMP_NAMEカラムがNAMEカラムになっていますね。
それから、データはどうでしょう。0003/create/040-dump/emp.csvを開きます。



あれ、NAMEカラムが空ですね。「名前を変更したらデータを引き継げないのかよ、使えねー」って思いました?大丈夫、変更前のデータ(0002/create/040-dump/emp.csv)からコピーすればいいのです。



Cassava Editorを使うと列のコピーはとても簡単です。

これで、DDLの変更とデータの調整は終わりですので、Ant Viewからmigrateターゲットを実行します。最後に、H2のコンソールで「SELECT * FROM EMP」を実行して、リファクタリングが成功しているか確認してみましょう。



Good!
EMP_NAMEカラムがNAMEカラムになって、データも入っています。これも簡単ですね。

DBリファクタリングのコツ

コードのリファクタリングもそうですが、DBリファクタリングも小さく1つずつ行った方が簡単です。上で「新たなカラムの導入」と「カラム名の変更」の2つの例をやってみましたが、たとえば、これを同時にやるのは危険です。エンティティの修正、gen-ddl、migrateのサイクルは小さくして、できれば、migrateの後にはテストを動かし動作確認するのがいいと思います。

SVNなどを使って複数人で開発するときは、自分の環境でmigrateとそのテストが終わってからコミットするようにします。他の人は、svn updateで新しいコードとDDLCSVを取り込んでmigrateするだけで最新の環境になります。

さぁ、データベースリファクタリングをはじめよう

Javaコードからデータベーススキーマリファクタリングする手法は従来の方法と異なるので最初は違和感を覚えるかもしれませんが、実際に使ってみるとテンポよく開発できると思います。
質問やわからないところがあれば、このブログにコメントするかMLに投稿してください。お気軽にどうぞー。

あと、まだまだですがドキュメントも以前よりは充実させているのでこちらも参考にしてください。
http://s2container.seasar.org/2.4/ja/s2jdbc_gen/index.html