いささか旧聞ですが、2ヶ月ほど前にDBFluteのバージョン1がリリースされました。
DBFluteといえば、非常に強力で柔軟なO/Rマッパーです。
どれぐらい、強力で柔軟かというと、便利すぎて、DBFluteのv1.0リリースを記念として、社内DBFlute信者による布教活動勉強会が先日行われたほどです:
今回は、これを、JavaのPlay!Frameworkで使っちゃおうというのが、本記事です。
ただ、Play!Frameworkについては、最新の2.0ではなく、1.2系を使っています。
と言うのも、まだ2.0系とDBFluteの連携は試してないから、そもそも記事に出来ないという単純な理由です。
とまぁ、そんな前置きは置いておいて、とりあえず、進めていきましょう。
今回の環境
まず、今回の環境は以下の通りです。
特にDBサーバー等は予め用意しておいて下さい。
- Play! Framwork 1.2.5
- DBFlute 1.0.0
- IDE:Eclipse
- DBサーバー:postgres://postgres@dbServer/exampleDb (PostgreSQL9.2)
Play!Frameworkの初期設定
まずはPlay!Frameworkがなければ始まりませんので、公式サイトから最新の1.2.5をダウンロードします。
http://www.playframework.org/
適当な場所に解凍してPlayのプロジェクト作成です。
C:\Bancho\play-1.2.5>play new PlayTheDBFlute
今回は、DBFluteのDIコンテナとしてguiceモジュールを使うので、Playのguiceモジュールを入れてしまいます。(★ポイント1★)
.\PlayTheDBFlute\conf\dependencies.yml
のファイルをテキストエディターソフトで開きguiceモジュールを使う指定を書き加えます。
# Application dependencies require: - play - play -> guice 1.2
そして、依存性の解決と、eclipsifyします。
C:\Bancho\play-1.2.5>cd PlayTheDBFlute C:\Bancho\play-1.2.5\PlayTheDBFlute>..\play dependencies C:\Bancho\play-1.2.5\PlayTheDBFlute>..\play eclipsify
ここまでやったら、今作ったプロジェクトをEclipseのインポートメニューで「既存プロジェクトをワークスペースへ」で取り込みます。
guiceモジュールの導入作業が入っていますが、ここまでは、至って普通のPlay! Frameworkの初期設定です。
DBFluteのインストール
次に、DBFluteのインストールです。
DBFluteのダウンロードや設定は、EclipseならばEMechaが定番ですので、DBFluteの公式(http://dbflute.seasar.org/ja/environment/setup/emecha.html)サイトの手順を参考に、EclipseにEMechaを入れて、DBFlute New Clientを行います。
この時の、DBFluteの設定ですが、今回はDIコンテナにguiceを使うのでguiceを指定します。そのため、設定画面はこのようなイメージになります。(★ポイント2★)
DBFluteの最新バージョンの確認と、ダウンロードを行って「完了」を押せば、DBFluteに必要なファイル群がプロジェクト内に用意されます。
DBFluteのインストールが終わったら、DBFluteの既定の場所にreplace-schema.sqlを用意して置くのも忘れずに。
今回は、非常にシンプルな、以下のようなテーブルを用意しました。
CREATE TABLE example_table ( id serial PRIMARY KEY NOT NULL, message text NOT NULL );
このあたりの設定はDBFluteユーザーには定番ですね。
DBFluteとPlay!の連携
ここからが、ある意味本題で、Play!上でDBFluteを使うための各種設定です。
DBFluteの各種クラスの生成
Play!が使用するソースファイルの場所と、DBFluteが各種ソースファイルを生成する場所は異なるため、これを同じ場所に設定しなおします。(★ポイント3★)
/dbflute_exampleDb/dfprop/basicInfoMap.dfprop
の設定例を見ながら、
; generateOutputDirectory = ../app
と書き換えましょう。
その上で、DBFluteの定番コマンド、
C:\Bancho\play-1.2.5\PlayTheDBFlute\dbflute_exampleDb> replace-schema.bat C:\Bancho\play-1.2.5\PlayTheDBFlute\dbflute_exampleDb> jdbc.bat C:\Bancho\play-1.2.5\PlayTheDBFlute\dbflute_exampleDb> generate.bat
の実行をすれば、先に用意したreplace-schema.sqlを元に、データベースが初期化されて、DBFluteのコードがプロジェクト内に生成されます。
このとき、ビルドエラーが出ていますが、原因は、dbfluteのライブラリが不足していることなので、Eclipseのプロジェクトのプロパティーで、
/mydbflute/dbflute-1.0.0/lib/dbflute-runtime.jar
をビルドパスに加えてあげれば、ビルドエラーは無くなるはずです。
DIの設定とコネクション管理
さて、DBFluteのコードが正常に生成されたので今度は、GuiceのDI対象にDBFluteを追加します。
DBFluteでは、この処理のためのクラスDBFluteModuleを自動生成しているため、これでGuiceの設定をします。
また、Play!には元々データベース操作の仕組みは持っています。
元はと言えば、「それを使うよりもDBFluteが使いたい!」という、わがままから始まったのが今回のお話。
さらにわがままを言うと、「データベースの接続管理やExceptionが出たときのRollback等は、Play!が元々持っている機能に任せたい!」とわがままをさらに言ってみたいです。
これらの、諸問題(?)を解決するために、以下のようなクラスを作ります。(★ポイント4★)
package framework.guice; import java.sql.Connection; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import javax.sql.DataSource; import org.seasar.dbflute.jdbc.DataSourceHandler; import play.db.DB; import play.modules.guice.GuiceSupport; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Module; import dbflute.allcommon.DBFluteConfig; import dbflute.allcommon.DBFluteModule; /** * GuiceにDBFluteのクラス群を利用できるようにする。 * また、その際に、DBFluteのデータベース接続を * Playのコネクション管理を利用するように設定する。 */ public class GuiceConfigure extends GuiceSupport { /* * (非 Javadoc) * @see play.modules.guice.GuiceSupport#configure() */ @Override protected Injector configure() { // まず、DBFluteのDBのコネクションを、Playの管理するものにする。 DBFluteConfig config = DBFluteConfig.getInstance(); config.unlock(); config.setDataSourceHandler(new DataSourceHandler() { @Override public Connection getConnection(DataSource source) throws SQLException { return DB.getConnection(); } }); config.lock(); // DBFluteのクラス群をDIコンテナに認識させる。 List<Module> moduleList = new ArrayList<Module>(); moduleList.add(new DBFluteModule(DB.datasource)); return Guice.createInjector(moduleList.toArray(new Module[]{})); } }
これで、DBFluteをPlay!のコード上でDI出来るようになり、さらにデータベースの接続の設定はPlay!Frameworkに任せることが出来るようになりましたので、データベースの接続情報をPlay!の設定ファイルに書き込んでおきましょう。
/conf/application.conf
の
db=postgres://postgres@dbServer/exampleDb
を書き加えます。
以上、めでたしめでたし・・・
と思いきや、まだ問題があります。
実はこのままでは、一見動くのですが、1回のリクエストでSQLを2回実行するとエラーになるなど、方々で問題が起きます。
そこで以下のように、@javax.persistence.Entity で注釈したダミークラスを用意します。このアノテーションが付いたクラスが存在すると、Play!FrameworkはJPA エンティティマネージャを開始し、その実装でDBFluteとの接続の諸問題も解決されます。(★ポイント5★)
※詳しい説明は割愛。詳しくはDBクラスのgetConnectionメソッドあたりを辿って見て下さい。
package models; import javax.persistence.Entity; import play.db.jpa.Model; /** * DBFluteがPlay!FrameworkのDBを利用する諸問題を解決するためのクラス。 */ @Entity public class Dummy extends Model { }
実際に使ってみる
では、実際に使ってみます。
使用方法は難しくありません。DBFluteを使いたいクラスに対して、@InjectSupportのアノテーションを付加し、DBFluteのBehaviorのプロパティに@Injectのアノテーションを付加。あとは普通にDBFluteを使うだけです。
@play.modules.guice.InjectSupport public class SampleModel { @javax.inject.Inject private static ExampleTableBhv exampleTableBhv; public void check() { ExampleTableCB cb = exampleTableBhv.newMyConditionBean(); int count = exampleTableBhv.selectCount(cb); if (count == 0) { //適当な処理 } ExampleTable ent = exampleTableBhv.newMyEntity(); ent.setMessage("This is test message!"); exampleTableBhv.insert(ent); // 例外発生時には自動的にロールバックするが、手動でロールバックする場合は以下のようにする // JPA.setRollbackOnly();ロールバックする場合 return ; } }
以上、駆け足でPlay!Framework1.2系とDBFluteとの連携手順でした。