ImplessDAOユーザーズガイド

最終更新日: 2007年8月 15日


目次

1. ImplessDAOの概要
ImplessDAOとは何か
ImplessDAOのメリット
クイックスタート
ディストリビューションの入手
ディストリビューションのインストール
2. ImplessDAOの使い方
補完される抽象メソッド
SQLの指定方法
実行結果の取得
背後にあるJDBCオブジェクト
3. ImplessDAOの制限

表目次

2.1. DBアクセス抽象メソッドの戻り値
2.2. 式の記述
2.3. ImplessDAOのクラスと背後にあるJDBCクラス

第1章 ImplessDAOの概要

ImplessDAOとは何か

ImplessDAOとは、JDBCの面倒なコードを記述せずにDBへアクセスするためのライブラリです。 名前も、IMPlementation-LESS DAOから来てます。 DBアクセスのためのライブラリは既にいくつもありますが、ImplessDAOは次のような目標で設計されています。

  1. XMLの設定ファイルは使わない。EclipseなどのIDEに特別なプラグインを追加しなくても不自由なく開発が行えます。

  2. DBの機能を最大限に活用することを妨げない。実際のアプリケーションではDBMSの変更など滅多にないので、もっとも適切なSQL文をプログラマが記述できます。

  3. ImplessDAOをバイパスし直接JDBCを利用できる手段も用意し、開発者が最適な手段を利用できるようにする。効率あるいはその他の理由からImplessDAOの振る舞いが好ましくないときに、あなたは選択的かつシームレスにJDBCを直接利用したコードを記述できます。

ImplessDAOの一番簡単な使い方を示しましょう。 まず、net.sf.implessdao.AbstractDAOを継承して抽象クラスをを作成します。この抽象クラスでは、DBアクセスを行う抽象メソッド(ここではexecuteCreateCarTable) を定義します。このメソッドにnet.sf.implessdao.Sqlアノーテーションを使って、このメソッドが呼ばれたときに実行したいSQLを指定します。

public abstract class CarDAO extends net.sf.implessdao.AbstractDAO {
	@Sql(sql="create table car(ID INT PRIMARY KEY, name VARCHAR(32) NOT NULL)")
	public abstract void executeCreateCarTable();
}
	
次に、java.sql.Connectionのインスタンスを取得するためのファクトリインスタンスを生成します。 ImplessDAOにはjava.sql.DriverManagerを利用したファクトリクラスnet.sf.implessdao.DriverManagerConnectionFactoryと JNDIから取得したデータソースを利用したファクトリクラスnet.sf.implessdao.DataSourceConnectionFactoryがあらかじめ用意されています。 後は、先ほど作った抽象クラス(CarDAO)のクラスオブジェクトとコネクションファクトリーを引数に、net.sf.implessdao.DAOFactorycreateWithCompletionメソッドを呼び出します。 結果として得られるCarDAOのインスタンスは、抽象メソッドであったexecuteCreateCarTableメソッドがSqlアノーテーションで指定されたSQLを実行する ように補完されています。そのため、executeCreateCarTableメソッドを呼び出すことができます。
		IConnectionFactory connectionFactory = DriverManagerConnectionFactory
				.createConnectionFactory(
						"org.apache.derby.jdbc.EmbeddedDriver",
						"jdbc:derby:testdb;create=true", "", "");
		CarDAO dao = DAOFactory.createWithCompletion(CarDAO.class,
				connectionFactory);
		dao.executeCreateCarTable();
	
このように、抽象メソッドを実装する代わりにSQL文を指定することで、DBを操作することができます。

ImplessDAOのメリット

ImplessDAOは他のO-Rマッピングフレームワークと比較して、次のようなメリットがあります。

  1. XMLなどを使った設定ファイルを利用せずすべてJavaで記述するため、コンパイル時にエラーが指摘される、デプロイに特別な配慮がいらない。

  2. ImplessDAOの既定の動作を変更するあるいはバイパスする方法がいくつか用意されており、カスタマイズしやすい。また、ImplessDAOを使うか使わないかの二者択一ではなく、部分的活用もできる。

クイックスタート

ディストリビューションの入手

ImplessDAOを利用するには、ここ から最新のディストリビューションを取得します。 ディストリビューションは"implessdao-x.x.zip"というファイル名になっいます(x.xはバージョンを表します)。 また、ImplessDAOはOGNL 2.6.9Jakarta Commons - Logging 1.1javassist 3.4に依存しているため、これらのjarファイルを入手しクラスパスに加えておきます。

ディストリビューションのインストール

入手したディストリビューションを展開します。\optというディレクトリの下にインストールするには、 次のようにします。


cd \opt
jar xvf \path\to\implessdao-x.x.zip
        
以上で、"\opt\implessdao-x.x"というディレクトリの直下に、"implessdao.jar"と含むいくつかのファイルやディレクトリが展開されます。 あとは、あなたのプログラムのコンパイルや実行時に、"\opt\implessdao-x.x\implessdao.jar"をクラスパスに加えるだけで、ImplessDAOを利用することができます。 ImplessDAOを利用するには、通常、クラスnet.sf.implessdao.DAOFactoryの メソッドcreateWithCompletionを呼び出すだけです。 詳しくは、JavaDoc APIドキュメントと下記を参照してください。

第2章 ImplessDAOの使い方

ImplessDAOを使うには、を作成して大きく分けて次の3つを行うことにほかなりません。

  1. DBにアクセスする抽象メソッド(以下「DBアクセス抽象メソッド」と呼ぶ)をnet.sf.implessdao.AbstractDAOのサブクラスで定義する

  2. 上記のDBアクセス抽象メソッドに対して、DBアクセス抽象メソッドが呼ばれたときに実行するSQLを指定する。

  3. DBアクセス抽象メソッドを呼び出し実行結果を取得する。

では、それぞれの詳細を説明していきましょう。

補完される抽象メソッド

ImplessDAOで補完される抽象メソッドは、以下の条件を満たすものです。

  1. publicかつabstract

  2. メソッド名が、execute, update, insert, delete, selectのいずれかで始まる

これらは、DBアクセス抽象メソッドとみなされます。 上記以外のメソッドを抽象メソッドにしても補完は行われません。補完されない抽象メソッドがあるかどうかは、メソッドtest()を呼び出すことで 実行時に検査されます。単体テストのコードから呼び出すようにするとよいでしょう。

DBアクセス抽象メソッドは、指定されたSQLを実行し結果を返すようImplessDAOにより補完されます。SQLの実行結果の返し方は、メソッド名が何で始まるかによって次のように決まっています。

表 2.1. DBアクセス抽象メソッドの戻り値

メソッド名の先頭可能な戻り値の型戻り値の説明
executevoid-
insertvoid/int/Integerint/Integerを返す場合は、挿入/更新/削除したレコード数
update
delete
selectnet.sf.implessdao.IRSIteratableselectした結果のレコード

DBアクセス抽象メソッドの引数については特に制約はありません。 また、DBアクセス抽象メソッド実行中に発生した例外は、java.lang.RuntimeExceptonとして 投げられます。特にjava.sql.SQLException等は、net.sf.implessdao.DAORuntimeException にラップされて投げられます。

SQLの指定方法

DBアクセス抽象メソッドで実行するSQLを指定する一つ目の方法はnet.sf.implessdao.Sqlアノーテーションを用いることです。 例えば、下記のようにSQLを指定します。

@Sql(sql="select count(*) from someTable where ${{0:INTEGER}} <= col1 and col1 < ${{1:INTEGER}}")
public abstract IRSIteratable<Map<String,Object>> selectBetween(int lower, int upper);
	
「${{」と「}}」で囲まれた部分は、ImplessDAOによって「?」IN パラメータプレースホルダーに置換されます。 各IN パラメータプレースホルダーには値が設定されますが、どのような値をどのように設定するかは、「${{」と「}}」の 内側の記述に依存します。「${{」と「}}」の内側には、下記の形式で式とアクセッサ指定子を記述します。
:アクセッサ指定子
	
式には、0以上の整数、またはOGNL式を記述することができます。

表 2.2. 式の記述

書式「?」IN パラメータプレースホルダーに設定される値
0以上の整数DBアクセス抽象メソッドの引数の値。式の値0:DBアクセス抽象メソッドの最初の引数、式の値1:二つ目の引数、以下...
OGNL式OGNL式の評価結果の値。DBアクセス抽象メソッドの引数は、OGNL式の中では、「args」配列としてアクセスできます。 したがって、${{0:INTEGER}}は、${{args[0]:INTEGER}}と等価です。OGNL式を使うとMapの値やJavaBeansのプロパティに簡単にアクセスできます。

アクセッサ指定子は、式で示される値をどのようにして「?」IN パラメータプレースホルダーに設定するかを制御します。 アクセッサ(net.sf.implessdao.IAccessor)とは、アプリケーションにおけるデータ表現と、 JDBCが提供するデータ表現の双方向の変換を抽象化したものです。このアクセッサを列挙したenum(net.sf.implessdao.IAccessorEnumを実装したenum)の アクセッサ指定子には、このenum定数名を指定します。net.sf.implessdao.AbstractDAOのメソッドgetAccessorEnum()のデフォルトの実装は、 net.sf.implessdao.DefaultAccessorを返すようになっており、「INTEGER」などのはDefaultAccessorのenum定数名なのです。 メソッドgetAccessorEnum()をオーバーライドすることで、アクセッサを独自のものにカスタマイズすることもできます。

Sqlアノーテーションを利用して指定できるSQLは静的な文字列に限定されるので、DBアクセス抽象メソッドの引数の値に応じて SQLを変更したい場合には使えません。動的にSQLを生成するには、Sqlアノーテーションの代わりに、DBアクセス抽象メソッドと同じ引数を持ち、DBアクセス抽象メソッドのメソッド名の後ろに「SQL」を追加した、 Stringを返すメソッド定義します。

public String selectBetweenSQL(Integer lower, Integer upper) {
	String sql = "select count(*) from someTable";
	String sep = " where ";
	if (lower != null) {
		sql += sep + "${{0:INTEGER}} <= col1"; // IN パラメータプレースホルダーを使用
		sep = " and ";
	}
	if (upper != null) {
		sql += sep + "col1 < " + upper; // 文字列に埋め込み
	}
	return sql;
}
public abstract IRSIteratable<Map<String,Object>> selectBetween(Integer lower, Integer upper);
	

実行結果の取得

select以外で始まるDBアクセス抽象メソッドは、何も返さないかレコード数を返すかなので特に説明は必要ないでしょう。 selectで始まるDBアクセス抽象メソッドは、net.sf.implessdao.IRSIteratableを返します。 IRSIteratableは、java.sql.ResultSetをアプリケーションが扱いやすいjava.util.Iteratorを 通じて利用できるようにしたものです。IRSIteratableの実装は、レコードをどのように格納するのか(Mapに格納するのか、特定のJavaBeansに格納するのかなど)によっていくつか考えられます。 どのIRSIteratableを利用するかは、net.sf.implessdao.IRSIteratableFactoryを指定することで制御します。 IRSIteratableFactoryを指定するには2つの方法があります。一つ目の方法は、AbstractDAOのメソッドdefaultRIF()をオーバーライドすることです。 このメソッドの戻り値が、デフォルトで利用されるIRSIteratableFactoryとなります。二つ目の方法は、動的なSQLの指定と同じように、DBアクセス抽象メソッドと同じ引数で、DBアクセス抽象メソッド名の後ろに「RIF」を追加した、 IRSIteratableFactoryを返すメソッドを定義することです。当然、二つ目の方法が一つ目の方法より優されます。

AbstractDAOのデフォルトの実装では、net.sf.implessdao.DefaultRSIteratableFactoryのインスタンスを返すように実装されており、 DefaultRSIteratableFactoryは、カラム名をキーとしたMapにレコードの値を格納するnet.sf.implessdao.DefaultRSIteratableを 生成します。また、DefaultRSIteratableFactoryは、ResultSetからレコードの値を取り出すのにnet.sf.implessdao.DefaultDBAccessMapperを使用します。 DefaultDBAccessMapperは、ResultSetのメタデータから取得したカラムの型等の情報を元に、値を取り出します。

背後にあるJDBCオブジェクト

ImplessDAOのいくつかのクラスの背後には、関連するJDBCのオブジェクトが存在します。リソースの開放やエラーの解析を行うには、 時としてその関連を理解することが重要となります。

表 2.3. ImplessDAOのクラスと背後にあるJDBCクラス

ImplessDAOのクラス背後にあるJDBCクラス
IDAO/AbstractDAOjava.sql.Connection
IRSIteratable/IRSIteratorjava.sql.PreparedStatement, java.sql.ResultSet

上記のような関連があるため、ImplessDAOの各クラスのインスタンスの利用が終わり次第、finally節などからメソッドclose()を呼び出し リソースの開放を行うべきです。

AbstractDAOは、Connectionを提供するためのメソッドprepareConnection()を提供しています。 これを利用して、JDBCを直接操作するメソッドを定義することもできます。また、明示的なcommitを行うためのメソッドを次のように定義することもできます。

	public void commit() throws SQLException {
		prepareConnection().commit();
	}
	

第3章 ImplessDAOの制限

ImplessDAOは現在のところ、一つのjava.sql.Connectionを複数のDAOインスタンスで共有する 簡単な方法を提供していません。そのため、 UserTransactionを利用した分散トランザクション環境下、あるは、オートコミットモードのConnection、あるいは単一のDAOインスタンスによるトランザクションの完遂を 前提としています。