http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#jdbc をテキトーに訳した。※途中で飽きたので一部抜けあり。
18. Data access with JDBC
18.1 Introduction to Spring Framework JDBC
Spring Framework JDBCの抽象化がもたらす価値を以下の表にまとめています。以下の表は、Springが提供するものと、アプリケーション開発者が設定などの責任を負うもの、に関する要約です。
Table 18.1. Spring JDBC - who does what?
Action | Spring | You |
---|---|---|
コネクションパラメータの定義 | X | |
コネクションの接続 | X | |
SQLステートメントの指定 | X | |
パラメータ宣言と値の指定 | X | |
ステートメントのプリペアと実行 | X | |
結果セット走査のセットアップ | X | |
結果セットループに対する操作 | X | |
例外処理 | X | |
トランザクション | X | |
コネクション・ステートメント・リザルトセットの切断 | X |
Spring FrameworkはJDBCなどの面倒なAPIを使用して行う低レベルの詳細部分を処理してくれます。
18.1.1 Choosing an approach for JDBC database access
JDBCデータアクセスには何種類かの方法があります。JdbcTemplate, SimpleJdbcInsert, SimplejdbcCallというデータベースメタデータを最適する三種類の方法に加え、RDBMS ObjectスタイルはJDO Queryに似たオブジェクト指向のアプローチが出来ます。その内の一つを使い始めた後、別の方法の機能と混在して使うことも出来ます。すべてのアプローチはJDBC 2.0準拠のドライバを要求し、一部の高度な機能はJDBC 3.0ドライバを要求します。
- JdbcTemplateはクラシックなSpring JDBCアプローチで最もポピュラーなものです。この"低レベル"アプローチとその他全ては裏側でJdbcTemplateを使用しています。
- NamedParameterJdbcTemplateは
JdbcTemplate
をラップし、伝統的なJDBCの"?"プレースホルダの代わりに名前付きパラメータを提供します。このアプローチによりSQLステートメントに複数パラメータを持ちたい場合の使い勝手と可読性が向上します。 - SimpleJdbcInsert and SimpleJdbcCallは必要な設定の量を制限するためにデータベースメタデータを最適化します。このアプローチはコーディングをシンプルにするもので、テーブルもしくはプロシージャ名を記述するだけでカラム名に対応するパラメータのマップを取得します。この機能はデータベースが適切なメタデータを返す場合のみ動作します。データベースがメタデータを返さない場合、パラメータの設定を明示的に指定する必要があります。
- RDBMS Objects including MappingSqlQuery, SqlUpdate and StoredProcedureはデータアクセスレイヤーの初期化中に再利用可能かつスレッドセーフなオブジェクトの生成を開発者に要求します。このアプローチはJDO Queryをモデルにしており、クエリ文字列の定義・パラメータ宣言・クエリのコンパイル、を行います。一度設定を行うと、渡す各種のパラメータで複数回メソッド実行が可能です。
18.1.2 Package hierarchy
Spring Framework’s JDBC抽象化フレームワークは4つのパッケージで構成されており、それぞれcore
, datasource
, object
, support
です。
org.springframework.jdbc.core
パッケージにはJdbcTemplate
クラスと各種のコールバックインタフェース、それらの関連クラス、があります。サブパッケージorg.springframework.jdbc.core.simple
にはSimpleJdbcInsert
とSimpleJdbcCall
があります。別のサブパッケージorg.springframework.jdbc.core.namedparam
にはNamedParameterJdbcTemplate
クラスとその関連サポートクラスがあります。Section 18.2, “Using the JDBC core classes to control basic JDBC processing and error handling”, Section 18.4, “JDBC batch operations”とSection 18.5, “Simplifying JDBC operations with the SimpleJdbc classes”を参照してください。
org.springframework.jdbc.datasource
パッケージにはDataSource
アクセス簡易化用のユーティリティクラスと、各種のシンプルなDataSource
実装があり、この実装によりJava EEコンテナ外でJDBCコードを修正することなくテストと実行が可能です。org.springfamework.jdbc.datasource.embedded
サブパッケージにはHSQL, H2, DerbyなどJavaデータベースエンジンを使用する組み込みデータベースの生成サポートがあります。Section 18.3, “Controlling database connections”とSection 18.8, “Embedded database support”を参照してください。
org.springframework.jdbc.object
パッケージにはRDBMSクエリと更新を表現するクラスとスレッドセーフで再利用オブジェクトなストアドプロシージャが含まれます。Section 18.6, “Modeling JDBC operations as Java objects”を参照してください。このアプローチはJDOをモデルにしていますが、クエリが返すオブジェクトはデータベースから切断されて(disconnected )います。この高レベルなJDBC抽象化はorg.springframework.jdbc.core
パッケージの低レベル抽象化に依存しています。
org.springframework.jdbc.support
パッケージはSQLException
の変換機能とユーティリティクラスを提供します。JDBC処理中にスローされた例外はorg.springframework.dao
パッケージに定義されている例外に変換されます。つまり、Spring JDBC抽象化レイヤーを使うコードではJDBCもしくはRDBMS固有のエラー処理を実装する必要がありません。変換された例外は未チェックのため、呼び出し元に伝播可能な例外で回復可能な例外の場合には選択肢を与えることにあります。Section 18.2.3, “SQLExceptionTranslator”を参照してください。
18.2 Using the JDBC core classes to control basic JDBC processing and error handling
18.2.1 JdbcTemplate
JdbcTemplate
クラスはJDBC coreパッケージの中核となるクラスです。リソースの生成と破棄を処理し、コネクション切断忘れなどよくあるエラーの回避を開発者の代わりにやってくれます。core JDBCのワークフローの基本的なタスクはステートメントの生成と実行で、SQLの指定と結果セットの展開はアプリケーションコードでやります。JdbcTemplate
クラスは、SQLクエリの実行、ステートメントの更新とストアドプロシージャの呼び出し、ResultSetsのイテレーションと返されたパラメータ値の展開、を行います。また、JDBC例外をキャッチして、ジェネリックで情報量の多いorg.springframework.dao
パッケージに定義された例外階層の例外クラスに変換します。
JdbcTemplate
を使うコードは、明確に定義された契約に基づき、コールバックインタフェースを実装するだけです。PreparedStatementCreator
コールバックインタフェースには、SQLと必要なパラメータを指定し、このコールバックに渡されるConncetion
を用いてプリペアードステートメントを生成します。CallableStatementCreator
はcallableステートメントを生成することと全く同じです。RowCallbackHandler
インタフェースはResultSet
の各行から値を抽出します。
JdbcTemplate
はDAO実装で使用可能で、DataSource
参照で直接インスタンス化したり、Spring IoDコンテナで設定したり、ビーン参照としてDAOに与えたりします。
DataSource
は常にSpring IoCコンテナのビーンとして設定すべきです。 In the first case the bean is given to the service directly; in the second case it is given to the prepared template.
このクラスで発生したSQLの問題はテンプレートインスタンス(通常はJdbcTemplate
ですがJdbcTemplate
のサブクラスをカスタムしている場合には異なるものになります)の完全修飾クラス名に対応するカテゴリ下にDEBUG
レベルでログ出力されます。
Examples of JdbcTemplate class usage
このセクションではJdbcTemplate
クラスの使用法のサンプルをいくつか紹介します。これらのサンプルはJdbcTemplate
が提供する機能をすべて網羅しているとは言えないため、詳細については関連javadocを参照してください。
Querying (SELECT)
以下はリレーションの行数を取得する簡単なクエリの例です。
int rowCount = this.jdbcTemplate.queryForObject("select count(*) from t_actor", Integer.class);
バインド変数を使用するクエリ。
int countOfActorsNamedJoe = this.jdbcTemplate.queryForObject( "select count(*) from t_actor where first_name = ?", Integer.class, "Joe");
String
を返すクエリ。
String lastName = this.jdbcTemplate.queryForObject( "select last_name from t_actor where id = ?", new Object[]{1212L}, String.class);
クエリと単一のドメインオブジェクトの処理。
Actor actor = this.jdbcTemplate.queryForObject( "select first_name, last_name from t_actor where id = ?", new Object[]{1212L}, new RowMapper<Actor>() { public Actor mapRow(ResultSet rs, int rowNum) throws SQLException { Actor actor = new Actor(); actor.setFirstName(rs.getString("first_name")); actor.setLastName(rs.getString("last_name")); return actor; } });
List<Actor> actors = this.jdbcTemplate.query( "select first_name, last_name from t_actor", new RowMapper<Actor>() { public Actor mapRow(ResultSet rs, int rowNum) throws SQLException { Actor actor = new Actor(); actor.setFirstName(rs.getString("first_name")); actor.setLastName(rs.getString("last_name")); return actor; } });
もし最後の二つが同一アプリケーションに存在する場合、二つのRowMapper
無名内部クラスの重複を除去したくなると思われ、その場合、単一クラス(通常はstatic
ネストクラス)に抽出し、必要に応じてDAOメソッドから参照します。たとえば、以下のように書き換えられます。
public List<Actor> findAllActors() { return this.jdbcTemplate.query( "select first_name, last_name from t_actor", new ActorMapper()); } private static final class ActorMapper implements RowMapper<Actor> { public Actor mapRow(ResultSet rs, int rowNum) throws SQLException { Actor actor = new Actor(); actor.setFirstName(rs.getString("first_name")); actor.setLastName(rs.getString("last_name")); return actor; } }
Updating (INSERT/UPDATE/DELETE) with jdbcTemplate
追加・更新・削除の実行にはupdate(..)
メソッドを使います。パラメータ値は可変長引数かobject配列として渡します。
this.jdbcTemplate.update( "insert into t_actor (first_name, last_name) values (?, ?)", "Leonor", "Watling");
this.jdbcTemplate.update( "update t_actor set last_name = ? where id = ?", "Banjo", 5276L);
this.jdbcTemplate.update( "delete from actor where id = ?", Long.valueOf(actorId));
Other jdbcTemplate operations
任意のSQL、DDLステートメントなど、の実行にはexecute(..)
メソッドを使います。オーバーロードされたメソッドが幾つかあり、各種のコールバックインターフェースを取るものや、変数配列のバインドなどがあります。
this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");
以下のサンプルはストアドプロシージャを呼び出しています。より高機能なストアドサポートについては後に解説します。
this.jdbcTemplate.update( "call SUPPORT.REFRESH_ACTORS_SUMMARY(?)", Long.valueOf(unionId));
JdbcTemplate best practices
JdbcTemplate
クラスのインスタンスは設定後はスレッドセーフ(threadsafe once configured)です。この点は重要で、つまり、単一のJdbcTemplate
インスタンスを設定後、安全にこのインスタンスの参照を共有して複数のDAO(かリポジトリ)にDIできます。JdbcTemplate
は、DataSource
の参照を保持し続けるという点でステートフルですが、その状態は会話的(conversational)な状態ではないです。
JdbcTemplate
クラス(と関連NamedParameterJdbcTemplate
クラス)のよくある使い方は、Spring設定ファイルでDataSource
を設定し、共有DataSource
ビーンをDAOクラスにDIします。JdbcTemplate
はDataSource
のセッター時に生成します。これに従うとDAOは以下のようなコードになります。
public class JdbcCorporateEventDao implements CorporateEventDao { private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } // 以下、JDBC経由でアクセスするCorporateEventDaoの実装 }
対応する設定ファイルは以下のようになります。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <bean id="corporateEventDao" class="com.example.JdbcCorporateEventDao"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <context:property-placeholder location="jdbc.properties"/> </beans>
明示的な設定ではなくコンポーネントスキャンとDIのアノテーションを使うことも出来ます。この場合、クラスに@Repository
(コンポーネントスキャンの候補にする)を付与し、@Autowired
をDataSource
のセッターメソッドに付与します。
@Repository public class JdbcCorporateEventDao implements CorporateEventDao { private JdbcTemplate jdbcTemplate; @Autowired public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } // 以下、JDBC経由でアクセスするCorporateEventDaoの実装 }
対応する設定ファイルは以下のようになります。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- @Componentをビーンとして設定するためにアプリケーションのbase パッケージ内をスキャンする --> <context:component-scan base-package="org.springframework.docs.test" /> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <context:property-placeholder location="jdbc.properties"/> </beans>
SpringのJdbcDaoSupport
クラスと、それを拡張する各種のJDBCベースDAOクラスを使う場合、JdbcDaoSupport
クラスのsetDataSource(..)
をサブクラスで継承します。このクラスを継承するかどうかは選択可能です。JdbcDaoSupport
クラスは便宜的な利用としてのみ提供されています。
テンプレートの初期化がどういうやり方であろうと、SQLを実行するたびにJdbcTemplate
クラスの新規インスタンスを生成する必要性はまずありません。一度設定すればJdbcTemplate
インスタンスはスレッドセーフです。アプリケーションが複数データベースにアクセスするのであれば複数のJdbcTemplate
インスタンスが必要で、その場合は複数のDataSources
が必要で、よって複数の異なる設定をしたJdbcTemplates
を作ることになります。
18.2.2 NamedParameterJdbcTemplate
NamedParameterJdbcTemplate
は、クラシックなプレースホルダー(?
)引数を使用したJDBCのステートメントに対し、名前付きパラメータでJDBCステーメントを操作するサポートを追加したものです。NamedParameterJdbcTemplate
クラスはJdbcTemplate
をラップし、より便利に使えるようにラップしたJdbcTemplate
にデリゲートします。このセクションではJdbcTemplate
自体ではなくNamedParameterJdbcTemplate
クラスの範囲についてのみ解説します。つまり、名前付き引数を使用したJDBCステーメントについてです。
// some JDBC-backed DAO class... private NamedParameterJdbcTemplate namedParameterJdbcTemplate; public void setDataSource(DataSource dataSource) { this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); } public int countOfActorsByFirstName(String firstName) { String sql = "select count(*) from T_ACTOR where first_name = :first_name"; SqlParameterSource namedParameters = new MapSqlParameterSource("first_name", firstName); return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class); }
sql
変数への代入値としての名前付きパラメータ記法と、それに対応する値は(MapSqlParameterSource
型の)namedParameters
変数で結びつけます。
もしくは、Map
で名前付きパラメータとそれに対応する値をNamedParameterJdbcTemplate
に渡せます。NamedParameterJdbcOperations
の公開メソッドとNamedParameterJdbcTemplate
の実装メソッドは似たようなパターンになっていますが、その点についてはここでは触れません。
以下の例はMap
ベーススタイル使った例です。
// some JDBC-backed DAO class... private NamedParameterJdbcTemplate namedParameterJdbcTemplate; public void setDataSource(DataSource dataSource) { this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); } public int countOfActorsByFirstName(String firstName) { String sql = "select count(*) from T_ACTOR where first_name = :first_name"; Map<String, String> namedParameters = Collections.singletonMap("first_name", firstName); return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class); }
NamedParameterJdbcTemplate
の関連機能の一つが(同一パッケージ内の)SqlParameterSource
インタフェースです。このインタフェース実装の使い方の例は前述のサンプルで(MapSqlParameterSource
クラス)示しました。SqlParameterSource
はNamedParameterJdbcTemplate
に渡す名前付きパラメータ値です。MapSqlParameterSource
クラスはjava.util.Map
のアダプターとなるごくシンプルな実装で、キーがパラメータ名で値がパラメータ値となります。
別のSqlParameterSource
実装にBeanPropertySqlParameterSource
クラスがあります。このクラスは任意のJavaBean(つまりthe JavaBean conventions)に従うクラスのインスタンス)をラップし、ラップしたJavaBeanのプロパティを名前付きパラメータ値として使用します。
public class Actor { private Long id; private String firstName; private String lastName; public String getFirstName() { return this.firstName; } public String getLastName() { return this.lastName; } public Long getId() { return this.id; } // setters omitted... }
// some JDBC-backed DAO class... private NamedParameterJdbcTemplate namedParameterJdbcTemplate; public void setDataSource(DataSource dataSource) { this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); } public int countOfActors(Actor exampleActor) { // notice how the named parameters match the properties of the above 'Actor' class String sql = "select count(*) from T_ACTOR where first_name = :firstName and last_name = :lastName"; SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(exampleActor); return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class); }
先にNamedParameterJdbcTemplate
クラスはクラシックなJdbcTemplate
をラップしている、との述べました。もしJdbcTemplate
クラスにだけ存在する機能にアクセスしたい場合、JdbcOperations
インタフェースのラップされたJdbcTemplate
にアクセスするためのgetJdbcOperations()
メソッドを使います。
アプリケーションコンテキストでNamedParameterJdbcTemplate
クラスを使うガイドラインについてはthe section called “JdbcTemplate best practices”を参照してください。
18.2.3 SQLExceptionTranslator
SQLExceptionTranslator
はSQLExceptions
とSpring固有のorg.springframework.dao.DataAccessException
に変換可能なクラスが実装するインタフェースで、データアクセスの手段とは切り離されています。実装はジェネリック(例えばJDBCのSQLStateコードを使用)かより正確を期すためにプロプライエタリ(例えばOracleのエラーコードを使用)に出来ます。
SQLErrorCodeSQLExceptionTranslator
はSQLExceptionTranslator
の実装でデフォルトです。この実装はベンダー固有のコードを使います。SQLState
実装よりかは正確です。エラーコード変換はSQLErrorCodes
というJavaBean型が持つコードに基づいて行われます。このクラスはSQLErrorCodesFactory
が生成と処理を行い、クラス名の由来についてはsql-error-codes.xml
設定ファイルの内容に基づいてSQLErrorCodes
を生成するファクトリーである、という点から来ています。このファイルにはベンダーコードが含まれ、DatabaseMetaData
から取得したDatabaseProductName
に基づきます。使用している実際のデータベースのコードが使われます。
SQLErrorCodeSQLExceptionTranslator
は以下の順序でマッチングルールを適用します。
SQLErrorCodesFactory
はデフォルトのエラーコード定義とカスタム例外変換に使われます。エラーコードはクラスパスのsql-error-codes.xml
ファイル内を参照し、マッチするSQLErrorCodes
インスタンスが使用中のデータベースのメタデータのDB名に基づいて配置されます。
- サブクラスによるカスタム変換の実装。通常は
SQLErrorCodeSQLExceptionTranslator
が使われるのでこのルールは適用されません。このルールが適用されるのはサブクラス実装を自前で作成する場合だけです。 SQLErrorCodes
クラスのcustomSqlExceptionTranslator
プロパティとして提供されるSQLExceptionTranslator
インタフェースのカスタム実装。CustomSQLErrorCodesTranslation
クラスのインスタンスのリスト、SQLErrorCodes
クラスのcustomTranslations
プロパティで提供される。このリストが検索条件にマッチする場合。- マッチするエラーコードの適用
- フォールバックの使用。
SQLExceptionSubclassTranslator
はデフォルトのフォールバックトランスレータです。この変換が利用不可能な場合、次のフォールバックトランスレータはSQLStateSQLExceptionTranslator
になります。
SQLErrorCodeSQLExceptionTranslator
を拡張可能です。
public class CustomSQLErrorCodesTranslator extends SQLErrorCodeSQLExceptionTranslator { protected DataAccessException customTranslate(String task, String sql, SQLException sqlex) { if (sqlex.getErrorCode() == -12345) { return new DeadlockLoserDataAccessException(task, sqlex); } return null; } }
上記の例では、固有のエラーコード-12345
を変換し、それ以外はデフォルトのトランスレータ実装に任せています。上記のカスタムトランスレータを使用するには、setExceptionTranslator
メソッド経由でJdbcTemplate
に渡し、このトランスレータが必要となるすべてのデータアクセス処理でJdbcTemplate
を使う必要があります。以下はカスタムトランスレータの使い方の例です。
private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { // create a JdbcTemplate and set data source this.jdbcTemplate = new JdbcTemplate(); this.jdbcTemplate.setDataSource(dataSource); // create a custom translator and set the DataSource for the default translation lookup CustomSQLErrorCodesTranslator tr = new CustomSQLErrorCodesTranslator(); tr.setDataSource(dataSource); this.jdbcTemplate.setExceptionTranslator(tr); } public void updateShippingCharge(long orderId, long pct) { // use the prepared JdbcTemplate for this update this.jdbcTemplate.update("update orders" + " set shipping_charge = shipping_charge * ? / 100" + " where id = ?", pct, orderId); }
カスタムトランスレータに渡すデータソースはsql-error-codes.xml
のエラーコードのルックアップに使います。
18.2.4 Executing statements
SQLステートメントの実行に必要なコードはほんのわずかです。必要なのはDataSource
とJdbcTemplate
で、JdbcTemplate
には便利なメソッドがいくつかあります。以下の例は新規テーブルを作成する機能を持つクラスに最低限必要となるコードの例です。
import javax.sql.DataSource; import org.springframework.jdbc.core.JdbcTemplate; public class ExecuteAStatement { private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } public void doExecute() { this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))"); } }
18.2.5 Running queries
クエリーのメソッドによっては単一値を返すものがあります。カウントや一行の特定値を取得するにはqueryForObject(..)
を使います。戻されるJDBCType
を引数に渡したJavaクラスに変換します。型変換が妥当ではない場合、InvalidDataAccessApiUsageException
がスローされます。以下はクエリーメソッドの例で、一つはint
でもう一つはString
を戻り値に要求します。
import javax.sql.DataSource; import org.springframework.jdbc.core.JdbcTemplate; public class RunAQuery { private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } public int getCount() { return this.jdbcTemplate.queryForObject("select count(*) from mytable", Integer.class); } public String getName() { return this.jdbcTemplate.queryForObject("select name from mytable", String.class); } public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } }
単一結果を返すクエリーメソッドの他に、クエリーが戻す各行のエントリから成るリストを戻すメソッドもあります。最も汎用のメソッドはqueryForList(..)
で、List
を返し各行はカラム値を表現するMap
です。上記の例に全行をリストで取得するメソッドを追加する場合、以下のようになります。
private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } public List<Map<String, Object>> getList() { return this.jdbcTemplate.queryForList("select * from mytable"); }
リストは以下のようなものが返されます。
18.2.6 Updating the database
以下の例は主キーでカラムを更新する例です。以下の例では、SQLステートメントはパラメータ用にプレースホルダーを持っています。パラメータ値は可変長引数かオブジェクト配列で渡せます。プリミティブは明示的もしくはオートボクシングでラッパークラスにラップすべきです。
import javax.sql.DataSource; import org.springframework.jdbc.core.JdbcTemplate; public class ExecuteAnUpdate { private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } public void setName(int id, String name) { this.jdbcTemplate.update("update mytable set name = ? where id = ?", name, id); } }
18.2.7 Retrieving auto-generated keys
update()
はデータベースが生成する主キー取得をサポートするメソッドです。このサポートはJDBC 3.0標準の一部で、詳細は仕様のChapter 13.6を参照してください。このメソッドは最初の引数にPreparedStatementCreator
を取り、これには挿入のステートメントを指定します。それ以外の引数はKeyHolder
で、これにはupdateが正常時に返す生成キーが含まれます。PreparedStatement
(which explains why the method signature is the way it is)を生成する単一で標準的な方法というわけではありません。以下の例はOracleでは動作しますが他プラットフォームでは動作しない可能性があります。
final String INSERT_SQL = "insert into my_test (name) values(?)"; final String name = "Rob"; KeyHolder keyHolder = new GeneratedKeyHolder(); jdbcTemplate.update( new PreparedStatementCreator() { public PreparedStatement createPreparedStatement(Connection connection) throws SQLException { PreparedStatement ps = connection.prepareStatement(INSERT_SQL, new String[] {"id"}); ps.setString(1, name); return ps; } }, keyHolder); // keyHolder.getKey() now contains the generated key
18.3 Controlling database connections
18.3.1 DataSource
SpringはDataSource
経由でデータベースへのコネクションを取得します。DataSource
はJDBC仕様の一部でコネクションファクトリーを汎用化したものです。このクラスによりコンテナやフレームワークでコネクションプーリングやトランザクション関連の問題をアプリケーションコードから隠すことが可能となります。開発者にとっては、データベース接続に関する詳細を知る必要がなくなります。詳細に関する責任はデータベースをセットアップする管理者のものとなります。開発者は開発とテストコードに責任は持ちますが、本番環境のデータソースの設定方法については知る必要はありません。
SpringのJDBCレイヤーを使う場合、データソースはJNDIから取得したり、サードパーティ提供のコネクションプール実装を設定したりします。ポピュラーな実装はApache Jakarta Commons DBCPとC3P0です。Springディストリビューションの実装はテスト目的のものだけなのでプーリングは提供しません。
このセクションではSpringのDriverManagerDataSource
実装と、後に説明するいくつかの別の実装を使います。
DriverManagerDataSource
クラスの使用はテスト目的のためだけに使用してください。このクラスはプーリングを提供せず、コネクションを複数要求する場合のパフォーマンスが悪いためです。
JDBCのコネクションを得るようにDriverManagerDataSource
でコネクションを取得します。DriverManager
がドライバクラスをロードするために、JDBCドライバの完全修飾クラス名を指定します。次に、JDBCドライバごとに異なるURLを指定します。(ドライバごとの正しい値については各ドキュメントを参照してください)それから、データベース接続のためにユーザ名とパスワードを指定します。以下はJavaコードでDriverManagerDataSource
を設定する方法です。
DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("org.hsqldb.jdbcDriver"); dataSource.setUrl("jdbc:hsqldb:hsql://localhost:"); dataSource.setUsername("sa"); dataSource.setPassword("");
以下は上記に相当するXML設定です。
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <context:property-placeholder location="jdbc.properties"/>
上記はDBCPとC3P0の基本的な設定と接続の例です。プーリング機能の制御オプションなど詳細については、個々のコネクションプーリング実装のドキュメントを参照してください。
DBCPの設定。
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <context:property-placeholder location="jdbc.properties"/>
C3P0の設定。
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass" value="${jdbc.driverClassName}"/> <property name="jdbcUrl" value="${jdbc.url}"/> <property name="user" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <context:property-placeholder location="jdbc.properties"/>
18.3.2 DataSourceUtils
DataSourceUtils
クラスは必要に応じてJNDIからコネクションの接続と切断のためのstatic
メソッドを提供するヘルパークラスです。DataSourceTransactionManager
を使用してスレッドにバインドするコネクションをサポートします。
18.3.3 SmartDataSource
SmartDataSource
インタフェース関係型データベースへの接続を提供可能なクラスが実装すべきクラスです。DataSource
インタフェースを拡張しており、このインタフェースを使うクラスが指定操作後にコネクションを接続すべきかどうかを問い合わせることが可能です。開発者がコネクションを再利用すると知っている場合に有用です。
18.3.4 AbstractDataSource
AbstractDataSource
はSpringのDataSource
実装のベースとなるabstract
クラスで、すべてのDataSource
実装に共通なコードが含まれます。自前のDataSource
実装を作る場合にはこのAbstractDataSource
クラスを拡張します。
18.3.5 SingleConnectionDataSource
SingleConnectionDataSource
クラスはSmartDataSource
インタフェースの実装で単一のConnection
をラップし、使用後にはクローズされません。このことから明らかなとおり、マルチスレッド環境には適しません。
いま、何らかの永続化ツールを使用するなど、コネクションプーリングを使っていると仮定して、その上でクライアントコードがclose
を呼ぶ場合、suppressClose
プロパティをtrue
に設定してください。この設定により物理コネクションをラップしてクローズを抑止するプロキシが返されます。ネイティブのOracleConnection
などにはキャスト出来ない点に注意してください。
主にテスト用のクラスとして使います。たとえば、単純なJNDI環境と組み合わせて、アプリケーションサーバ外でコードのちょっとしたテストをする、などです。DriverManagerDataSource
とは対照的に、物理コネクションの過度な生成を避けて常に同一のコネクションを再利用します。
18.3.6 DriverManagerDataSource
DriverManagerDataSource
クラスは一般的なDataSource
インタフェースの実装で、ビーンプロパティ経由でJDBCドライバを設定し、常に新規のConnection
を返します。
この実装は、Spring IoCコンテナのDataSource
ビーンやJNDI環境との組み合わせなど、Java EEコンテナ外のスタンドアローン環境とテストに便利です。プーリングを行うConnection.close()
はただ単にコネクションをクローズし、どんなDataSrouce
ベースの永続化コードでも動作します。commons-dbcp
などのJavaBeanスタイルのコネクションプールを使う方が簡単で、テスト環境においても同様に、DriverManagerDataSource
の上でコネクションプールなどを使うのが望ましいです。
18.3.7 TransactionAwareDataSourceProxy
TransactionAwareDataSourceProxy
はターゲットとなるDataSource
のプロキシで、Springマネージドトランザクションの機能を追加するためにターゲットとなるDataSource
をラップします。その点では、Java EEサーバが提供するトランザクショナルなJNDI DataSource
と似ています。
このクラスを使用した場合は稀であり、ただし、既存コードに標準JDBCDataSource
インタフェースの実装を渡して呼び出す必要がある場合を除きます。この除外ケースの場合に、既存コードがそのまま使用可能で、同時にコードをSpringマネージドトランザクション下に置くことが出来ます。通常、新規のコードを書く場合、リソース管理にはJdbcTemplate
やDataSourceUtils
などの高レベル抽象化を使用するのが望ましいです。
(詳細についてはTransactionAwareDataSourceProxy
のjavadocを参照してください)
18.3.8 DataSourceTransactionManager
DataSourceTransactionManager
クラスはPlatformTransactionManager
の実装で単一JDBCデータソース用です。指定のデータソースからのJDBCコネクションを現在実行中のスレッドにバインドし、データソースごとに1スレッドに1コネクションが可能になります*1。
アプリケーションコードではJava EE標準DataSource.getConnection
の代わりにDataSourceUtils.getConnection(DataSource)
経由でJDBCコネクションを取得する必要があります。チェック例外SQLExceptions
の代わりに未チェック例外org.springframework.dao
をスローします。JdbcTemplate
などすべてのフレームワーククラスは暗黙的にこの機能を使用しています。If not used with this transaction manager, the lookup strategy behaves exactly like the common one - it can thus be used in any case.
DataSourceTransactionManager
クラスはカスタム分離レベルと、JDBCステートメントのクエリタイムアウトとして適用されるタイムアウト、をサポートします。後者をサポートするには、アプリケーションコードでJdbcTemplate
を使うか生成するステートメントごとにDataSourceUtils.applyTransactionTimeout(..)
を呼ぶ必要があります。
JTAをサポートするコンテナが不要など、単一リソースの場合にJtaTransactionManager
の代わりにこの実装を使用可能です。コネクションのルックアップパターンを変えたい場合、両者を切り替えるには設定を変更するだけです。JTAはカスタム分離レベルをサポートしていません。
18.3.9 NativeJdbcExtractor
稀に、標準JDBC APIとは異なるベンダー固有のJDBCメソッドにアクセスしたい場合があります。アプリケーションサーバで動作させようとしたり、Connection
, Statement
, ResultSet
をラップするDataSource
と共に動作させようとすると、困難が伴います。ネイティブオブジェクトへのアクセスを取得するには、JdbcTemplate
を設定するかNativeJdbcExtractor
と共にOracleLobHandler
を使います。
NativeJdbcExtractor
は実行環境に適合するために様々な種類があります。
- SimpleNativeJdbcExtractor
- C3P0NativeJdbcExtractor
- CommonsDbcpNativeJdbcExtractor
- JBossNativeJdbcExtractor
- WebLogicNativeJdbcExtractor
- WebSphereNativeJdbcExtractor
- XAPoolNativeJdbcExtractor
通常はSimpleNativeJdbcExtractor
を使用して各種の環境でConnection
オブジェクトをアンラップすれば十分です。詳細についてはjavadocを参照してください。
18.4 JDBC batch operations
大抵のJDBCドライバには、バッチで同一のプリペアードステートメントを複数回呼び出す場合の、パフォーマン向上機能を提供しています。バッチ更新をグループ化することでデータベースとのラウンドトリップ回数を制限できます。
18.4.1 Basic batch operations with the JdbcTemplate
専用インタフェースBatchPreparedStatementSetter
の二つのメソッドを実装することでJdbcTemplate
のバッチ処理を実現できます。batchUpdate
メソッド呼び出しの第二引数にその実装を渡します。getBatchSize
は現在のバッチのサイズを指定するのに使います。setValues
はプリペアードステートメントのパラメータに値を設定するのに使います。setValues
はgetBatchSize
の回数分呼び出されます。以下のサンプルではリストを使用してactorテーブルを更新しています。この例ではリストの内容すべてがバッチに使われます。
public class JdbcActorDao implements ActorDao { private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } public int[] batchUpdate(final List<Actor> actors) { int[] updateCounts = jdbcTemplate.batchUpdate("update t_actor set first_name = ?, " + "last_name = ? where id = ?", new BatchPreparedStatementSetter() { public void setValues(PreparedStatement ps, int i) throws SQLException { ps.setString(1, actors.get(i).getFirstName()); ps.setString(2, actors.get(i).getLastName()); ps.setLong(3, actors.get(i).getId().longValue()); } public int getBatchSize() { return actors.size(); } }); return updateCounts; } // ... additional methods }
ファイルを読み込んでのストリーム処理の場合、適切なバッチサイズを指定する必要がありますが、エントリの数が分からない可能性があります。この場合は、InterruptibleBatchPreparedStatementSetter
インタフェースを使用します。このインタフェースにより入力ソースを読み終えるまでバッチをインタラプト出来ます。isBatchExhausted
でバッチの終了を判定可能です。
18.4.2 Batch operations with a List of objects
JdbcTemplate
とNamedParameterJdbcTemplate
によりバッチ更新に別の方法を取ることが出来ます。専用のバッチインタフェースを実装する代わりに、リスト形式ですべてのパラメータ値を指定します。フレームワークがループ処理で内部的にプリペアードステートメントのセッターに与えます。このAPIは名前付きパラメータを使う点が異なります。名前付きパラメータではSqlParameterSource
の配列を使い、各エントリがバッチのメンバになります。配列の生成にはSqlParameterSource.createBatch
を使い、引数にはJavaBeansの配列かパラメータ値を含むMapの配列を渡します。
名前付きパラメータを使用するバッチ更新の例は以下の通りです。
public class JdbcActorDao implements ActorDao { private NamedParameterTemplate namedParameterJdbcTemplate; public void setDataSource(DataSource dataSource) { this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); } public int[] batchUpdate(final List<Actor> actors) { SqlParameterSource[] batch = SqlParameterSourceUtils.createBatch(actors.toArray()); int[] updateCounts = namedParameterJdbcTemplate.batchUpdate( "update t_actor set first_name = :firstName, last_name = :lastName where id = :id", batch); return updateCounts; } // ... additional methods }
クラシックな"?"プレースホルダを使うSQLステートメントの場合、更新する値のオブジェクト配列のリストを渡します。オブジェクト配列にはSQLステートメントの各プレースホルダに対応する一つのエントリを持つ必要があり、SQLステートメントの定義と同じ順序である必要があります。
上記と同じことをクラシックなJDBC"?"プレースホルダを使う例は以下です。
public class JdbcActorDao implements ActorDao { private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } public int[] batchUpdate(final List<Actor> actors) { List<Object[]> batch = new ArrayList<Object[]>(); for (Actor actor : actors) { Object[] values = new Object[] { actor.getFirstName(), actor.getLastName(), actor.getId()}; batch.add(values); } int[] updateCounts = jdbcTemplate.batchUpdate( "update t_actor set first_name = ?, last_name = ? where id = ?", batch); return updateCounts; } // ... additional methods }
上記すべてのバッチ更新メソッドは各バッチエントリが影響を与えた行数を含むint配列を返します。このカウントはJDBCドライバがレポートするものです。カウントが利用不可能の場合、JDBCドライバは-2を返します。
18.4.3 Batch operations with multiple batches
バッチ更新の最後の例は巨大なバッチのためにいくつかのバッチに小分けしたい場合です。上記のbatchUpdate
メソッドを複数回呼び出して解決することも可能ですが、より便利なメソッドが存在します。このメソッドはSQLステートメントに加え、パラメータを持つオブジェクトのCollection、各バッチごとの更新数、プリペアードステートメントのパラメータに値を設定するためのParameterizedPreparedStatementSetter
、を渡します。フレームワークは与えられた値をループして指定サイズでバッチ更新を行います。
以下の例はサイズ100のバッチ更新の例です。
public class JdbcActorDao implements ActorDao { private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } public int[][] batchUpdate(final Collection<Actor> actors) { int[][] updateCounts = jdbcTemplate.batchUpdate( "update t_actor set first_name = ?, last_name = ? where id = ?", actors, 100, new ParameterizedPreparedStatementSetter<Actor>() { public void setValues(PreparedStatement ps, Actor argument) throws SQLException { ps.setString(1, argument.getFirstName()); ps.setString(2, argument.getLastName()); ps.setLong(3, argument.getId().longValue()); } }); return updateCounts; } // ... additional methods }
この場合のバッチ更新メソッドはintの二次元配列を返し、各バッチごとの配列で、その配列の各要素の配列は更新ごとに影響を受けた行数です。一次元目の配列長は実行したバッチの数で、二次元目の配列長は各バッチごとの更新数です。各バッチの更新数は指定のバッチサイズで最後の一つは指定のサイズより少なくなる事が多いですが、更新オブジェクトの合計数に依存します。更新ステートメントのカウントはJDBCがレポートしたものです。カウントが利用不可能の場合、JDBCドライバは-2を返します。
18.5 Simplifying JDBC operations with the SimpleJdbc classes
SimpleJdbcInsert
とSimpleJdbcCall
はJDBCドライバ経由で取得可能なデータベースメタデータを活かして設定の単純化をしています。つまり事前の設定が少なくて済みますが、もしコードで詳細を指定したい場合にはメタデータ処理を切ったりオーバーライド可能です。
18.5.1 Inserting data using SimpleJdbcInsert
最小の設定オプションでSimpleJdbcInsert
クラスを見ていきます。データアクセスレイヤの初期化メソッドでSimpleJdbcInsert
をインスタンス化します。例えば、初期化メソッドとはsetDataSource
などです。SimpleJdbcInsert
クラスをサブクラス化する必要は無く、単に新規のインスタンスをっ作成してwithTableName
メソッドでテーブル名を指定します。このクラスの設定メソッドは"fluid"(流れるようなインタフェース)スタイルなのでSimpleJdbcInsert
のインスタンスを返し、すべての設定メソッドをチェーンで呼び出せます。以下のサンプルでは設定メソッドは一つだけ使用しており、複数の例は後述します。
public class JdbcActorDao implements ActorDao { private JdbcTemplate jdbcTemplate; private SimpleJdbcInsert insertActor; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); this.insertActor = new SimpleJdbcInsert(dataSource).withTableName("t_actor"); } public void add(Actor actor) { Map<String, Object> parameters = new HashMap<String, Object>(3); parameters.put("id", actor.getId()); parameters.put("first_name", actor.getFirstName()); parameters.put("last_name", actor.getLastName()); insertActor.execute(parameters); } // ... additional methods }
上記で使われているexecuteメソッドは単一パラメータとしてjava.utils.Map
を取ります。ここでの重要なポイントはMapのキーはデータベースに定義してあるテーブルのカラム名と一致する必要があります。実際のinsertステートメントを構築する際にメタデータを読み取るため、一致することは重要です。
18.5.2 Retrieving auto-generated keys using SimpleJdbcInsert
以下の例は前述と同じinsertですが、idを渡す代わりに自動生成キーを参照して新規のActorオブジェクトに設定します。SimpleJdbcInsert
を生成する時にテーブル名に加えてusingGeneratedKeyColumns
メソッドで生成キーのカラム名を指定します。
public class JdbcActorDao implements ActorDao { private JdbcTemplate jdbcTemplate; private SimpleJdbcInsert insertActor; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); this.insertActor = new SimpleJdbcInsert(dataSource) .withTableName("t_actor") .usingGeneratedKeyColumns("id"); } public void add(Actor actor) { Map<String, Object> parameters = new HashMap<String, Object>(2); parameters.put("first_name", actor.getFirstName()); parameters.put("last_name", actor.getLastName()); Number newId = insertActor.executeAndReturnKey(parameters); actor.setId(newId.longValue()); } // ... additional methods }
前述の例とこのやり方でのinsert実行時の違いは、Mapにidを追加不要なのとexecuteAndReturnKey
メソッドを呼ぶ点です。このメソッドが返すのは、ドメインクラスで使われる数値型のインスタンスを生成可能なjava.lang.Number
となります*2。特定のJavaクラスを返すようにはすべてのデータベースに依存出来ません。java.lang.Number
が依存可能なベースクラスです。複数の自動生成カラムがあるか、生成値が非数値の場合、executeAndReturnKeyHolder
が返すKeyHolder
を使います。
18.5.3 Specifying columns for a SimpleJdbcInsert
usingColumns
メソッドでカラム名のリストを指定することによりinsertのカラムを制限できます。
public class JdbcActorDao implements ActorDao { private JdbcTemplate jdbcTemplate; private SimpleJdbcInsert insertActor; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); this.insertActor = new SimpleJdbcInsert(dataSource) .withTableName("t_actor") .usingColumns("first_name", "last_name") .usingGeneratedKeyColumns("id"); } public void add(Actor actor) { Map<String, Object> parameters = new HashMap<String, Object>(2); parameters.put("first_name", actor.getFirstName()); parameters.put("last_name", actor.getLastName()); Number newId = insertActor.executeAndReturnKey(parameters); actor.setId(newId.longValue()); } // ... additional methods }
使用カラムの決定にメタデータに依存していた場合と同じ事になります。
18.5.4 Using SqlParameterSource to provide parameter values
パラメータ値を渡すのにMap
で十分動作しますが、便利だとは言えません。Springはその代わりとなるSqlParameterSource
インタフェースの実装を提供しています。最初の一つはBeanPropertySqlParameterSource
で、値をJavaBean互換のクラスで持つ場合には大変便利なクラスです。パラメータ値の抽出には対応するgetterメソッドを使います。以下がその例です。
public class JdbcActorDao implements ActorDao { private JdbcTemplate jdbcTemplate; private SimpleJdbcInsert insertActor; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); this.insertActor = new SimpleJdbcInsert(dataSource) .withTableName("t_actor") .usingGeneratedKeyColumns("id"); } public void add(Actor actor) { SqlParameterSource parameters = new BeanPropertySqlParameterSource(actor); Number newId = insertActor.executeAndReturnKey(parameters); actor.setId(newId.longValue()); } // ... additional methods }
別のオプションにMapSqlParameterSource
があり、Mapに似ていますが、より便利なメソッドチェーンが可能なaddValue
を持っています。
public class JdbcActorDao implements ActorDao { private JdbcTemplate jdbcTemplate; private SimpleJdbcInsert insertActor; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); this.insertActor = new SimpleJdbcInsert(dataSource) .withTableName("t_actor") .usingGeneratedKeyColumns("id"); } public void add(Actor actor) { SqlParameterSource parameters = new MapSqlParameterSource() .addValue("first_name", actor.getFirstName()) .addValue("last_name", actor.getLastName()); Number newId = insertActor.executeAndReturnKey(parameters); actor.setId(newId.longValue()); } // ... additional methods }
以上の例は、設定は共通で、それぞれの入力クラスを使うようにコードを変更している点だけが異なります。
18.5.5 Calling a stored procedure with SimpleJdbcCall
SimpleJdbcCall
クラスはin
とout
パラメータ名をルックアップするのにデータベースのメタデータを参照するため、明示的にそれらを宣言する必要はありません。もし明示的に宣言したい場合や、Javaクラスへの自動マッピングが出来ないARRAY
やSTRUCT
などのパラメータがある場合、パラメータの宣言が可能です。最初の例はシンプルなプロシージャでMySQLデータベースからVARCHAR
とDATE
フォーマットのスカラ値を返します。サンプルのプロシージャは指定されたactorエントリを読み込みout
パラメータ形式でfirst_name
, last_name
, birth_date
を返します。
CREATE PROCEDURE read_actor ( IN in_id INTEGER, OUT out_first_name VARCHAR(100), OUT out_last_name VARCHAR(100), OUT out_birth_date DATE) BEGIN SELECT first_name, last_name, birth_date INTO out_first_name, out_last_name, out_birth_date FROM t_actor where id = in_id; END;
in_id
パラメータは検索対象のactorのid
です。out
パラメータはテーブルから読み込んだデータを返します。
SimpleJdbcCall
はSimpleJdbcInsert
と似たような形式で宣言されています。データアクセスレイヤの初期化メソッドでこのクラスのインスタンス化と設定を行います。StoredProcedureクラスとは対照的に、サブクラスを生成したりデータベースメタデータでルックアップ可能なパラメータを宣言してはいけません。以下は上記のストアドプロシージャを使うSimpleJdbcCall設定のサンプルです。設定オプションはDataSource
とストアドプロシージャ名だけです。
public class JdbcActorDao implements ActorDao { private JdbcTemplate jdbcTemplate; private SimpleJdbcCall procReadActor; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); this.procReadActor = new SimpleJdbcCall(dataSource) .withProcedureName("read_actor"); } public Actor readActor(Long id) { SqlParameterSource in = new MapSqlParameterSource() .addValue("in_id", id); Map out = procReadActor.execute(in); Actor actor = new Actor(); actor.setId(id); actor.setFirstName((String) out.get("out_first_name")); actor.setLastName((String) out.get("out_last_name")); actor.setBirthDate((Date) out.get("out_birth_date")); return actor; } // ... additional methods }
上記のコードではINパラメータに渡す値を持つSqlParameterSource
を生成しています。重要な点は、ストアドプロシージャで宣言するパラメータ名と入力値の名前が一致していることです。このケースでは一致していませんが、ストアドプロシージャで参照されるデータベースオブジェクトの決定にメタデータを使用するためです。ストアドプロシージャのソースがデータベースにそのまま保存されるとは限りません。データベースによっては小文字をすべて大文字に変換するものがあります。
execute
メソッドはINパラメータを取り、ストアドプロシージャで定義した名前がキーとなるout
パラメータのMapを返します。上記の例の場合ではout_first_name
, out_last_name
, out_birth_date
になります。
それからexecute
メソッドは取得したデータを返すためにActorインスタンスを生成します。繰り返しますが、重要な点は、ストアドプロシージャに宣言したout
パラメータ名を使います。なお、結果mapに格納されるout
パラメータ名とデータベースのout
パラメータ名は一致します。ただし、データベースによっては一致しない場合があります。コードの移植可能性を高めるには、大文字小文字を区別しないルックアップか、SpringにLinkedCaseInsensitiveMap
を使うよう指定します。後者の場合、JdbcTemplate
を生成してsetResultsMapCaseInsensitive
プロパティをtrue
に設定します。それから、そのJdbcTemplate
インスタンスをSimpleJdbcCall
のコンストラクタに渡します。設定例は以下の通りです。
public class JdbcActorDao implements ActorDao { private SimpleJdbcCall procReadActor; public void setDataSource(DataSource dataSource) { JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); jdbcTemplate.setResultsMapCaseInsensitive(true); this.procReadActor = new SimpleJdbcCall(jdbcTemplate) .withProcedureName("read_actor"); } // ... additional methods }
上記の方法により、out
パラメータ名に関する衝突を避けられます。
18.5.6 Explicitly declaring parameters to use for a SimpleJdbcCall
メタデータに基づくパラメータの推定について見てきましたが、必要であれば明示的に宣言することも可能です。入力として複数のSqlParameter
変数を受け取るdeclareParameters
メソッドでSimpleJdbcCall
を生成および設定することで行います。`SqlParameter
を定義する方法の詳細については次のセクションで解説します。
Springがサポートしないデータベースを使用する場合に明示的な宣言が必要となります。今のところ、次のデータベースでストアドプロシージャ呼び出しのメタデータルックアップをサポートしています。Apache Derby, DB2, MySQL, Microsoft SQL Server, Oracle, and Sybase. また、MySQL, Microsoft SQL Server, and Oracleで、ストアド関数のメタデータルックアップをサポートしています。
明示的なパラメータ宣言を行う数は、すべて・複数・単一、から選択可能です。明示的なパラメータ宣言を行わない場合であっても、パラメータのメタデータは使用可能です。パラメータ候補のメタデータルックアップ処理をすべてバイパスし、パラメータ宣言のみを使うのであれば、宣言にwithoutProcedureColumnMetaDataAccess
メソッドの呼び出しを含めます。Suppose that you have two or more different call signatures declared for a database function. INパラメータ名のリストを指定にはuseInParameterNames
を使います。
以下はプロシージャ呼び出しの例です。
public class JdbcActorDao implements ActorDao { private SimpleJdbcCall procReadActor; public void setDataSource(DataSource dataSource) { JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); jdbcTemplate.setResultsMapCaseInsensitive(true); this.procReadActor = new SimpleJdbcCall(jdbcTemplate) .withProcedureName("read_actor") .withoutProcedureColumnMetaDataAccess() .useInParameterNames("in_id") .declareParameters( new SqlParameter("in_id", Types.NUMERIC), new SqlOutParameter("out_first_name", Types.VARCHAR), new SqlOutParameter("out_last_name", Types.VARCHAR), new SqlOutParameter("out_birth_date", Types.DATE) ); } // ... additional methods }
実行結果は以前の例のものと同じになります。上記の例はメタデータではなく明示的にすべてを指定します。
18.5.7 How to define SqlParameters
Section 18.6, “Modeling JDBC operations as Java objects”で解説するRDBMSオペレーションクラス(RDBMS operations classes)とSimpleJdbcクラスでパラメータを定義するには、SqlParameter
かそのサブクラスを使います。基本的にはコンストラクタでパラメータ名とSQL型を指定します。SQL型はjava.sql.Types
を使用して指定します。以前の例もあったように以下のように宣言します。
new SqlParameter("in_id", Types.NUMERIC), new SqlOutParameter("out_first_name", Types.VARCHAR),
最初の行はSqlParameter
でINパラメータを宣言しています。INパラメータはストアドプロシージャおよびSqlQuery
と以降のセクションで解説するSqlQuery
のサブクラスを使用するクエリで使用可能です。
その次の行はSqlOutParameter
でストアドプロシージャコールで使うためのout
パラメータを宣言しています。またInOut
パラメータにSqlInOutParameter
があり、これのパラメータにはプロシージャに渡すIN
を渡すと、と戻り値が得られます。
SqlParameter
もしくはSqlInOutParameter
として宣言するパラメータは入力値を渡すためにだけ使います。StoredProcedure
とは異なり、こちらは後方互換性を理由に入力値をSqlOutParameter
で宣言するパラメータに渡すことが可能です*3。
INパラメータでは、名前とSQL型に加えて、数値データのスケールやカスタムデータベース型の型名を指定可能です。out
パラメータでは、REF
カーソルから返される行のマッピング処理用にRowMapper
を指定可能です。別の方法として、戻り値のカスタマイズ処理を定義するSqlReturnType
を指定することも可能です。
18.5.8 Calling a stored function using SimpleJdbcCall
ストアトプロシージャ呼び出しと同様な方法でストアド関数を呼び出せます。この場合、プロシージャ名ではなく関数名を指定します。関数呼び出しを示すための設定としてwithFunctionName
メソッドを使用すると, and the corresponding string for a function call is generated. 特殊な実行呼び出しであるexecuteFunction
が関数呼び出しに使われ、指定型のオブジェクトとして関数の戻り値が返されます。つまり、結果mapから戻り値を取得してはいけません*4。似たようなメソッドにexecuteObject
という簡易版があり、これはストアドプロシージャにも利用可能で、``outパラメータ一つのみ持ちます。以下の例はアクターのフルネームを返す
get_actor_name```というストアド関数を使用しています。以下の関数はMySQLのものです。
CREATE FUNCTION get_actor_name (in_id INTEGER) RETURNS VARCHAR(200) READS SQL DATA BEGIN DECLARE out_name VARCHAR(200); SELECT concat(first_name, ' ', last_name) INTO out_name FROM t_actor where id = in_id; RETURN out_name; END;
この関数を呼ぶには初期化メソッドでSimpleJdbcCall
を再度生成します。
public class JdbcActorDao implements ActorDao { private JdbcTemplate jdbcTemplate; private SimpleJdbcCall funcGetActorName; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); jdbcTemplate.setResultsMapCaseInsensitive(true); this.funcGetActorName = new SimpleJdbcCall(jdbcTemplate) .withFunctionName("get_actor_name"); } public String getActorName(Long id) { SqlParameterSource in = new MapSqlParameterSource() .addValue("in_id", id); String name = funcGetActorName.executeFunction(String.class, in); return name; } // ... additional methods }
関数呼び出しから戻り値を含むString
が返されます。
18.5.9 Returning ResultSet/REF Cursor from a SimpleJdbcCall
結果セットを返すストアドプロシージャもしくは関数の呼び出しには若干の注意が必要です。DBによってはJDBCの結果処理中に結果セットを返すものや、明示的に特定の型をout
パラメータに登録する必要があります。どちらの場合も、result setのループ処理用と戻される行の処理に追加のコードが必要です。SimpleJdbcCall
ではreturningResultSet
メソッドを使用して指定パラメータに対して使うRowMapper
の実装を宣言します。この場合、結果セットは結果処理中に返され、名前の定義は無い(there are no names defined)ので、返される結果はRowMapper
実装を宣言する順序に一致します。The name specified is still used to store the processed list of results in the results map that is returned from the execute statement.
次の例はINパラメータを取らないストアドプロシージャでt_actorテーブルから全行を取得します。以下のプロシージャはMySQLのものです。
CREATE PROCEDURE read_all_actors() BEGIN SELECT a.id, a.first_name, a.last_name, a.birth_date FROM t_actor a; END;
このプロシージャを呼ぶにはRowMapper
を宣言します。ここではJavaBeansでマッピングするクラスが必要なので、newInstance
にマッピングのクラスを渡して生成するBeanPropertyRowMapper
を使用可能です。
public class JdbcActorDao implements ActorDao { private SimpleJdbcCall procReadAllActors; public void setDataSource(DataSource dataSource) { JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); jdbcTemplate.setResultsMapCaseInsensitive(true); this.procReadAllActors = new SimpleJdbcCall(jdbcTemplate) .withProcedureName("read_all_actors") .returningResultSet("actors", BeanPropertyRowMapper.newInstance(Actor.class)); } public List getActorsList() { Map m = procReadAllActors.execute(new HashMap<String, Object>(0)); return (List) m.get("actors"); } // ... additional methods }
上記の呼び出しは引数を取らないので、空のMapを渡してexecuteを呼び出しています。Actorsのリストを結果mapから取得して、呼び出し元に返しています。
18.6 Modeling JDBC operations as Java objects
org.springframework.jdbc.object
パッケージのクラスを使うことでオブジェクト指向の作法に沿ってデータベースへのアクセスが可能になります。例として、リレーショナルのカラムデータをビジネスオブジェクトのプロパティにマップすることで、クエリの実行結果をビジネスオブジェクトのリストとして取得が可能です。また、ストアドプロシージャの実行や、update, delete, insertステートメントの実行も可能です。
以下で説明する各種のRDBMS操作クラスはJdbcTemplate
へと置換可能である(StoredProcedure
は例外)と、たいていのSpring開発者は考えています。JdbcTemplate
のメソッドを直接呼び出すようなDAOメソッドを書くことは簡単です(本格的なクラスでクラスをカプセル化することに比べると)。
ただし、RDBMS操作クラスを使用してmeasurable value*5を取得している場合、継続してそのクラスを使い続けてください。
18.6.1 SqlQuery
SqlQuery
はSQLクエリをカプセル化する再利用可能でスレッドセーフなクラスです。サブクラスはRowMapper
を返すnewRowMapper(..)
メソッドを実装する必要があります。RowMapper
はクエリ実行中に生成されるResultSet
のループ処理時に取得する行ごとに一オブジェクトを生成します。SqlQuery
クラスを直接することは稀であり、サブクラスのMappingSqlQuery
が行をJavaクラスへマッピングする便利な実装を提供しているためです。SqlQuery
を拡張するその他の実装としてはMappingSqlQueryWithParameters
とUpdatableSqlQuery
があります。
18.6.2 MappingSqlQuery
ResultSet
の各行を指定型のオブジェクトへ変換するためのmapRow(..)
抽象メソッドを実装するサブクラスを用意することで、MappingSqlQuery
は再利用可能なクエリとなります。以下の例はデータをt_actor
リレーションからActor
クラスのインスタンスにマップするカスタムクエリです。
public class ActorMappingQuery extends MappingSqlQuery<Actor> { public ActorMappingQuery(DataSource ds) { super(ds, "select id, first_name, last_name from t_actor where id = ?"); super.declareParameter(new SqlParameter("id", Types.INTEGER)); compile(); } @Override protected Actor mapRow(ResultSet rs, int rowNumber) throws SQLException { Actor actor = new Actor(); actor.setId(rs.getLong("id")); actor.setFirstName(rs.getString("first_name")); actor.setLastName(rs.getString("last_name")); return actor; } }
MappingSqlQuery
の拡張クラスはActor
型のパラメータを付与しています。カスタムクエリのコンストラクタは単一のパラメータDataSource
を取ります。このコンストラクタではスーパークラスのコンストラクタをDataSource
とSQLで呼び出しており、そのSQLは行を取得するために実行されるものです。このSQLはPreparedStatement
を使用するので、実行時にパラメータを渡すためのプレースホルダーを含めることが可能です。declareParameter
メソッドにSqlParameter
を渡すことでパラメータを宣言する必要があります。SqlParameter
は名前とjava.sql.Types
に定義されているJDBC型を取ります。すべてのパラメータ定義後に、compile()
を呼び出すことでステートメントのpreparedと実行が可能になります。このクラスはコンパイル後にスレッドセーフとなるので、DAO初期化時にインスタンスを生成していれば、インスタンス変数としてキープすれば再利用となります。
private ActorMappingQuery actorMappingQuery; @Autowired public void setDataSource(DataSource dataSource) { this.actorMappingQuery = new ActorMappingQuery(dataSource); } public Customer getCustomer(Long id) { return actorMappingQuery.findObject(id); }
上記例のメソッドは唯一のパラメータとして渡されるidでcustomerを取得します。この場合はオブジェクトを一つだけ返して欲しいので、パラメータにidを指定してfindObject
メソッドを呼びます。オブジェクトのリストを返すクエリを実行する場合、可変長引数にパラメータの配列を渡すexecuteメソッドのいずれかを使用します。
public List<Actor> searchForActors(int age, String namePattern) { List<Actor> actors = actorSearchMappingQuery.execute(age, namePattern); return actors; }
18.6.3 SqlUpdate
SqlUpdate
はSQLのudateをカプセル化するクラスです。query同様にupdateオブジェクトも再利用可能で、RdbmsOperation
クラス群同様に、updateはパラメータを指定可能で、SQLを使用して定義出来ます。このクラスはqueryオブジェクトのexecute(..)
と似たような構造で複数のupdate(..)
メソッドを持ちます。SQLUpdate
クラスは具象クラスです。サブクラス化は可能で、例えば、以下のコード例のようにカスタムのupdateメソッドを追加可能です。このメソッドはただ単にexecute
を呼ぶだけです。ただし、SqlUpdate
はSQLの設定とパラメータ宣言によりパラメータ化が簡単なので、SqlUpdate
のサブクラス化はしない方が良いです*6。
import java.sql.Types; import javax.sql.DataSource; import org.springframework.jdbc.core.SqlParameter; import org.springframework.jdbc.object.SqlUpdate; public class UpdateCreditRating extends SqlUpdate { public UpdateCreditRating(DataSource ds) { setDataSource(ds); setSql("update customer set credit_rating = ? where id = ?"); declareParameter(new SqlParameter("creditRating", Types.NUMERIC)); declareParameter(new SqlParameter("id", Types.NUMERIC)); compile(); } /** * @param id for the Customer to be updated * @param rating the new value for credit rating * @return number of rows updated */ public int execute(int id, int rating) { return update(rating, id); } }
18.6.4 StoredProcedure
StoredProcedure
クラスはRDBMSのストアドプロシージャのオブジェクト抽象化用のスーパークラスです。このクラスはabstract
で、各種のexecute(..)
メソッドのアクセス制御はprotected
なので、サブクラス以外から使用することは出来ません。
sql
プロパティがRDBMSのストアドプロシージャ名になります。
StoredProcedure
クラス用にパラメータを定義するには、SqlParameter
かそのサブクラスを使います。以下のコード例のようにコンストラクタでSQL型とパラメータ名の指定が必須です。SQL型はjava.sql.Types
に定義されています。
new SqlParameter("in_id", Types.NUMERIC), new SqlOutParameter("out_first_name", Types.VARCHAR),
SqlParameter
宣言の最初の列はINパラメータです。INパラメータはストアドプロシージャ呼び出しとSqlQuery
および以降のセクションで解説するSqlQuery
のサブクラスを使用するクエリの両方で使用可能です。
二行目のSqlOutParameter
はストアドプロシージャ呼び出しで使うout
パラメータを宣言します。I
nOut
パラメータにはSqlInOutParameter
も使用可能で、前者はプロシージャにin
を、後者は戻り値のパラメータになります。
i
n
パラメータには、名前とSQL型に加えて、数値データの精度やカスタムデータベース型用の型名を指定可能です。out
パラメータにはREFカーソルから返される行マッピングを処理するためのRowMapper
を指定可能です。また、戻り値のカスタマイズ処理を定義するSqlReturnType
も指定可能です。
以下の例は簡単なDAOで関数呼び出しにStoredProcedure
を使い、Oracleデータベースのsysdate()
を呼び出しています。ストアドプロシージャの機能を使うためにはStoredProcedure
を拡張するクラスを用意します。この例では、StoredProcedure
は内部クラスですが、StoredProcedure
を他でも使いたいのであればトップレベルクラスとして宣言します。この例は入力パラメータを持たず、出力パラメータをSqlOutParameter
クラスを使用してデータ型を宣言しています。execute()
メソッドはプロシージャを実行して結果のMap
から戻り値の日付を展開します。結果のMap
は出力パラメータ宣言に対応するエントリを持ち、この例では1エントリだけで、キーはパラメータ名となります。
import java.sql.Types; import java.util.Date; import java.util.HashMap; import java.util.Map; import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.SqlOutParameter; import org.springframework.jdbc.object.StoredProcedure; public class StoredProcedureDao { private GetSysdateProcedure getSysdate; @Autowired public void init(DataSource dataSource) { this.getSysdate = new GetSysdateProcedure(dataSource); } public Date getSysdate() { return getSysdate.execute(); } private class GetSysdateProcedure extends StoredProcedure { private static final String SQL = "sysdate"; public GetSysdateProcedure(DataSource dataSource) { setDataSource(dataSource); setFunction(true); setSql(SQL); declareParameter(new SqlOutParameter("date", Types.DATE)); compile(); } public Date execute() { // the 'sysdate' sproc has no input parameters, so an empty Map is supplied... Map<String, Object> results = execute(new HashMap<String, Object>()); Date sysdate = (Date) results.get("date"); return sysdate; } } }
以下のStoredProcedure
例は二つの出力パラメータを持ちます(この例ではOracleのREFカーソル)。
import org.springframework.jdbc.object.StoredProcedure; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; public class TitlesAndGenresStoredProcedure extends StoredProcedure { private static final String SPROC_NAME = "AllTitlesAndGenres"; public TitlesAndGenresStoredProcedure(DataSource dataSource) { super(dataSource, SPROC_NAME); declareParameter(new SqlOutParameter("titles", OracleTypes.CURSOR, new TitleMapper())); declareParameter(new SqlOutParameter("genres", OracleTypes.CURSOR, new GenreMapper())); compile(); } public Map<String, Object> execute() { // again, this sproc has no input parameters, so an empty Map is supplied return super.execute(new HashMap<String, Object>()); } }
TitlesAndGenresStoredProcedure
コンストラクタで使っているdeclareParameter(..)
のオーバーロードメソッドにRowMapper
実装のインスタンスを渡している点に注意してください。このやり方は既存機能を利用する便利で強力な方法です。二つのRowMapper
実装のコードは以下にあります。
TitleMapper
クラスはResultSet
の各行をTitle
ドメインオブジェクトにマッピングします。
import org.springframework.jdbc.core.RowMapper; import java.sql.ResultSet; import java.sql.SQLException; import com.foo.domain.Title; public final class TitleMapper implements RowMapper<Title> { public Title mapRow(ResultSet rs, int rowNum) throws SQLException { Title title = new Title(); title.setId(rs.getLong("id")); title.setName(rs.getString("name")); return title; } }
GenreMapper
クラスはResultSet
の各行をGenre
ドメインオブジェクトにマッピングします。
import org.springframework.jdbc.core.RowMapper; import java.sql.ResultSet; import java.sql.SQLException; import com.foo.domain.Genre; public final class GenreMapper implements RowMapper<Genre> { public Genre mapRow(ResultSet rs, int rowNum) throws SQLException { return new Genre(rs.getString("name")); } }
一つ以上のパラメータを持つストアドプロシージャにパラメータを渡すには、強い型付きのexecute(..)
メソッドを自前で用意してスーパークラスの型付けが無いexecute(Map parameters)
にデリゲートします。
import oracle.jdbc.OracleTypes; import org.springframework.jdbc.core.SqlOutParameter; import org.springframework.jdbc.core.SqlParameter; import org.springframework.jdbc.object.StoredProcedure; import javax.sql.DataSource; import java.sql.Types; import java.util.Date; import java.util.HashMap; import java.util.Map; public class TitlesAfterDateStoredProcedure extends StoredProcedure { private static final String SPROC_NAME = "TitlesAfterDate"; private static final String CUTOFF_DATE_PARAM = "cutoffDate"; public TitlesAfterDateStoredProcedure(DataSource dataSource) { super(dataSource, SPROC_NAME); declareParameter(new SqlParameter(CUTOFF_DATE_PARAM, Types.DATE); declareParameter(new SqlOutParameter("titles", OracleTypes.CURSOR, new TitleMapper())); compile(); } public Map<String, Object> execute(Date cutoffDate) { Map<String, Object> inputs = new HashMap<String, Object>(); inputs.put(CUTOFF_DATE_PARAM, cutoffDate); return super.execute(inputs); } }
18.7 Common problems with parameter and data value handling
18.9 Initializing a DataSource
org.springframework.jdbc.datasource.init
パッケージは既存のDataSource
を初期化するサポートを提供します。組み込みデータベースサポートはアプリケーション向けのDataSource
の初期化と作成のオプションを提供しますが、場合によってはサーバーなどで実行するインスタンスを初期化する必要があります。
18.9.1 Initializing a database using Spring XML
データベースを初期化してDataSource
ビーンの参照を取得したい場合、spring-jdbc
名前空間のinitialize-database
タグを使います。
<jdbc:initialize-database data-source="dataSource"> <jdbc:script location="classpath:com/foo/sql/db-schema.sql"/> <jdbc:script location="classpath:com/foo/sql/db-test-data.sql"/> </jdbc:initialize-database>
上記の例はデータベースに対して二つのスクリプトを実行しており、前者はスキーマの生成、後者はテストデータセットの作成処理です。スクリプトの場所はSpringリソースで使われるantスタイルのワイルドカードパターン(classpath*:/com/foo/**/sql/*-data.sql
)を使用可能です。パターンを使う場合、スクリプトの実行順序はURLやファイル名の辞書順となります。
データベース初期化のデフォルトの振る舞いは指定したスクリプトを無条件に実行します。この動作は常にユーザの期待通りというわけではなく、たとえば、テストデータ投入済みのデータベースに対してスクリプトを実行する場合などです。以下のような先ずテーブルを作成してからデータ投入するパターンに従うことで、不幸にもデータが削除されている状況の可能性は減らせます。テーブルが既に存在する場合には最初のステップが失敗します。
既存データの作成と削除をより細かく制御するには、XML名前空間がいくつかのオプションを提供します。一つ目は初期化のオンオフをスイッチするフラグです。この値は環境変数を設定可能です(例:システムプロパティもしくはenvironment beanからboolean値を取得する)。
<jdbc:initialize-database data-source="dataSource" enabled="#{systemProperties.INITIALIZE_DATABASE}"> <jdbc:script location="..."/> </jdbc:initialize-database>
二つ目のオプションは既存データに対して発生するエラーを許容するように制御します。スクリプト実行時の特定のSQLエラーを無視するようイニシャライザを制御可能です。
<jdbc:initialize-database data-source="dataSource" ignore-failures="DROPS"> <jdbc:script location="..."/> </jdbc:initialize-database>
この例では、空のデータベースに対してスクリプトが実行される場合があると想定しており、その場合にはスクリプト中のDROP
ステートメントは失敗します。よって、DROP
ステートメントの失敗は無視しますが、その他のエラーは例外を吐きます。SQLダイアレクトがDROP ... IF EXISTS
をサポートしないが、作成前に無条件にテストデータを全削除したい場合に便利です。その場合、最初のスクリプトでDROP
ステートメントを実行し、次にCREATE
ステートメントを実行します。
ignore-failures
オプションに設定可能な値は、NONE
(デフォルト)、DROPS
(dropのエラーを無視)、ALL
(すべてのエラーを無視)
XML名前空間で指定可能なものよりも細かく制御したい場合、DataSourceInitializer
を直接使用可能で、アプリケーションのコンポーネントとして定義できます。
Initialization of other components that depend on the database
*1:potentially allowing for one thread connection per data source.が原文。これ1スレッド1コネクションという解釈で合ってるよね?
*2: This returns a java.lang.Number object with which you can create an instance of the numerical type that is used in our domain class.が原文。うまい日本語に出来なかった…
*3:which for backwards compatibility reasons allows input values to be provided for parameters declared as SqlOutParameter.が原文。ちょっと訳に自信が無い
*4:which means you do not have to retrieve the return value from the results map. が原文。訳に自信無い
*5:これ何?
*6:However, you don’t have to subclass the SqlUpdate class since it can easily be parameterized by setting SQL and declaring parameters.良く分からん