Seasar DI Container with AOP

SQLは自由に発行したい。でもJDBCを使うのはめんどくさい。そんなあなたのために用意されているのが、S2JDBCです。

セットアップ

簡単に機能を試すことができるように、RDBMSとしてHSQLDBを用意しています。機能を試す前にあらかじめHSQLDBを実行しておいてください。HSQLDBを実行するには、bin/runHsqldb.batをダブルクリック(Windowsの場合)します。lib/hsqldb.jarはHSQLDBを実行する上では必要ですが、本番では必要ありません。

検索処理(BeanList)

さっそく複数件のJavaBeansを検索してみましょう。検索するためにはBasicSelectHandlerを使います。次のようなプロパティが用意されています。

org.seasar.extension.jdbc.impl.BasicSelectHandler

プロパティ 説明 S2Containerでの設定例
dataSource DataSource 通常自動的に設定されます。
sql 実行したいSQL文 "SELECT * FROM emp"
resultSetHandler ResultSetを処理するHandlerのクラス

複数件のJavaBeansを返すためにはBeanListResultSetHandlerを使います。

<component
  class="org.seasar.extension.jdbc.impl.BeanListResultSetHandler">
  <arg>@examples.jdbc.Employee@class</arg>
</component>

fetchSize 1回のフェッチで取得する行数 100
maxRows 取得する最大行数。-1の場合は特に設定は行われない。  

examples/jdbc/SelectBeanList.dicon

<components>
<include path="j2ee.dicon"/>
<component name="selectBeanListHandler"
class="org.seasar.extension.jdbc.impl.BasicSelectHandler">
<property name="sql">
"SELECT empno, ename, job, mgr, hiredate, sal, comm, deptno FROM emp"
</property>
<property name="resultSetHandler">
<component class="org.seasar.extension.jdbc.impl.BeanListResultSetHandler">
<arg>@examples.jdbc.Employee@class</arg>
</component>
</property>
</component>
</components>

examples.jdbc.SelectBeanListClient

package examples.jdbc;

import java.util.List;

import org.seasar.extension.jdbc.SelectHandler;
import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;

public class SelectBeanListClient {

    private static final String PATH =
        "examples/jdbc/SelectBeanList.dicon";

    public static void main(String[] args) {
        S2Container container = S2ContainerFactory.create(PATH);
        container.init();
        try {
            SelectHandler handler = (SelectHandler)
            container.getComponent("selectBeanListHandler");
            List result = (List) handler.execute(null);
            for (int i = 0; i < result.size(); ++i) {
                System.out.println(result.get(i));
            }
        } finally {
            container.destroy();
        }

    }
}

実行結果

DEBUG 2004-04-23 11:40:45,086 [main] SELECT empno, ename, job, mgr, hiredate, sal, comm, deptno
  FROM emp
DEBUG 2004-04-23 11:40:46,408 [main] 物理的なコネクションを取得しました
DEBUG 2004-04-23 11:40:46,408 [main] 論理的なコネクションを取得しました
DEBUG 2004-04-23 11:40:46,749 [main] 論理的なコネクションを閉じました
7369, SMITH, CLERK, 7902, 1980-12-17 00:00:00.0, 800.0, 0.0, 20
7499, ALLEN, SALESMAN, 7698, 1981-02-20 00:00:00.0, 1600.0, 300.0, 30
7521, WARD, SALESMAN, 7698, 1981-02-22 00:00:00.0, 1250.0, 500.0, 30
7566, JONES, MANAGER, 7839, 1981-04-02 00:00:00.0, 2975.0, 0.0, 20
7654, MARTIN, SALESMAN, 7698, 1981-09-28 00:00:00.0, 1250.0, 1400.0, 30
7698, BLAKE, MANAGER, 7839, 1981-05-01 00:00:00.0, 2850.0, 0.0, 30
7782, CLARK, MANAGER, 7839, 1981-06-09 00:00:00.0, 2450.0, 333.0, 10
7788, SCOTT, ANALYST, 7566, 1982-12-09 00:00:00.0, 3000.0, 0.0, 20
7839, KING, PRESIDENT, 0, 1981-11-17 00:00:00.0, 5000.0, 444.0, 10
7844, TURNER, SALESMAN, 7698, 1981-09-08 00:00:00.0, 1500.0, 0.0, 30
7876, ADAMS, CLERK, 7788, 1983-01-12 00:00:00.0, 1100.0, 0.0, 20
7900, JAMES, CLERK, 7698, 1981-12-03 00:00:00.0, 950.0, 0.0, 30
7902, FORD, ANALYST, 7566, 1981-12-03 00:00:00.0, 3000.0, 0.0, 20
7934, MILLER, CLERK, 7782, 1982-01-23 00:00:00.0, 1300.0, 555.0, 10
DEBUG 2004-04-23 11:40:46,799 [main] 物理的なコネクションを閉じました

バインド変数がある場合には、execute(Object[])の引数に指定します。今回の例はバインド変数がないので、nullを設定しています。S2Containerにコンポーネントを組み立ててもらい、後はexecute()を呼び出すだけなので楽チンです。カラム名にアンダースコア(_)が含まれている場合は、アンダスコアを除いてプロパティにマッピングされます。

検索処理(Bean)

1件のJavaBeansを検索するためには上記と同様にBasicSelectHandlerを使います。BeanResultSetHandlerを使うこと以外は上記と同様です。結果はリストではなくJavaBeansとしてかえってきます。

examples/jdbc/SelectBean.dicon

<components>
<include path="j2ee.dicon"/>
<component name="selectBeanHandler"
class="org.seasar.extension.jdbc.impl.BasicSelectHandler">
<property name="sql">
"SELECT empno, ename, job, mgr, hiredate, sal, comm, deptno
FROM emp WHERE empno = ?"
</property>
<property name="resultSetHandler">
<component class="org.seasar.extension.jdbc.impl.BeanResultSetHandler">
<arg>@examples.jdbc.Employee@class</arg>
</component>
</property>
</component>
</components>

examples.jdbc.SelectBeanClient

package examples.jdbc;

import org.seasar.extension.jdbc.SelectHandler;
import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;

public class SelectBeanClient {

    private static final String PATH =
        "examples/jdbc/SelectBean.dicon";

    public static void main(String[] args) {
        S2Container container = S2ContainerFactory.create(PATH);
        container.init();
        try {
            SelectHandler handler =
                (SelectHandler) container.getComponent("selectBeanHandler");
            Employee result = (Employee) handler.execute(
                new Object[]{new Integer(7788)});
            System.out.println(result);
        } finally {
            container.destroy();
        }

    }
}

実行結果

DEBUG 2004-04-23 16:59:24,989 [main] SELECT empno, ename, job, mgr, hiredate, sal, comm, deptno
FROM emp WHERE empno = 7788
DEBUG 2004-04-23 16:59:26,452 [main] 物理的なコネクションを取得しました
DEBUG 2004-04-23 16:59:26,452 [main] 論理的なコネクションを取得しました
DEBUG 2004-04-23 16:59:26,792 [main] 論理的なコネクションを閉じました
7788, SCOTT, ANALYST, 7566, 1982-12-09 00:00:00.0, 3000.0, 0.0, 20
DEBUG 2004-04-23 16:59:26,802 [main] 物理的なコネクションを閉じました

検索処理(MapList)

複数件のMapを取得することもできます。ResultSetHandlerにはMapListResultSetHandlerを使います。

examples/jdbc/SelectMapList.dicon

<components>
<include path="j2ee.dicon"/>
<component name="selectMapListHandler"
class="org.seasar.extension.jdbc.impl.BasicSelectHandler">
<property name="sql">
"SELECT empno, ename, job, mgr, hiredate, sal, comm, deptno FROM emp"
</property>
<property name="resultSetHandler">
<component class="org.seasar.extension.jdbc.impl.MapListResultSetHandler"/>
</property>
</component>
</components>

examples.jdbc.SelectMapListClient

package examples.jdbc;

import java.util.List;

import org.seasar.extension.jdbc.SelectHandler;
import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;

public class SelectMapListClient {

    private static final String PATH =
        "examples/jdbc/SelectMapList.dicon";

    public static void main(String[] args) {
        S2Container container = S2ContainerFactory.create(PATH);
        container.init();
        try {
            SelectHandler handler = (SelectHandler)
            container.getComponent("selectMapListHandler");
            List result = (List) handler.execute(null);
            for (int i = 0; i < result.size(); ++i) {
                System.out.println(result.get(i));
            }
        } finally {
            container.destroy();
        }

    }
}

実行結果

DEBUG 2004-04-23 18:27:08,748 [main] SELECT empno, ename, job, mgr, hiredate, sal, comm, deptno
  FROM emp
DEBUG 2004-04-23 18:27:10,150 [main] 物理的なコネクションを取得しました
DEBUG 2004-04-23 18:27:10,150 [main] 論理的なコネクションを取得しました
DEBUG 2004-04-23 18:27:11,032 [main] 論理的なコネクションを閉じました
{empno=7369, ename=SMITH, job=CLERK, mgr=7902, hiredate=1980-12-17 00:00:00.0, sal=800, comm=null, deptno=20}
{empno=7499, ename=ALLEN, job=SALESMAN, mgr=7698, hiredate=1981-02-20 00:00:00.0, sal=1600, comm=300, deptno=30}
{empno=7521, ename=WARD, job=SALESMAN, mgr=7698, hiredate=1981-02-22 00:00:00.0, sal=1250, comm=500, deptno=30}
{empno=7566, ename=JONES, job=MANAGER, mgr=7839, hiredate=1981-04-02 00:00:00.0, sal=2975, comm=null, deptno=20}
{empno=7654, ename=MARTIN, job=SALESMAN, mgr=7698, hiredate=1981-09-28 00:00:00.0, sal=1250, comm=1400, deptno=30}
{empno=7698, ename=BLAKE, job=MANAGER, mgr=7839, hiredate=1981-05-01 00:00:00.0, sal=2850, comm=null, deptno=30}
{empno=7782, ename=CLARK, job=MANAGER, mgr=7839, hiredate=1981-06-09 00:00:00.0, sal=2450, comm=333, deptno=10}
{empno=7788, ename=SCOTT, job=ANALYST, mgr=7566, hiredate=1982-12-09 00:00:00.0, sal=3000, comm=null, deptno=20}
{empno=7839, ename=KING, job=PRESIDENT, mgr=null, hiredate=1981-11-17 00:00:00.0, sal=5000, comm=444, deptno=10}
{empno=7844, ename=TURNER, job=SALESMAN, mgr=7698, hiredate=1981-09-08 00:00:00.0, sal=1500, comm=0, deptno=30}
{empno=7876, ename=ADAMS, job=CLERK, mgr=7788, hiredate=1983-01-12 00:00:00.0, sal=1100, comm=null, deptno=20}
{empno=7900, ename=JAMES, job=CLERK, mgr=7698, hiredate=1981-12-03 00:00:00.0, sal=950, comm=null, deptno=30}
{empno=7902, ename=FORD, job=ANALYST, mgr=7566, hiredate=1981-12-03 00:00:00.0, sal=3000, comm=null, deptno=20}
{empno=7934, ename=MILLER, job=CLERK, mgr=7782, hiredate=1982-01-23 00:00:00.0, sal=1300, comm=555, deptno=10}
DEBUG 2004-04-23 18:27:11,142 [main] 物理的なコネクションを閉じました

検索処理(Map)

1件のMapを取得することもできます。ResultSetHandlerにはMapResultSetHandlerを使います。

examples/jdbc/SelectMap.dicon

<components>
<include path="j2ee.dicon"/>
<component name="selectMapListHandler"
class="org.seasar.extension.jdbc.impl.BasicSelectHandler">
<property name="sql">
"SELECT empno, ename, job, mgr, hiredate, sal, comm, deptno
FROM emp WHERE empno = ?"
</property>
<property name="resultSetHandler">
<component class="org.seasar.extension.jdbc.impl.MapResultSetHandler"/>
</property>
</component>
</components>

examples.jdbc.SelectMapClient

package examples.jdbc;

import java.util.Map;

import org.seasar.extension.jdbc.SelectHandler;
import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;

public class SelectMapClient {

    private static final String PATH =
        "examples/jdbc/SelectMap.dicon";

    public static void main(String[] args) {
        S2Container container = S2ContainerFactory.create(PATH);
        container.init();
        try {
            SelectHandler handler =
                (SelectHandler) container.getComponent("selectMapHandler");
            Map result = (Map) handler.execute(
                new Object[]{new Integer(7788)});
            System.out.println(result);
        } finally {
            container.destroy();
        }

    }
}

実行結果

DEBUG 2004-04-23 18:59:52,652 [main] SELECT empno, ename, job, mgr, hiredate, sal, comm, deptno
FROM emp WHERE empno = 7788
DEBUG 2004-04-23 18:59:53,924 [main] 物理的なコネクションを取得しました
DEBUG 2004-04-23 18:59:53,924 [main] 論理的なコネクションを取得しました
DEBUG 2004-04-23 18:59:54,235 [main] 論理的なコネクションを閉じました
{empno=7788, ename=SCOTT, job=ANALYST, mgr=7566, hiredate=1982-12-09 00:00:00.0, sal=3000, comm=null,deptno=20}
DEBUG 2004-04-23 18:59:54,255 [main] 物理的なコネクションを閉じました

更新処理

更新するためにはBasicUpdateHandlerを使います。次のようなプロパティが用意されています。

org.seasar.extension.jdbc.impl.BasicUpdateHandler

プロパティ 説明 S2Containerでの設定例
dataSource DataSource 通常自動的に設定されます。
sql 実行したいSQL文 "UPDATE emp SET ename = ? WHERE empno = ?"

examples/jdbc/Update.dicon

<components>
<include path="j2ee.dicon"/>
<component name="updateHandler"
class="org.seasar.extension.jdbc.impl.BasicUpdateHandler">
<property name="sql">
"UPDATE emp SET ename = ? WHERE empno = ?"
</property>
</component>
</components>

examples.jdbc.UpdateClient

package examples.jdbc;

import org.seasar.extension.jdbc.UpdateHandler;
import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;

public class UpdateClient {

    private static final String PATH = "examples/jdbc/Update.dicon";

    public static void main(String[] args) {
        S2Container container = S2ContainerFactory.create(PATH);
        container.init();
        try {
            UpdateHandler handler =
                (UpdateHandler) container.getComponent("updateHandler");
            int result =
                handler.execute(new Object[] { "SCOTT", new Integer(7788)});
            System.out.println(result);
        } finally {
            container.destroy();
        }

    }
}

実行結果

DEBUG 2004-04-23 19:18:40,134 [main] UPDATE emp SET ename = 'SCOTT' WHERE empno = 7788
DEBUG 2004-04-23 19:18:41,455 [main] 物理的なコネクションを取得しました
DEBUG 2004-04-23 19:18:41,455 [main] 論理的なコネクションを取得しました
DEBUG 2004-04-23 19:18:41,766 [main] 論理的なコネクションを閉じました
1
DEBUG 2004-04-23 19:18:41,776 [main] 物理的なコネクションを閉じました

バッチ更新処理

複数件のデータを一度にバッチ更新するためにはBasicBatchHandlerを使います。BatchHandler.execute(List argsList)の引数はObject[]をリストにつめたものです。次のようなプロパティが用意されています。

org.seasar.extension.jdbc.impl.BasicBatchHandler

プロパティ 説明 S2Containerでの設定例
dataSource DataSource 通常自動的に設定されます。
sql 実行したいSQL文 "UPDATE emp SET ename = ? WHERE empno = ?"
batchSize 1度に処理するデータ数 設定しなかった場合はバッチの件数が自動的に設定されます。

examples/jdbc/Batch.dicon

<components>
<include path="j2ee.dicon"/>
<component name="batchHandler"
class="org.seasar.extension.jdbc.impl.BasicBatchHandler">
<property name="sql">
"UPDATE emp SET ename = ? WHERE empno = ?"
</property>
</component>
</components>

examples.jdbc.BatchClient

package examples.jdbc;

import java.util.ArrayList;
import java.util.List;

import org.seasar.extension.jdbc.BatchHandler;
import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;

public class BatchClient {

    private static final String PATH = "examples/jdbc/Batch.dicon";

    public static void main(String[] args) {
        S2Container container = S2ContainerFactory.create(PATH);
        container.init();
        try {
            BatchHandler handler =
                (BatchHandler) container.getComponent("batchHandler");
            List argsList = new ArrayList();
            argsList.add(new Object[] { "SMITH", new Integer(7369)});
            argsList.add(new Object[] { "SCOTT", new Integer(7788)});
            int result = handler.execute(argsList);
            System.out.println(result);
        } finally {
            container.destroy();
        }

    }
}

実行結果

DEBUG 2004-04-23 20:12:38,680 [main] 物理的なコネクションを取得しました
DEBUG 2004-04-23 20:12:38,710 [main] 論理的なコネクションを取得しました
DEBUG 2004-04-23 20:12:38,761 [main] UPDATE emp SET ename = 'SMITH' WHERE empno = 7369
DEBUG 2004-04-23 20:12:38,941 [main] UPDATE emp SET ename = 'SCOTT' WHERE empno = 7788
DEBUG 2004-04-23 20:12:38,971 [main] 論理的なコネクションを閉じました
2
DEBUG 2004-04-23 20:12:38,981 [main] 物理的なコネクションを閉じました

OracleのUnicode問題

OracleのJDBC Driverは、Unicodeのマッピングに一部問題があり、〜が化けてしまいます。この問題に対処するには、j2ee.diconのResultSetFactoryの設定を変更します。通常は下記のようになっています。

<component class="org.seasar.extension.jdbc.impl.BasicResultSetFactory"/>
<!--
<component class="org.seasar.extension.jdbc.impl.OracleResultSetFactory"/>
-->

BasicResultSetFactoryの部分をコメントアウトし、かわりに直ぐ下のOracleResultSetFactoryの部分のコメントをはずし有効にします。

<!--
<component class="org.seasar.extension.jdbc.impl.BasicResultSetFactory"/>
-->
<component class="org.seasar.extension.jdbc.impl.OracleResultSetFactory"/>

Booleanの扱い

Oracleのようにboolean型に対応していないRDBMSもあります。この問題に対処するには、j2ee.diconのCallableStatementFactoryの設定を変更します。通常は下記のようになっています。

<component class="org.seasar.extension.jdbc.impl.BasicCallableStatementFactory"/>
<!--
<component class="org.seasar.extension.jdbc.impl.BooleanToIntCallableStatementFactory"/>
-->

BasicCallableStatementFactoryの部分をコメントアウトし、かわりにBooleanToIntCallableStatementFactoryの部分のコメントをはずし有効にします。

<!--
<component class="org.seasar.extension.jdbc.impl.BasicCallableStatementFactory"/>
-->
<component class="org.seasar.extension.jdbc.impl.BooleanToIntCallableStatementFactory"/>

SQLのログの取得

S2JDBCで実行したSQLはログとして記録されるためアプリケーションから参照することが可能です。 SQLのログはorg.seasar.extension.jdbc.SqlRegistryのインスタンスから取得します。

SqlRegistryのインスタンスはDIもしくはルックアップにより取得できます。

DIのための設定は次のようになります。

<component name="sqlLogRegistry" class="org.seasar.extension.jdbc.SqlLogRegistry">
  @org.seasar.extension.jdbc.SqlLogRegistryLocator@getInstance()
</component>

プログラムから直接ルックアップする場合は次のようなコードが必要です。

SqlLogRegistry sqlLogRegistry = SqlLogRegistryLocator.getInstance();
SqlRegistryのインスタンスは複数のログを保持しています(デフォルトでは最新3つのログを保持します)。 最新の(最後に実行された)SQLのログは次のようなコードで取得できます。
SqlLog sqlLog = sqlLogRegistry.getLast();
org.seasar.extension.jdbc.SqlLogのインスタンスは次の情報を保持しています。
  • 未加工のSQL
  • バインド変数を実際の値で置き換えた完全なSQL
  • SQLにバインドされる値の配列
  • SQLにバインドされる値の型の配列