S2Daoのサンプルアプリ作成(トランザクション制御モード)
サンプルアプリの概要(トランザクションありの場合)
「トランザクション制御を用いて、SAMPLE1テーブルへの挿入、更新、削除処理を行う簡単なサンプルアプリです。」
SAMPLE1テーブル
ID(PK) | NUMBER(2) |
NAME | VARCHAR2(10) |
MASTER1テーブル
MASTER_ID(PK) | NUMBER(2) |
SALARY | NUMBER(6) |
UTIWAKE | VARCHAR2(16) |
[開発環境]
- JDK1.5.0_13
- eclipse3.2
- Seasar2 2.4.20
- S2.4.20.zipを解凍し、各jarファイルにクラスパスを通す(詳細設定は他サイトを参考にして下さい)
- S2Dao 1.0.47
- s2-dao-1.0.47.zipを解凍し、各jarファイルにクラスパスを通す
[用意するもの]
- データベースに上記テーブルSAMPLE1、MASTER1をCREATE
- Seasar2コンテナからDaoを取り出しDBの参照を行うメインクラスDBQuery.java
- テーブルとの関連付けに使用するJavaBeans(SAMPLE1テーブル用)…MemberInfo.java
- テーブルとの関連付けに使用するJavaBeans(MASTER1テーブル用)…MasterInfo.java
- Daoインタフェース(SAMPLE1テーブル用)…MemberInfoDao.javaインタフェース
- Daoラッピングクラスのインターフェース…TransactionInterface.java
- Daoをラッピングしたクラス(diconでこのクラスにDaoを注入します)…TransactionWrapper.java
以下のdiconファイルはクラスパスの通っている場所に配備すること
- j2ee.dicon(データベースとの接続設定を記述)
- 自作.dicon(Daoの注入に使用)
[処理の流れその1]
入力値チェックを行う場合
1.メインクラスのDBQueryThreadでSeasar2コンテナからSAMPLE1テーブル更新用の
Daoラッパーコンポーネント(TransactionWrapper)を取得する。
↓
2.トランザクション制御のもと、Daoのインタフェース経由でSAMPLE1テーブルのデータを更新する。
もし途中でSQLExceptionが発生した場合はすべての処理がロールバックされる。
DBQuery.java
package jp.co.companyname.app; import java.sql.SQLException; import java.util.List; import jp.co.companyname.dao.MemberInfo; import jp.co.companyname.dao.MemberInfoDao; import org.seasar.framework.container.S2Container; import org.seasar.framework.container.factory.S2ContainerFactory; import org.seasar.framework.exception.SQLRuntimeException; /** * S2コンテナとS2Daoを使用して、データベースの更新を行います。 * 2008/1/16 * @author shimabukurot * */ public class DBQuery { public static void main(String[] args) { // ■複数SQLのトランザクション制御 System.out.println(Thread.currentThread() + " --------- S2Dao複数SQLトランザクション制御テスト開始"); S2Container container = S2ContainerFactory.create("自作.dicon"); TransactionInterface daoWrapper = (TransactionInterface) container.getComponent("TransactionWrapperDesu"); try { daoWrapper.doTransactionInsert(); } catch (SQLRuntimeException e) { // ロールバックはSeasar2フレームワークが自動で行ってくれます。(設定は自作.diconファイルで行います) System.out.println("daoWrapper呼び出し側でtry-catch"); e.printStackTrace(); } System.out.println(Thread.currentThread() + " --------- S2Dao複数SQLトランザクション制御テスト終了"); } }
MemberInfo.java
package jp.co.companyname.dao; public class MemberInfo { /* * [構文] * スキーマ名.テーブル名 * テーブル名だけでも可 */ public static final String TABLE = "スキーマ名.SAMPLE1"; // 定数アノテーション //public static final String id_COLUMN = "ID"; //public static final String name_COLUMN = "NAME"; /** * * RELNOをString型にするとjava.lang.IllegalArgumentExceptionで落ちます。 * RELNO定数は、N:1マッピングの連番になるので以下のように0から付けていく必要があります。 */ public static final int masterInfo_RELNO = 0; /** * [構文] * * エンティティ名(JavaBeansのクラス名:先頭は小文字)_RELKEYS = "N側のテーブルのカラム名: 1側のテーブルのカラム名"; * ※エンティティ名を間違うとテーブルの結合ができません。 */ public static final String masterInfo_RELKEYS = "ID:MASTER_ID"; private int id; private String name; private MasterInfo abc;// 結合対象のテーブルを表すエンティティ(変数名は任意でよい) // コンストラクタ public MemberInfo(){} public MemberInfo(int id, String name) { this.id = id; this.name = name; } public MemberInfo(int id, String name, int salary, MasterInfo masterInfo) { this.id = id; this.name = name; this.abc = masterInfo; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public MasterInfo getMasterInfo() { return abc; } public void setMasterInfo(MasterInfo masterInfo) { this.abc = masterInfo; } }
MasterInfo.java
package jp.co.companyname.dao; public class MasterInfo { /* * [構文] * スキーマ名.テーブル名 * テーブル名だけでも可。 */ public static final String TABLE = "スキーマ名.MASTER1"; private int masterId; private int salary; private String utiwake; // 空コンストラクタ(DIコンテナがインスタンス生成を行うために必須) public MasterInfo(){}; public MasterInfo(int id, int salary) { this.masterId = id; this.salary = salary; } public int getSalary() { return salary; } public void setSalary(int salary) { this.salary = salary; } public int getMasterId() { return masterId; } public void setMasterId(int masterId) { this.masterId = masterId; } public String getUtiwake() { return utiwake; } public void setUtiwake(String utiwake) { this.utiwake = utiwake; } }
MemberInfoDao.javaインタフェース
package jp.co.companyname.dao; import java.util.List; /* * MemberInfoから情報を取得するDao。 * 実装はDIコンテナが行います。 * よって自分で実装クラスの * 宣言をする必要はありません。 */ public interface MemberInfoDao { public static final Class BEAN = MemberInfo.class; public int insert(MemberInfo mInfo); public int update(MemberInfo mInfo); public int delete(MemberInfo mInfo); public void doSomething(); public String getText(); public List findAll(); }
TransactionInterface.javaインターフェース
package jp.co.companyname.app; public interface TransactionInterface { public void doTransactionInsert(); public void doTransactionUpdate(); }
TransactionWrapper.java
package jp.co.companyname.app; import org.seasar.framework.exception.SQLRuntimeException; import jp.co.companyname.dao.MemberInfo; import jp.co.companyname.dao.MemberInfoDao; public class TransactionWrapper implements TransactionInterface { private MemberInfoDao dao = null; public TransactionWrapper(){}; // Daoはdiconを用いたコンストラクタインジェクション(Dependency Injection)で注入させる(設定は自作.diconファイルに記述) public TransactionWrapper(MemberInfoDao d) { this.dao = d; } public void doTransactionInsert() { System.out.println("doTransactionInsert開始"); /* * [注意点] * ここでtry-catch節を記述してしまうと、ロールバックされずに、SQLException発生前の正常なSQLの実行分がコミットされてしまうので注意! * 下のコードで言うと、五番、六番がコミットされ、七番xxxxはSQLExceptionのため無効になります。 * トランザクション制御を正しく動作させるにはdoTransactionInsertメソッドの呼び出し元で * try-catch句を記述する。 * * S2コンテナが呼び出し元クラスからDaoをインタフェースとして、トランザクション管理を行っているのが * 原因だと思われます。 */ dao.insert(new MemberInfo(5, "五番")); dao.insert(new MemberInfo(6, "六番")); dao.insert(new MemberInfo(7, "七番xxxxxxxxxxxxxxxxxx"));//ここで意図的にSQLExceptionを発生させ、ロールバックを起こさせる。 dao.insert(new MemberInfo(8, "八番")); System.out.println("doTransactionInsert終了"); } public void doTransactionUpdate() { this.dao.update(new MemberInfo(4, "四番update")); } }
j2ee.dicon(抜粋)
<!-- for Oracle --> <component name="xaDataSource" class="org.seasar.extension.dbcp.impl.XADataSourceImpl"> <property name="driverClassName"> "oracle.jdbc.driver.OracleDriver" </property> <property name="URL"> "jdbc:oracle:thin:@ホスト名:ポート番号:サービス名" </property> <property name="user">"TEST"</property> <property name="password">"TEST"</property> <initMethod name="addProperty"> <arg>"includeSynonyms"</arg> <arg>"true"</arg> </initMethod> </component>
自作.dicon
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.3//EN" "http://www.seasar.org/dtd/components23.dtd"> <components> <include path="dao.dicon" /> <component name="MemberInfoDaoDesu" class="jp.co.companyname.dao.MemberInfoDao"> <aspect>dao.interceptor</aspect> </component> <component name="TransactionWrapperDesu" class="jp.co.infonic.app.TransactionWrapper"> <aspect>j2ee.requiredTx</aspect> ★上記コンポーネントMemberInfoDaoDesuをコンストラクタインジェクション aspectタグではなくargタグである点に注意! <arg>MemberInfoDaoDesu</arg> </component> </components>