iBATISのORMとSpringのAOPを活用したDBアクセスオープンソースTERASOLUNAで作るWebアプリ(3)(2/3 ページ)

» 2008年12月24日 00時00分 公開
[正野勇嗣株式会社NTTデータ]

コネクションを意識しないDBアクセス

 従来のDB操作の場合、業務ロジック内にコネクションの取得/解放処理やSQL文を記述する必要がありました。そのため、下記のデメリットがありました。

・コネクションの解放漏れによるWebアプリケーションサーバのコネクションの枯渇の恐れがある

・SQL文の変更が容易ではない

 TERASOLUNAのDAOを用いたデータベース操作では、ロジック上にコネクションに関する記述はなく、SQLはXMLファイルに記述します。これらにより下記のメリットがあります。

・コーディングによるバグ混入を防止できる

・SQL文の変更が容易

try {
    // データソースからConnectionを取得
    con = ds.getConnection();
 
    // Statementを取得
    stmt = con.createStatement();
 
    // 検索するSQL文を作成
    String sql = "SELECT TEST_COL1, TEST_COL2 FROM TEST_TABLE";
 
    // クエリーを実行して結果セットを取得
    ResultSet rs = stmt.executeQuery(sql);
 
    while(rs.next()){
        String col1 = rs.getString("TEST_COL1");
        String col2 = rs.getString("TEST_COL2");
        // 略
    }
} catch (Exception e) {
    // 略
} finally {
    try {
        if (stmt!=null) {stmt.close();}
 
     // コネクションの開放
        if (con!=null) {con.close();}
 
    } catch (Exception e) {
        // 略
    }
}
リスト3 従来のDB操作の例

 TERASOLUNAのDAOには用途ごとに特化した機能を持つインターフェイスが提供されています。また、iBATISを利用したデフォルト実装が提供されています。

DAO名 インターフェイス名 実装クラス名 実行できるSQL
参照系DAO QueryDAO QueryDAOiBatisImpl SELECT文
更新系DAO UpdateDAO UpdateDAOiBatisImpl INSERT文
UPDATE文
DELETE文
ストアドプロシージャDAO StoredProcedureDAO StoredProcedureDAOiBatisImpl ストアドプロシージャ
表4 DAOインターフェイス

業務ロジックとDAOの間のデータ受け渡し

 TERASOLUNAのDAOを利用する場合はSQL設定ファイルに対応するDAO入出力クラスを定義する必要があります。

図3 DAO入出力クラスとSQL設定ファイル 図3 DAO入出力クラスとSQL設定ファイル

DBへデータを追加するサンプルWebアプリの実装

 連載第2回の際に作成したアプリケーションを修正する形で実装します(下記リストはインデックスになっています)。

手順【1】SQL設定ファイル(sqlMap.xml)の設定追加

 sqlMap.xmlファイルの定義は「webapps\WEB-INF\sqlMapConfig.xml」にあります。

 USER_LISTテーブルに対し、画面から入力されたidとnameを登録するSQLを<insert>要素の値に定義しています。このとき、parameter属性にはSQLのVALUESに指定するフィールド(#で囲む)を持つクラスを指定します。また、USER_LISTテーブルからidとnameを取得するSQLを<select>要素の値に定義しています。このとき、parameter属性に条件文に利用するフィールドを持つクラスを、resultClass属性には発行されたSQLの結果を格納するフィールドを持つクラスを指定します。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
  "http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap namespace="user">
  <insert id="addUser" parameterClass="sample.AddUserDaoInput">
    INSERT INTO user_list (id, name) VALUES (#addUserId#,#addUserName#)
  </insert>
  <select id="selectUser" parameterClass="sample.AddUserDaoInput"resultClass="sample.SelectUserDaoOutput">
    SELECT id as "userId",name as "userName" FROM user_list WHERE id = #addUserId#
  </select>
</sqlMap>
リスト4 SQL設定ファイル(sqlMap.xml)

手順【2】業務ロジッククラスの定義

 上記で定義されたSQLを呼び出すDAOを業務ロジック内に定義します。

 DAOの定義として、QueryDAOとUpdateDAOのフィールドを定義します。このときspringによるセッターインジェクションのために各フィールドのセッターを定義します。画面から入力されたfirstTermとsecondTermを業務入力クラス(ConcatInput)から取り出し、DAO入力クラス(AddUserDaoOutput)へ格納します。このDAO入力クラス(AddUserDaoInput)を引数として、DBへの登録処理(updateDAO)を実行します。このとき、sql設定ファイル(sqlMap.xml)における<insert>要素のid属性を指定します。

 次に、上記のDAO入力クラス(AddUserDaoInput)を引数として、DBからの参照処理(queryDAO)を実行し、返り値を、DAO出力クラス(SelectUserDaoOutput)に格納します。業務出力オブジェクトの生成以降の流れは連載第2回の記事と同様となりますので割愛します。

package sample;
import jp.terasoluna.fw.dao.QueryDAO;
import jp.terasoluna.fw.dao.UpdateDAO;
import jp.terasoluna.fw.service.thin.BLogic;
import jp.terasoluna.fw.service.thin.BLogicResult;
 
public class ConcatBLogic implements BLogic<ConcatInput>{
 
    //DAOの定義
    protected QueryDAO queryDAO;
    protected UpdateDAO updateDAO;
 
    public void setQueryDAO(QueryDAO queryDAO) {
        this.queryDAO = queryDAO;
    }
    public void setUpdateDAO(UpdateDAO updateDAO) {
        this.updateDAO = updateDAO;
    }
    public BLogicResult execute(ConcatInput input) {
 
        //業務処理(DB登録)の実行
        AddUserDaoInput addUserDaoInput = new AddUserDaoInput();
        addUserDaoInput.setAddUserId(input.getFirstTerm());
        addUserDaoInput.setAddUserName(input.getSecondTerm());
        this.updateDAO.execute("addUser", addUserDaoInput);
 
        //業務処理(DB参照)の実行
        SelectUserDaoOutput selectUser = this.queryDAO.
            executeForObject("selectUser", addUserDaoInput,
            SelectUserDaoOutput.class);
        String resultTerm = selectUser.getUserId() + " " +
            selectUser.getUserName();
 
        //業務出力オブジェクトを生成
        ConcatOutput output = new ConcatOutput();
        output.setResultTerm(resultTerm);
 
        //結果オブジェクトの生成
        BLogicResult result = new BLogicResult();
        result.setResultString("success");
        result.setResultObject(output);
 
        return result;
    }
}
リスト5 業務ロジッククラス(ConcatBLogic.java)

手順【3】SpringのBean定義ファイル(moduleContext.xml)の設定追加

 Bean定義ファイルを以下のように修正し、業務ロジックとDAOを関連付けます。

<!-- 下記が変更部分で、それ以外の部分は省略 -->
  <bean class="sample.ConcatBLogic" id="concatBLogic" 
    scope="singleton">
    <property name="queryDAO" ref="queryDAO" />
    <property name="updateDAO" ref="updateDAO" />
  </bean>
 
リスト6 SprinのBean定義ファイル(moduleContext.xml)

手順【4】DAO入出力クラスの定義

 DAO入出力のDTOクラスを定義します。

package sample;
 
public class AddUserDaoInput {
 
    private String addUserId;
    private String addUserName;
 
//以下省略、ゲッター、セッターの記述
}
リスト7 DAO入力クラス(AddUserDaoInput.java)
package sample;
 
public class SelectUserDaoOutput {
 
    private String userId;
    private String userName;
 
//以下省略、ゲッター、セッターの記述
}
リスト8 DAO出力クラス(SelectUserDaoOutput.java)

Spring AOPを用いたトランザクション管理

 TERASOLUNAフレームワークではトランザクション管理(コミットやロールバック)にSpringのAOPを用いて行うことができます。そのため、開発者がトランザクションコードを実装する必要がありません。今回の例では、業務処理(BLogic)単位でトランザクションを管理します。

図4 トランザクション管理 図4 トランザクション管理

手順【5】SpringのBean定義ファイル(applicationContext.xml、commonContext.xml)の設定追加

 AOPの設定に関してですが、commonContext.xmlにAOPの設定を追記します。<aop:pointcut>要素のexpression属性に「bean(*BLogic)」と指定します。これによりBean定義IDの接尾辞がBLogicのBeanに対してトランザクションが適用されます。また、要素のadvice-ref属性に、対応するトランザクションインターセプタを設定します。

 次に、トランザクションインターセプタの設定ですが、要素のpropagation属性にメソッド名を、トランザクション伝播にはREQUIREDを指定します。トランザクションマネージャのBean定義内に、対応するデータソースを設定します。

  <!-- AOPの設定 -->
  <aop:config>
    <!-- 業務ロジック(*BLogic)単位でのトランザクション設定 -->
    <aop:pointcut id="blogicBeans"  expression="bean(*BLogic)"/>
    <aop:advisor 
      pointcut-ref="blogicBeans" 
      advice-ref="transactionInterceptor"/>
  </aop:config>
リスト9 commonContext.xml
<!-- 略 -->
  <!-- データソース設定 -->
  <bean id="TerasolunaSampleDataSource"
    class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName">
      <value>java:comp/env/jdbc/TerasolunaSampleDataSource</value>
    </property>
  </bean>
 
  <!-- トランザクションマネージャの設定 -->
  <bean id="transactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource">
      <ref bean="TerasolunaSampleDataSource"/>
    </property>
  </bean>
 
  <!-- トランザクションインタセプタの設定 -->
  <tx:advice id="transactionInterceptor" >
    <tx:attributes>
      <!-- 対象メソッド名を「*」とする -->
      <tx:method name="*" propagation="REQUIRED"
        rollback-for="java.lang.Exception"/>
    </tx:attributes>
  </tx:advice>
<!-- 略 -->
リスト10 applicationContext.xml

 それでは、次ページで動作確認をしてみましょう。

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。