ゴールデンウィークにSeasar2を試してみましたが、引き続いて今日はS2DAOを試してみました。
我がThinkPadには、Oracle10gの試用版をインストールしてあるので、そこにGROUPとMEMBERの2つのテーブルを作成。中身のデータは、GROUPがモーニング娘。とかで、MEMBERが吉澤ひとみとか・・・。MEMBER.GROUP_IDは、GROUP.IDと関連しています。
(以下はEclipseの使用が前提の話です。)
さて、S2DAOですが、まずS2DAOのlibはs2-dao-1.0.26.jar、Seasar2のlibはめぼしいものすべてを、ビルドパスに加えます。次に、j2ee.dicon、dao.dicon、log4j.propertiesを、自分のワークスペースにコピー。これで、準備完了。
作らないといけないのは、JavaBeans、Dao、diconファイル、sqlファイルの4種類と、ドライバとなる実行プログラム。
JavaBeansは1テーブル1個ずつ作成。普通にgetter/setterを書いて、S2DAO用にアノテーションを書いていきます。ここが肝。N:1マッピングをする時は、JavaBeansのN側にアノテーションが必要です。
Daoも1テーブル1個ずつ。interfaceとして作ります。今回は、insert()、update()、delete()、get○○By○○()といったメソッドを作成(interfaceなのでメソッド定義だけ)。ここでもアノテーションをうまく書けば、SQLの自動生成も可能になります。
ちなみに、自動生成されないメソッドの場合は、Dao名_メソッド名.sqlというファイルを作り、その中にSQLを書いておけば、それだけでOK。
今回の実行プログラムは、MEMBER.GROUP_IDを指定して該当するMEMBER.NAMEと紐づくGROUP.NAMEを表示するもの(参照系)と、複数のMEMBERを1トランザクションで追加するもの(更新系)の2つを作りました。
参照系は、MemberDaoに追加したgetMemberByGroupId(int groupId)を呼び出してListを貰い、それを表示するように作りました。このgetMemberByGroupIdも、アノテーションでSQL自動生成可能。JavaBeansでN:1マッピングのアノテーションを書いてあるので、aMember.getGroupUnit().getName()といった感じで、関連先のGROUPテーブルに対応したJavaBeansであるGroupUnitが取得できます。
更新系は、実行プログラムがMemberAdderインタフェースを実装したサービス(VudAdder=美勇伝Adder)を呼び出し(DIする)、そのサービスがMemberDao#insert(Member member)を使って3人をMEMBERテーブルに追加するというもの。
3人分の追加が1トランザクションになるようにしたいので、diconファイルで、3人のMemberを追加するサービスであるVudAdderにj2ee.requiredTxをaspectとして追加しました。また、VudAdderが使用するDaoをDIするため、コンストラクタインジェクションの設定をしてあります。
MemberAdder.diconファイル
GroupUnitDao
MemberDao
j2ee.requiredTx
さて、そんな感じで、S2DAOの実験は成功したのですが、ハマった点について。
何も考えずに、1つのサービスを処理するために、diconファイルをあちこちで読んでS2Containerをあちこちで作ったりすると、トランザクションがバラバラになります。
あちこちでS2Containerを作ってもトランザクションさえなければ普通に動きますので要注意です。サービスを呼び出すレイヤのところあたりでS2Containerを作ってサービスをgetComponent()し、そのサービス内で使うコンポーネントは、DIしてあげる(VudAdderがコンストラクタインジェクションでDIされているように・・・)のが良いようです。しかし、どっかで誰かがgetComponent()しなければならないものなんですかね?それ自体もDIされたいんですけどね・・・。要調査。
あと、気になる点。
N:1マッピングで1の方を引っ張ってきますが、逆にN側をINSERTする時に1側のオブジェクトを入れておいたからといって自動で外部KEYが設定されるわけではないです。N側に1側をセットする時に、1側のKEYをN側の外部KEYプロパティにセットするとかの工夫が必要です。例えば、Member#setGroupUnit(GroupUnit gu)において、this.groupUnit = guだけでなく、this.groupUnitId = gu.getId()しておくという話。
総括としては、慣れれば簡単便利に使えそうです。ただ、N:1マッピングのあたりの話や、S2DAOでは1:Nがマッピングされないといった話もあって、オブジェクト指向を本気でやる場合には、力不足の感は否めません。本気にオブジェクト指向しないケースなら、うまく使えそうな気がします。