CodeZine(コードジン)

特集ページ一覧

iBATISを使ったO/RマッピングによるDBアクセスの実例 2

検索結果のマッピングと動的SQLの記述方法

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2007/07/05 14:00

ダウンロード SampleiBATIS2.zip (10.4 KB)

目次

WHERE句が可変な動的SQL

 動的なSQLの組み立てができる点は、iBATISの大きな利点です。外部ファイルにSQLを定義する仕組みでは、動的SQLに対応できないと困ったことになります。

 例えば次のような、検索処理を考えて見ます。

  • 社員名が指定されたときは、WHERE ENAME LIKE ~の検索条件を追加
  • 部署番号が指定されたときは、WHERE DEPTNO = ~の検索条件を追加
  • 両方の条件が指定されたときは、AND条件とする
  • 何も指定されないときは、WHERE句は使用しない

 検索画面などでありがちな処理です。検索条件が指定されることでWHERE句が変化します。

 SQL文字列の単純な連結となりますが、"WHERE"という文字列で連結したり、"AND"という文字列で連結したりと、なかなか厄介なプログラミングになります。

検索条件クラスSearchEmpParam

 検索条件を指定するためのオブジェクトです。社員名と部署番号を検索条件に指定するために作成しました。

 このオブジェクトに検索条件を格納し、iBATISに渡してSQLを実行することになります。

SearchEmpParam.javaの抜粋
public class SearchEmpParam {
    private String ename;
    private int deptno;

    public String getEname() {
        return ename;
    }
    public void setEname(String ename) {
        this.ename = ename;
    }
    public int getDeptno() {
        return deptno;
    }
    public void setDeptno(int deptno) {
        this.deptno = deptno;
    }
}

SqlMap

 作成済みの「SqlMap-Emp.xml」にSQL定義を追加します。

SqlMap-Emp.xmlの抜粋
<!-- 動的SQLの例
    条件指定されときのみWHERE句が付加される -->
<select id="searchEmp" parameterClass="examples.dto.SearchEmpParam"
        resultMap="rm1">
    SELECT * FROM EMP
    <dynamic prepend="WHERE">
        <isNotNull prepend="AND" property="ename">
        ENAME like #ename#         <!-- ename条件がnullでないとき -->
        </isNotNull>
        <isNotEqual compareValue="0" prepend="AND" property="deptno">
        DEPTNO = #deptno#          <!-- deptno条件が0でないとき -->
        </isNotEqual>
    </dynamic>
</select>

 ちょっと複雑になってきましたが、よく見るとシンプルです。

 このSQL定義は、固定部分と動的に決定する部分に分かれています。<dynamic>で定義される部分は動的に決定する部分です。parameterClassで指定されたオブジェクトによって、動的にSQLが構築されます。

 enameプロパティがNotNullの場合(つまり検索条件enameが指定されたとき)に、ENAME like #ename#というSQL文字列が連結されます。

 deptnoプロパティが0以外の場合(つまり検索条件deptnoが指定されとき)に、DEPTNO = #deptno#というSQL文字列が連結されます。

 iBATISが1つ目か2つ目以降かを判断して、"WHERE"か"AND"の文字列を使い、連結してくれます。

実行プログラム

 3通りのSQLが作成される実行例です。動的なSQLを実行するわけですが、プログラムの記述は非常にシンプルです。

Sample09_DynaSQL1.javaの抜粋
System.out.println("動的SQLの指定 条件の有無");
System.out.println("条件指定なし");
SearchEmpParam paramAll = new SearchEmpParam();
List<Emp> listAll =
    (List<Emp>)sqlMap.queryForList("searchEmp", paramAll);
for (Emp e : listAll){
    System.out.println(e);
}

System.out.println("ENAME条件のみ指定 like '%WA%'");
SearchEmpParam paramEname = new SearchEmpParam();
paramEname.setEname("%WA%");    //ENAME条件を指定
List<Emp> listEname =
    (List<Emp>)sqlMap.queryForList("searchEmp", paramEname);
for (Emp e : listEname){
    System.out.println(e);
}

System.out.println("両方指定 dept=20, like '%WA%'");
SearchEmpParam paramBoth = new SearchEmpParam();
paramBoth.setDeptno(20);        //Deptno条件を指定
paramBoth.setEname("%WA%");     //Ename条件を指定
List<Emp> listBoth =
    (List<Emp>)sqlMap.queryForList("searchEmp", paramBoth);
for (Emp e : listBoth){
    System.out.println(e);
}

 SearchEmpParamに格納されている値によって、WHERE句が付加されたりされなかったりします。3回のqueryForListが実行されていますが、動的に作成されるSQLはそれぞれ次のようになります。

SELECT * FROM EMP
SELECT * FROM EMP WHERE ENAME like '%WA'
SELECT * FROM EMP WHERE ENAME like '%WA' AND DEPTNO = 20

繰り返し構造をもつ動的SQL

 指定された値によって、SQLの組み立てが繰り返し構造になることがよくあります。

 サンプルは、WHERE句のINで指定する条件が動的に変化する例です。INで指定するパラメータが0個からn個に変化します。任意の数の社員番号を指定可能で、指定した社員番号がINで列挙指定されます。

検索条件クラスSearchEmpParam

 検索条件クラスは、複数の社員番号が指定できるようにListで社員番号を複数保持できるようにします。

SearchEmpParam.javaの抜粋
public class SearchEmpParam {
    private List<Integer> empnoList = new ArrayList<Integer>();

    public List<Integer> getEmpnoList() {
        return empnoList;
    }
}

SqlMap

 作成済みの「SqlMap-Emp.xml」にSQL定義を追加します。

SqlMap-Emp.xmlの抜粋
<!-- 動的SQLの例 INで指定する値が可変 -->
<select id="searchEmpIn" parameterClass="examples.dto.SearchEmpParam"
        resultMap="rm1">
    SELECT * FROM EMP
    <iterate property="empnoList" prepend="WHERE EMPNO IN"
             open="(" close=")" conjunction="," >
        #empnoList[]#
    </iterate>
</select>

 複数の社員番号をListから列挙するために、iterateタグを指定します。この部分はempnoListプロパティの要素数によって繰り返し出力されます。

 prepend属性は、要素が1個以上存在する際に付加されます。

 open属性、およびclose属性は、繰り返しブロックの先頭と末尾に付加されます(開き括弧、閉じ括弧に使います)。

 conjuction属性は、各繰り返しの連結文字列として使用されます。SQLのIN演算子は","(カンマ)で区切って記述するので上記のようになります。

 プロパティの指定では、empnoList[]のように、プロパティ名の最後に[ ]が付くことに注意してください。

実行プログラム

 検索条件クラスSearchEmpParamempnoListプロパティに、複数の社員番号を追加指定します。

Sample10_DynaSQL2.javaの抜粋
System.out.println("動的SQLの指定");

SearchEmpParam paramIn = new SearchEmpParam();
paramIn.getEmpnoList().add(new Integer(7839));
paramIn.getEmpnoList().add(new Integer(7566));
paramIn.getEmpnoList().add(new Integer(7788));
paramIn.getEmpnoList().add(new Integer(7934));

List<Emp> list =
    (List<Emp>)sqlMap.queryForList("searchEmpIn", paramIn);
for (Emp e : list){
    System.out.println(e);
}

 SQLのIN演算子の部分に展開されて実行されます。

SQL文字列の埋め込みをする動的SQL

 最も柔軟な動的SQLは、SQL文の一部を文字列で指定する方法です。

 ORDER BY句のソート条件を指定する場合などは文を組み立てるよりも、文字列でORDER BY句そのものを直接指定できた方が楽だったりします。

 iBATISでは、SQL文に文字を埋め込むことが可能です。これは埋め込みパラメータとは違います。あくまでSQL文として埋め込まれるものです。

SqlMap

 作成済みの「SqlMap-Emp.xml」にSQL定義を追加します。

SqlMap-Emp.xmlの抜粋
<!-- 動的SQLの例 文字列でSQL文そのものを指定 -->
<select id="searchEmpOrder" resultMap="rm1">
    SELECT * FROM EMP
    ORDER BY $value$
</select>

 $value$が、後から埋め込まれるSQL文字列です。#value#のような埋め込みパラメータの記述と違う点に注意してください。

 この例では唯一の文字列をパラメータとするため、$value$になりますが、parameterClassを指定する場合は、$fooProperty$$barProperty$のように、複数の埋め込み文字列を指定することもできます(もちろんMapでの指定も可能です)。

実行プログラム

Sample11_DynaSQL3.javaの抜粋
System.out.println("動的SQLの指定 SQL文を指定 ORDER BY EMPNO DESC");
List<Emp> list =
    (List<Emp>)sqlMap.queryForList("searchEmpOrder", "EMPNO DESC");
for (Emp e : list){
    System.out.println(e);
}

 queryForListを実行する際に、SQLに埋め込む文字列を指定します。この例では次のように展開されます。

SqlMapの定義
SELECT * FROM EMP
ORDER BY $value$

 ↓

展開後
SELECT * FROM EMP
ORDER BY EMPNO DESC

 この埋め込み方法は、複雑な動的SQLを作成するために使用すべきです。

 データの埋め込みには、#property#形式で、値の埋め込みパラメータを使用するのが適切です。

まとめ

  • resultMap要素を使用することで検索結果とBeanのマッピングができる
  • dynamic要素を使用することで動的SQLが定義できる
  • iterate要素を使用することで繰り返し構造の動的SQLが定義できる
  • 実行時にSQL文字列を埋め込むことができる

 動的SQLが定義できることで、ソースコードからSQL文が完全に分離できます。ソースコードはSQL定義を参照するだけになり非常にシンプルになります。高機能なO/Rマッピングフレームワークは、SQLを隠蔽してしまうものもありますが、iBATISは、あえてSQLに特化した機能を強化しています。

 強力なSQLの機能を最大限に利用できるフレームワークとして、iBATISは最良の選択肢かと思います。

参考資料



  • LINEで送る
  • このエントリーをはてなブックマークに追加

バックナンバー

連載:iBATISを使ったO/RマッピングによるDBアクセスの実例

著者プロフィール

あなたにオススメ

All contents copyright © 2005-2021 Shoeisha Co., Ltd. All rights reserved. ver.1.5