前の記事 に続いてSeasar conferenceをレポートする。
Kuina-Dao と Dolteng による Easy Enterprise
3つめのコマはTuigwaaのセッションとどちらにしようか迷ったけれどもKuinaにした。講演者は小林浩一さん。このセッションは小さめの部屋だったのだけれど、大変な人気で、立ち見の人でいっぱい。私も立ち見になった。
Kuina
KuinaはJavaの高水準データベースAPIであるJPA(Java Persistence API)をサポートするseasarのプロダクト。Kuina-coreとKuinaDaoから成る。Kuina-coreはSeasar.orgによるJPA実装で、まだ構想段階。KuinaDaoはJPAをさらに便利に使うためのフレームワーク。
JPAにおけるDAOの意義
JPAはJSR-220(EJB 3.0)の一部をなすO/R Mappingの仕組みである。現在Sunなどから実装が出ている。全体の構成はHibernateに近く、Hibernateも3.0でこの標準に追随している。アプリケーションはEntityオブジェクトをO/R Mapperを通じて管理する。SQLはMapperが発行し、アプリケーション側ではSQLを意識する必要がない。
JPAは永続コンテキストという概念を持っている。HibernateのSessionに相当する。永続化されたEntityは特定のコンテキストに属し、コンテキストを通じて取得/更新される。コンテキストを操作するためのJava interfaceがEntityManagerである。これによってかなり便利にEntityを扱うことができる。
EntityManagerがあればDAOは要らないというのが一つの見解であろう。EntityManagerは管理下にある任意のクラスに対して検索・取得/作成/更新/削除機能を持っているから、これは汎用のDAOであると考えることもできそうだ。実際、EntityManagerを用いてDAOを作ってみても、作成/更新/削除の実装は殆ど一行、コンテキストのメソッドを呼び出すだけだ。DAOの有り難みがない。(特にGenericsを使えばGenerics以前のHibernateのようなダウンキャストの必要もないしなー)
しかし、検索は違う。Criteriaを作成して、パラメータ変数を割り当て、と書いていくといくらでも複雑になってしまう。特に、条件に応じて動的にクエリ条件を組み立てるような場合にはStringBuilderを通じてJPQLを組み立てるようなことも必要になりうる。こういう処理をロジックに埋め込むのはやはりあまり嬉しいことではなく、DAOを作るメリットが出てくる。
KuinaDao
そこでKuinaDaoである。KuinaDaoはJPAをベースとして簡単にDAOを構築できるフレームワークである。ユーザーは実際にはDAOの実装を書く必要がない。DAOの挙動を規約に従ったinterfaceによって宣言するだけで、実装はフレームワークが作ってくれる。動的に作成された実装をS2ContainerのAOPの仕組みを通じて差し込んでくれる。
KuinaDaoは5つのクエリの仕組みを持っている。
- NamedQuery: JPAの名前付き問い合わせを利用する
- Query by Example: Entityオブジェクトを渡して、プロパティがそれに一致するEntityを取得する
- Query by DTO: DTOオブジェクトを渡して、プロパティがそれに一致するEntityを取得する
- Query by Parameter: メソッドの引数を条件式としてそれを満たすEntityを取得する
- SQL Query: S2Dao/Uujiと同じ。JPAを経由しないので直接JDBCを用いるので注意。
NamedQuery
エンティティ名/メソッド名に合致するするNamedクエリがJPAの設定ファイルに存在するとき、設定されているJPQLを使用する。JPQL中のパラメータには同名のメソッド引数を割り当てる。
Employeeエンティティに対してのDAOは、プロジェクトのrootパッケージを
<var>proj_root</var>
とするとき、規約により interface <var>proj_root</var>.dao.EmployeeDao
である。このとき、 EmployeeDao#findByName
というメソッドを定義する。
public interiface EmployeeDao {
List<Employee> findByName(String name);
}
EmployeeDaoの実装はDIを通じて取得できる。すると、findByNameメソッドを呼んだとき、Employee.findByNameという名前のクエリをJPAの設定から探してきてそれを実行する。設定ファイルに書かれたJPQL中の:nameというパラメータは引数nameの値に展開される。
Query by Example
Dao interfaceに、JPA Entityを唯一の引数とするようなメソッドを宣言するとQuery by Exampleとなる。引数に渡したオブジェクトのうち、nullでないプロパティを用いてそれらのプロパティについての=条件をANDで結んでwhere節を構築する。
例えば、EmployeeDaoに
List<Employee> findByExample(Employee example);
というメソッドがあったとする。これにnameプロパティとbirthdayプロパティが非nullであるEmployeeオブジェクトを渡すと、これは
SELECT emp FROM Employee emp WHERE emp.name = :name AND emp.birthday = :birthday
というJPQLクエリを内部で作成して発行する。
Query by DTO
発想はQuery by Exampleと同じであるが、検索条件として渡すのはエンティティではなくDTOである。また、=条件以外の条件式も用いることができる。条件として用いる演算子はDTOのプロパティ名から導出される。
例えば、name_LIKEというプロパティを定義しておいて非nullな値を入れておくと、
emp.name LIKE :name
という条件式が構築され、パラメータ:nameがプロパティのの値に展開される。これ以外の演算子を表すsuffixとしては
- EQ(=), NE(<>)
- GT(>), GE(>=), LT(<), LE(<=)
- STARTS(LIKE '...%'), ENDS(LIKE '%...'), CONTAINS(LIKE '%...%')
- IN, IS_NULL, IS_NOT_NULL
がある。suffixを付けない場合のデフォルトはEQである。また、"関連名$プロパティ名"のように$区切りで関連をたどることもできる。
Query by Parameter
Query by DTOと同じであるが、メソッドの引数としてDTOでなく、プロパティの値そのものを渡す。名前に用いる演算子suffixもQuery by DTOと同様である。
List<Employee> findByNameAndBirthDay(String name, Date birthday_GE)
などと宣言すると、このメソッドは
emp.name = :name emp.birthday >= :birthday
のような条件節を構築する。
Diigu
上記では何度もメソッドの引数名に基づくクエリ構築を行なっている。しかし、標準ではJavaのclassファイルには引数名の情報は書かれていない。如何にリフレクションを用いてもそもそも存在しない情報は取り出せない。そこで、classファイルにメソッドの引数名情報を埋め込んでそれを利用するための仕組みがDiiguである。
KuinaDaoのパラメータ自動展開はDiiguを活用している。
使い分け
KuinaDaoはやはり、JPAによるマッピングの上に成り立つものであるから、Table - Classを機械的にマッピングしていくには向いている。けれども、SQLを自由に発行したい場合にはUujiやS2Daoのほうが向いているであろうとのこと。UujiとKuinaDaoは適用領域が異なるであろう、と。
S2JFace
業務アプリケーションをわざわざWebアプリケーションとして構築する理由は何か。利用範囲は限られているからGUIアプリケーションでも構わないのに。
一つにはWebアプリケーションのほうが配布の手間が掛からないということであるが、これはJava Web Startがあるから既に解決済みである。残るは次のような問題である。
S2JFaceはこれを解決しようとするものである。HTMLに似た簡単なXMLファイルを書けばSWTを用いたGUIを構築できる。将来的にはWYSIWYGなGUIエディタも作成予定。イベントの発生をメソッドに結びつけるのもAnnotationで簡単にでき、Listenerを構築して登録するような手間が不要である。
夕暮れ
懇親会
前回と同様ピザとビール。まぁ、手にとって食べやすいけどね。
羽生さんの理事引退挨拶とか。
artonさんと話して、自分の得意な領域、やりたい領域を把握してるのがすごい、と。「やっぱり俺がやりたいのはそういうネットワークプログラミングで、言語は関係ないんだな」という台詞がかっこよかった。