アメリカではほとんどデフェクトスタンダードとなっている「」の覚書きです。Spring は簡単に言うと、IoC (制御の反転)、またの名を DI (依存性注入) という仕組みを取り入れた軽量コンテナです。

Springは JDBCを抽象化した層を持っています。JDBCを使ったプログラミングで、共通化できる部分や、煩雑な部分をすっきりとした統一的な方法でアクセスできる手段を提供してくれます。

Springの詳細については、ほかにもっとよいサイト(Springフレームワークの紹介)があるので、そちらを参考にしてください。ここでは、Springを使っていて、ハマった点や気になった点などをメモしていこうと思います。随時更新していくつもりです。間違っている可能性が高いので、気になる点があればコメントをお願いします。

参考

Spring Pad - Wiki

JavaWorld 7月号 2004年

JDBC抽象化フレームワーク

JDBC抽象化層とは?

Springは、JDBCをラップした抽象化層を提供しています。これは、JDBCを使ってプログラミングを行う際に必ず必要となる、Connectionのオープン / クローズや Statement、ResultSet の生成と破棄をテンプレート化して提供するものであると考えてよいと思います。

もう一つは、SQLException を使わないで永続化処理が書けるというものです。通常、データベースへの処理は SQLException を明示的に処理しなければなりません。しかし、実際のアプリケーションでSQLExceptionをキャッチして処理するということはあまりありません。なぜなら、ほとんどのSQLExceptionの原因はアプリケーション内からでは復帰が難しいものだからです。

JDBC抽象化層は、SQLException よりももっと抽象化した実行時例外である org.springframework.dao.DataAccessExceptionを扱います。実行時例外であるため、開発者は無意味な SQLException をわざわざキャッチしなくてすむようになります。アプリケーション側で復帰できそうな例外に関してはキャッチして処理することも可能です。

JDBC抽象化層では、テーブルに対する検索(SELECT)や更新(INSERT / UPDATE / DELETE)を一つのオブジェクトとして定義する方法が提供されています。一つのSQL文に対して、一つのクラスを対応させる感じです。クラス数が非常に多くなっちゃいそうですね・・・。

JDBC抽象化層で使われる主な クラス / インターフェース

JDBC抽象化層で使われる主なクラスとインターフェースです。

org.springframework.dao.DataAccessException

JDBC抽象化層で使われるトップレベルのデータアクセス実行時例外クラス。開発者は必要であれば、このクラスのサブクラスを適切に処理することができます。

org.springframework.jdbc.datasource.DriverManagerDataSource

Beanのプロパティを使ってJDBCドライバ構成を組み立てます。このクラスは、常に新しいコネクションオブジェクトを返します。既知のサブクラスに SingleConnectionDataSource クラスがあります。

このクラスは、常に同一のコネクションを使いまわします。マルチスレッド環境では使えません。

Bean定義ファイルで宣言するときに設定する属性

driverClassName
JDBCドライバの完全クラス名です。
url
データベースへのURLです。
username
データベースへつなぐユーザ名
password
データベースへつなぐパスワード

接続設定を直接書くのではなくて、設定ファイルに定義しておいて、その値を読み込むといったことも出来ます。詳しくは 『Spring-Java/J2EE アプリケーションフレームワークドキュメント (3.6.2 PropertyPlaceholderConfigurer)』をどうぞ。

org.springframework.jdbc.object.MappingSqlQuery

テーブルに関する検索処理を行う、再利用可能な問い合わせクラスです。サブクラスでは、mapRow メソッドによって、 ResultSet を オブジェクトに変換する処理が書けます。オブジェクト変換時に、SQL文に設定したパラメータ(PreparedStatementのパラメータ)が必要な場合には、org.springframework.jdbc.object.MappingSqlQueryWithParameters を使用します。

SqlQuery#findObject メソッドは、ユニークなオブジェクトを返すメソッドとなります。そのため、SQL文の結果がたった一つになるようにしなければなりません。主キーを指定するSQL文を作るのが一般的な使い方でしょう。

mapRow メソッドは、実行したSQL文の結果セットが存在する場合(レコードがある場合)のみ呼び出されます。

findObject メソッド
SELECT文を実行します。実行結果が一つだけ存在する場合に使います。
execute メソッド
SELECT文を実行します。実行結果が複数ある場合に使います。

org.springframework.jdbc.object.SqlUpdate

SQLの更新を表すクラスです。MappingSqlQueryと同様に再利用な可能(PreparedStatement式)なオブジェクトになります。update / execute メソッドを呼び出すことで、更新処理(INSERT, UPDATE, DELETE)が実行されます。

update メソッド
INSERT文、UPDATE文、DELETE文を実行します。

org.springframework.jdbc.core.JdbcTemplate

JDBC抽象化層のコアクラスです。通常のSQLクエリを抽象化します。RowCallbackHandler を使って、ResultSet をオブジェクトに変換する処理が書けます(サンプルコード参照)。

query メソッド

問い合わせSQL文を発行します。引数に現れる ResultSetExtractorRowCallbackHandlerRowMapper はいづれも ResultSet に何らかの処理を加えられるものです。通常、オブジェクトへの変換を行います。

execute メソッド

あらゆる種類のSQL文を実行します。引数にとるコールバックインターフェースは、execute メソッドが呼ばれたときに呼び出されます。

update メソッド

更新系(INSERT, UPDATE, DELETE) のSQL文を発行します。

org.springframework.jdbc.core.support.JdbcDaoSupport

このクラスは、サブクラスに DataSource を提供できるようにしたクラスです。Bean定義書で DataSource の設定を書いておくと、IoCコンテナが自動的に DataSource を生成してセットしておいてくれます。サブクラスで JdbcTemplate を利用することが期待されているみたいです。

org.springframework.jdbc.object.SqlFunction

単一の結果を返す、SQL問い合わせのラッパークラスです。例えば、テーブルレコードのカウントをとりたい場合などに利用できます。サンプルコードは後述。基本的には SqlFunction の返す結果は int 型になるようです。他の方が使いたい場合には、runGeneric() メソッドを呼び出せばいいです。

JdbcTemplateを使った基本的なSQL文の実行

JdbcTemplate クラスを使ったSQL文の実行方法です。ここでは、RowCallbackHandler クラスを使って、検索結果をオブジェクトに変換する処理までやっています。更新系の処理なら、JdbcTemplate オブジェクトを作成して、execute メソッドの引数にSQL文を渡してやるだけで実行されるようです。

JdbcTemplateの例

public List findPersons() throws DataAccessException {
  JdbcTemplate template = new JdbcTemplate(getDataSource());
  final List result = new ArrayList();
  template.query("select ID, NAME, AGE from PERSON", 
                  new RowCallbackHandler() {
    public void processRow(ResultSet rs) throws SQLException {
      Person p = new Person(rs.getBigDecimal(1));
      p.setName(rs.getString(2));
      p.setAge(rs.getBigDecimal(3));
      result.add(p);
    }
  });
  return result;
}
 
public List findPersonsAsMap() {
  /*
   * List の要素は Map オブジェクト
   * key が 列名 で value が値
   * [ {ID=1, NAME=Taro, AGE=22}, {ID=2, NAME=Hanako,AGE=25} ]
   */
  JdbcTemplate jt = new JdbcTemplate(getDataSource());
  List result = jt.queryForList("select * from PERSON");
  return result;
}
 
public void addPerson(BigDecimal id, String name, BigDecimal age) {
  JdbcTemplate jt = new JdbcTemplate(getDataSource());
  jt.update("insert into PERSON (ID, NAME, AGE) " +
            "  values (?, ?, ?) ",
            new Object[] {id, name, age});
}

DataSource の設定の仕方

JdbcDaoSupport クラスには getDataSource() というデータ接続を返すメソッドが用意されています。DataSource の設定は、Bean定義に例えば次のように書きます。

DataSourceのBean定義例

<bean id="dataSource" 
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
 
  <property name="driverClassName">
    <value>org.hsqldb.jdbcDriver</value>
  </property>
 
  <property name="url">
    <value>jdbc:hsqldb:hsql://localhost</value>
  </property>
 
  <property name="username">
    <value>sa</value>
  </property>
 
  <property name="password">
    <value></value>
  </property>    
</bean>

SqlFunction クラスを使った単一問い合わせのサンプル

SqlFunction クラスを使うと、単一の問い合わせを表現することができます。通常、単一問い合わせの結果は int 型になるみたいです。複数件の結果が返ってきたり、オブジェクトに変換したりする処理が発生する場合には、 JdbcTemplate の例のような方法を使います。

テーブルのレコード数を返す例

public int countRows() {
  SqlFunction sf = 
    new SqlFunction(dataSource, "select count(*) from mytable");
  sf.compile();
  return sf.run();
}

参考

  • Spring の ロッドジョンソンが贈る、J2EE技術者のためのバイブル
  • Spring のロッドジョンソンによる Spring ユーザのための本 (洋書)
  • SpringでWebアプリケーションを作りながら、Springの全体像がわかりやすく解説されています。