単純にデータベースアクセスを行う場合には、XDaoとXDaoFactoryのみでデータベースアクセスの実装が可能になります。また、推奨はしませんが、XMLによってSQLを管理しないシンプルな作りのDAOを実装したい場合にはSDaoを利用することによって可能になります。
XDaoFactoryはXDaoインスタンスを作成するためのFactoryクラスです。従って、このクラスのメソッドは通常はcreateDaoInstance(String id)しか使用しません。
XDaoFactoryを初期化するにはXMLファイルを指定する必要がありますが、その方法にはファイルパスによる指定する方法とInputStreamによって指定する方法とがあります。
以下のようにコンストラクタにファイルパスを直接記入して、インスタンスを作成します。
XDaoFactory factory = new XDaoFactory("etc/test-config.xml");
XDao dao = factory.createDaoInstance("TestDAO");
ファイルパスを指定してXDaoFactoryのインスタンスを作成した場合には、createDaoInstance()を呼び出す毎に、XMLファイルのタイムスタンプを確認して、リロードが必要な場合にはリロードを行います。
InputStreamによってXMLを指定する場合には、以下のようにCLASSPATH内からファイルを検索してXDaoFactoryのコンストラクタにセットします。
ClassLoader loader = Thread.currentThread().getContextClassLoader();
java.io.InputStream is =
loader.getResourceAsStream("test-config.xml");
XDaoFactory factory = new XDaoFactory(is);
XDao dao = factory.createDaoInstance("TestDAO");
InputStreamによってXMLを指定した場合には、ファイルパスを直接指定した場合と違って、ファイルのタイムスタンプのチェックは行いませんので注意してください。
APIの詳細は
JavaDocを確認してください。
XDaoのpublicなメソッドのほとんどは、単純な1トランザクションのデータベースアクセスを実行します。ただし、EJBのSessionBeanによるCMTのように、宣言的なトランザクション管理が行われている場合はそのかぎりではありません。EJBの場合にはトランザクション管理はCMTが優先されます。
もし、もっと厳密なトランザクションによるデータベースアクセスが行いたい場合には、XDaoを継承した
GeneralizedXDaoクラスを利用します。
データベースへ問い合わせを行い、その結果をMapのList、Object[][]、JavaBeansのList、LazyDynaBeanのListで取得します。下記のJavaコードでは、同一の問い合わせを違う戻り値で取得しています。
XDaoFactory factory = new XDaoFactory("etc/test-config.xml");
XDao dao = factory.createDaoInstance("TestDAO");
// 検索条件の設定
DynaBean parambean = new LazyDynaBean();
parambean.set("name", "A");
// DAOの実行
List mapList = dao.query("test4", parambean);
List beanList = dao.queryForBeans("test4", parambean);
List dynabeanList = dao.queryForDynaBean("test4", parambean);
Object[][] arrayList = dao.queryForArray("test4", parambean);
// DB接続が全て終了したらDAOをリリース
dao.release();
メソッド |
処理概要 |
List query(String, Object) |
データベースを検索し、結果を1レコード1MapのListで返します。検索条件は、MapまたはJavaBeansまたはDynaBeanで指定します。JavaBeansの場合はプロパティ名がKeyになります。 |
List queryForBeans(String, Object) |
データベースを検索し、結果を1レコード1JavaBeansのListで返します。検索条件は、MapまたはJavaBeansまたはDynaBeanで指定します。JavaBeansの場合はプロパティ名がKeyになります。 |
List queryForDynaBean(String, Object) |
データベースを検索し、結果を1レコード1LazyDynaBeanのListで返します。検索条件は、MapまたはJavaBeansまたはDynaBeanで指定します。JavaBeansの場合はプロパティ名がKeyになります。 |
Object[][] queryForArray(String, Object) |
データベースを検索し、結果を2次元配列で返します。その場合、最初の1行はヘッダ行になります。検索条件は、MapまたはJavaBeansまたはDynaBeanで指定します。JavaBeansの場合はプロパティ名がKeyになります。 |
APIの詳細は
JavaDocを確認してください。
Robbie DAOでは、データベースの検索結果をJavaBeansに変換する場合に、XMLなどの外部に定義した情報によってマッピングするのではなく独自の命名規則によって行います。queryForBeans()メソッドでのカラム名とJavaBeansのプロパティへのマッピングは、以下のように行われます。
データベースカラム名(エイリアス)
|
JavaBeansプロパティ名
|
USER、user
|
user
|
USERNAME、username、USER_NAME、user_name
|
userName、username
|
上記のように、カラム内に「_」(アンダーバー)が存在する場合には「_」を削除し、大文字小文字を無視して一致するプロパティにデータをセットしています。
データベースへの更新を行い、その更新件数を返します。QueryのIdとパラメータ用のMap、またはJavaBeansを引数とします。JavaBeansを引数とする場合には、XMLの記述はkey属性の名前=JavaBeasnのプロパティ名である必要があります。
下記の2つのupdate処理は同じSQLを発行しています。
XDaoFactory factory = new XDaoFactory("etc/test-config.xml");
XDao dao = factory.createDaoInstance("TestDAO");
// パラメータMapの設定
Map params = new HashMap();
params.put("id", new Integer(4));
params.put("name", "EEE");
params.put("age", new Integer(4));
// パラメータBeanの作成
TestBean bean = new TestBean();
bean.setId(4);
bean.setName("EEE");
bean.setAge(19);
// DAOの実行
dao.update("test10", params); ← Mapが引数
dao.update("test10", bean); ← JavaBeansが引数
// DB接続が全て終了したらDAOをリリース
dao.release();
メソッド |
処理概要 |
int update(String, Object) |
データベースを更新し、更新件数を返します。更新情報はMapまたはJavaBeansで指定します。 |
APIの詳細は
JavaDocを確認してください。
updateメソッドは更新処理が一意制約や参照制約で失敗し、SQLExceptionをキャッチすると、更新件数に-1を返します。その場合、getSQLException()メソッドを使用することで直前の処理で取得したSQLExceptionを参照することが可能です。
// DAOの実行
int updateCount = dao.update("test9", params);
// 更新件数の確認
if (updateCount == -1) {
SQLException ex = dao.getSQLException();
if (ex.getErrorCode() == 1) {
// Oracleの場合一意制約違反のエラーコードは、1
System.out.println("一意制約違反です。");
}
}
どのような原因で更新できなかったかは、java.sql.SQLException#getErrorCode()で確認することが出来ます。ただし、このgetErrorCode()の戻り値はデータベース製品に依存するため、製品の仕様にあった実装を行う必要があることに、注意して下さい。
Ver 1.1.0から追加されたAPIです。JDBCリソースを使用している場合には、このメソッドを使用してスレッド内でプーリングされているConnectionを開放する(閉じる)必要があります。
DataSourceを使用してConnectionを取得している場合には、サーバサイドのコネクションプールの仕組みを利用していると考えられるため、このメソッドを呼び出しても何も処理は行いません。しかし、慣例としてデータベースアクセスが終了したときにはこのメソッドを常に呼び出すように実装することを強く推奨します。
Ver 1.0.0ではXDaoが簡易なデータベース接続を行うことしかできず、トランザクション処理を行いたい場合にはEJBやJTSでのトランザクション管理機能を利用する必要がありました。しかし、Ver 1.0.1以降では、このGeneralizedXDaoを使用することによって、単独で複雑なトランザクション処理を行うことが可能です。
GeneralizedXDaoは、XDaoと同様に一つのテーブルに対して1つのDAOを定義するわけではありません。Daoと利用するSQLになんら関係は存在しないため、自由にSQLを実行して複数のテーブルへアクセスすることが可能です。

したがって、上記のように業務ロジックを実装するクラスでトランザクションの管理を行うような実装となります。
GeneralizedXDaoを使用するとXDaoと違って、下記のようなrollback()、commit()、setAutoCommit()のようなトランザクションを意識するようなAPIを使用することが可能になります。
XDaoFactory factory = new XDaoFactory("etc/test-config.xml");
GeneralizedXDao dao =
(GeneralizedXDao)factory.createDaoInstance("GeneralizedXDao");
try {
// Connectionの取得
dao.openConnection();
dao.setAutoCommit(false);
// パラメータMapの設定
Map params = new HashMap();
params.put("id", new Integer(4));
params.put("name", "EEE");
params.put("age", new Integer(4));
int count = dao.executeUpdate("test9", params);
if (count != 1) {
dao.rollback();
} else {
dao.commit();
}
System.out.println("updateCount=" + count);
} catch(Exception ex) {
try {
dao.rollback();
} catch(Exception ex2) {
}
throw ex;
} finally {
try {
// Connectionを閉じる
dao.closeConnection();
// DB接続が全て終了したらDAOをリリース
dao.release();
} catch(Exception ex) {
// 何もしない
}
}
GeneralizedXDaoのAPI一覧は以下のようになります。
メソッド |
処理概要 |
openConnection()
|
有効なConnectionオブジェクトをセットします。
|
closeConnection()
|
メンバ変数にセットされているConnectionオブジェクトをclose()します。
|
isClosed()
|
メンバ変数にセットされているConnectionオブジェクトがclose()しているかどうかを返します。このメソッドはJDBCトランザクションで使用するため、JTAなどのトランザクションコンテナの管理下では適切に動作しない場合があります。事前に動作確認を行ってください。
|
setAutoCommit(boolean)
|
JDBCのAutoCommitモードを指定します。このメソッドはJDBCトランザクションで使用するため、JTAなどのトランザクションコンテナの管理下では適切に動作しない場合があります。事前に動作確認を行ってください。
|
commit()
|
現在のトランザクションをコミットします。このメソッドはJDBCトランザクションで使用するため、JTAなどのトランザクションコンテナの管理下では適切に動作しない場合があります。事前に動作確認を行ってください。
|
rollback()
|
現在のトランザクションをロールバックします。このメソッドはJDBCトランザクションで使用するため、JTAなどのトランザクションコンテナの管理下では適切に動作しない場合があります。事前に動作確認を行ってください。
|
executeQuery(String, Object)
|
問い合わせ処理(SELECT文)を実行し結果をMapのListで返します。問い合わせの際にPreparedStatementとResultSetをopen/closeします。PreparedStatementの再利用はできません。また、Connectionのclose()は行いません。
|
executeQueryForArray(String, Object)
|
問い合わせ処理(SELECT文)を実行し結果をObject[][]で返します。問い合わせの際にPreparedStatementとResultSetをopen/closeします。PreparedStatementの再利用はできません。また、Connectionのclose()は行いません。
|
executeQueryForBeans(String, Object)
|
問い合わせ処理(SELECT文)を実行し結果をJavaBeansのListで返します。問い合わせの際にPreparedStatementとResultSetをopen/closeします。PreparedStatementの再利用はできません。また、Connectionのclose()は行いません。
|
executeQueryForDynaBean(String, Object)
|
問い合わせ処理(SELECT文)を実行し結果をLazyDynaBeanのListで返します。問い合わせの際にPreparedStatementとResultSetをopen/closeします。PreparedStatementの再利用はできません。また、Connectionのclose()は行いません。
|
executeUpdate(String, Object)
|
更新処理処理(INSER、UPDATE、DELETE文)を実行し結果を更新件数を返します。更新の際にPreparedStatementとResultSetをopen/closeします。PreparedStatementの再利用はできません。また、Connectionのclose()は行いません。
|
GeneralizedXDaoでは、PreparedStatement、ResulutSetのような低レベルなJDBC APIを使用することなくトランザクション処理を実行することが可能です。
XDaoFactoryによって生成するDAOは、dao-list要素内に定義します。
<!-- DAO List -->
<dao-list>
<dao id="JdbcDAO">
<class>robbie.dao.x.XDao</class>
<resource-ref>TestJDBC</resource-ref>
</dao>
<dao id="JdbcGeneralizedXDao">
<class>robbie.dao.x.GeneralizedXDao</class>
<resource-ref>TestJDBC</resource-ref>
</dao>
<dao id="JdbcUserDao">
<class>example.UserDao</class>
<resource-ref>TestJDBC</resource-ref>
</dao>
</dao-list>
上記の定義で、resource-ref要素で指定するDAOが利用するデータベースリソースは他のDAOと重複しても問題ありません。
また、GeneralizedXDaoやXDaoを独自に拡張したDAOをXDaoFactoryから取得するには、以下のようにオブジェクトのキャストをする必要があります。
public static final void main(String[] args) {
XDaoFactory factory = new XDaoFactory("etc/test-config.xml");
UserDao dao = (UserDao)factory.createDaoInstance("JdbcUserDao");
.............................(略)
// データベースアクセスが全て完了したらDAOをリリース
dao.release();
}
SDaoはSimpleDaoの略で、XDaoFactoryやXDaoのように外部XMLに制御情報を持つのではなく、クラス自身に接続情報やSQLをハードコードするシンプルなDAOです。
このドキュメントでは利用のサンプルコードのみ載せておきます。
package example;
import java.util.List;
import java.util.Properties;
import java.sql.SQLException;
import robbie.dao.DbConnectionUtil;
import robbie.dao.resource.JdbcResource;
import robbie.dao.s.SDao;
import robbie.dao.s.SQuery;
public class Test16 {
public static final void main(String[] args) throws SQLException {
// 接続プロパティの作成
Properties props = DbConnectionUtil.createConnectionProperties(
"org.hsql.jdbcDriver",
"jdbc:HypersonicSQL:hsql://localhost",
"sa",
""
);
// SDaoの作成
SDao dao = new SDao();
dao.setDbResource(new JdbcResource(props));
// SQueryの作成とバインド変数のセット
SQuery query =
new SQuery("SELECT * FROM TEST_TABLE WHERE NAME LIKE ?");
query.addBindParameter(1, "A"+ "%");
query.configure(); ← 変数を全てセットしてからconfigure()を呼ぶ
// SDaoの実行
try {
List result = dao.query(query);
System.out.println(result);
} finally {
dao.release();
}
}
}
上記の実装を見ると、バインド変数のセット、データベース接続プロパティの管理などXDAOで自動管理されている多くの情報の制御がJavaコード内に必要になります。また、動的なSQLも作成できません。
特別な理由がない限りXDaoおよびGeneralizedXDaoを使用することを推奨します。