JDBC コアクラスを使用して基本的な JDBC 処理とエラー処理を制御する
このセクションでは、JDBC コアクラスを使用して、エラー処理を含む基本的な JDBC 処理を制御する方法について説明します。次のトピックが含まれます。
JdbcTemplate
を使用する
JdbcTemplate
は、JDBC コアパッケージの中心的なクラスです。リソースの作成と解放を処理するため、接続を閉じるのを忘れるなどの一般的なエラーを回避できます。コア JDBC ワークフローの基本的なタスク(ステートメントの作成や実行など)を実行し、アプリケーションコードを残して SQL を提供し、結果を抽出します。JdbcTemplate
クラス:
SQL クエリを実行します
ステートメントとストアドプロシージャコールを更新する
ResultSet
インスタンスの反復と、返されたパラメーター値の抽出を実行します。JDBC 例外をキャッチし、
org.springframework.dao
パッケージで定義された汎用の、より有益な例外階層に変換します。( 一貫した例外階層を参照してください。)
コードに JdbcTemplate
を使用する場合、コールバックインターフェースを実装するだけで、明確に定義された契約が提供されます。JdbcTemplate
クラスによって提供される Connection
が与えられると、PreparedStatementCreator
コールバックインターフェースは準備されたステートメントを作成し、SQL および必要なパラメーターを提供します。同じことが、呼び出し可能ステートメントを作成する CallableStatementCreator
インターフェースにも当てはまります。RowCallbackHandler
インターフェースは、ResultSet
の各行から値を抽出します。
DataSource
参照を使用して直接インスタンス化することにより、DAO 実装内で JdbcTemplate
を使用するか、Spring IoC コンテナーで構成して Bean 参照として DAO に渡すことができます。
DataSource は、Spring IoC コンテナー内の Bean として常に構成する必要があります。最初のケースでは、Bean はサービスに直接与えられます。2 番目の場合、準備されたテンプレートに与えられます。 |
このクラスによって発行されるすべての SQL は、テンプレートインスタンスの完全修飾クラス名(通常は JdbcTemplate
ですが、JdbcTemplate
クラスのカスタムサブクラスを使用する場合は異なる場合があります)に対応するカテゴリの DEBUG
レベルで記録されます。
以下のセクションでは、JdbcTemplate
の使用例をいくつか示します。これらの例は、JdbcTemplate
によって公開されたすべての機能の完全なリストではありません。詳細については、付随する javadoc を参照してください。
クエリ (SELECT
)
次のクエリは、リレーションの行数を取得します。
Java
Kotlin
int rowCount = this.jdbcTemplate.queryForObject("select count(*) from t_actor", Integer.class);
val rowCount = jdbcTemplate.queryForObject<Int>("select count(*) from t_actor")!!
次のクエリはバインド変数を使用します。
Java
Kotlin
int countOfActorsNamedJoe = this.jdbcTemplate.queryForObject(
"select count(*) from t_actor where first_name = ?", Integer.class, "Joe");
val countOfActorsNamedJoe = jdbcTemplate.queryForObject<Int>(
"select count(*) from t_actor where first_name = ?", arrayOf("Joe"))!!
次のクエリは、String
を探します。
Java
Kotlin
String lastName = this.jdbcTemplate.queryForObject(
"select last_name from t_actor where id = ?",
String.class, 1212L);
val lastName = this.jdbcTemplate.queryForObject<String>(
"select last_name from t_actor where id = ?",
arrayOf(1212L))!!
次のクエリは、単一のドメインオブジェクトを見つけて入力します。
Java
Kotlin
Actor actor = jdbcTemplate.queryForObject(
"select first_name, last_name from t_actor where id = ?",
(resultSet, rowNum) -> {
Actor newActor = new Actor();
newActor.setFirstName(resultSet.getString("first_name"));
newActor.setLastName(resultSet.getString("last_name"));
return newActor;
},
1212L);
val actor = jdbcTemplate.queryForObject(
"select first_name, last_name from t_actor where id = ?",
arrayOf(1212L)) { rs, _ ->
Actor(rs.getString("first_name"), rs.getString("last_name"))
}
次のクエリは、ドメインオブジェクトのリストを見つけてデータを設定します。
Java
Kotlin
List<Actor> actors = this.jdbcTemplate.query(
"select first_name, last_name from t_actor",
(resultSet, rowNum) -> {
Actor actor = new Actor();
actor.setFirstName(resultSet.getString("first_name"));
actor.setLastName(resultSet.getString("last_name"));
return actor;
});
val actors = jdbcTemplate.query("select first_name, last_name from t_actor") { rs, _ ->
Actor(rs.getString("first_name"), rs.getString("last_name"))
コードの最後の 2 つのスニペットが実際に同じアプリケーションに存在する場合、2 つの RowMapper
ラムダ式に存在する重複を削除し、単一のフィールドに抽出して、必要に応じて DAO メソッドで参照できるようにします。例: 次のように上記のコードスニペットを記述する方がよい場合があります。
Java
Kotlin
private final RowMapper<Actor> actorRowMapper = (resultSet, rowNum) -> {
Actor actor = new Actor();
actor.setFirstName(resultSet.getString("first_name"));
actor.setLastName(resultSet.getString("last_name"));
return actor;
};
public List<Actor> findAllActors() {
return this.jdbcTemplate.query("select first_name, last_name from t_actor", actorRowMapper);
}
val actorMapper = RowMapper<Actor> { rs: ResultSet, rowNum: Int ->
Actor(rs.getString("first_name"), rs.getString("last_name"))
}
fun findAllActors(): List<Actor> {
return jdbcTemplate.query("select first_name, last_name from t_actor", actorMapper)
}
JdbcTemplate
で(INSERT
、UPDATE
、DELETE
)を更新
update(..)
メソッドを使用して、挿入、更新、削除の操作を実行できます。パラメーター値は通常、変数引数として、またはオブジェクト配列として提供されます。
次の例では、新しいエントリを挿入します。
Java
Kotlin
this.jdbcTemplate.update(
"insert into t_actor (first_name, last_name) values (?, ?)",
"Leonor", "Watling");
jdbcTemplate.update(
"insert into t_actor (first_name, last_name) values (?, ?)",
"Leonor", "Watling")
次の例は、既存のエントリを更新します。
Java
Kotlin
this.jdbcTemplate.update(
"update t_actor set last_name = ? where id = ?",
"Banjo", 5276L);
jdbcTemplate.update(
"update t_actor set last_name = ? where id = ?",
"Banjo", 5276L)
次の例では、エントリを削除します。
Java
Kotlin
this.jdbcTemplate.update(
"delete from t_actor where id = ?",
Long.valueOf(actorId));
jdbcTemplate.update("delete from t_actor where id = ?", actorId.toLong())
その他の JdbcTemplate
操作
execute(..)
メソッドを使用して、任意の SQL を実行できます。このメソッドは DDL ステートメントによく使用されます。コールバックインターフェース、変数配列のバインドなどをとるバリアントでかなりオーバーロードされています。次の例では、テーブルを作成します。
Java
Kotlin
this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");
jdbcTemplate.execute("create table mytable (id integer, name varchar(100))")
次の例では、ストアドプロシージャを呼び出します。
Java
Kotlin
this.jdbcTemplate.update(
"call SUPPORT.REFRESH_ACTORS_SUMMARY(?)",
Long.valueOf(unionId));
jdbcTemplate.update(
"call SUPPORT.REFRESH_ACTORS_SUMMARY(?)",
unionId.toLong())
より高度なストアドプロシージャのサポートについては、後で説明します。
JdbcTemplate
ベストプラクティス
JdbcTemplate
クラスのインスタンスは、一度設定されるとスレッドセーフです。これは、JdbcTemplate
の単一インスタンスを構成し、この共有参照を複数の DAO(またはリポジトリ)に安全に挿入できることを意味するため、重要です。JdbcTemplate
は、DataSource
への参照を維持するという点でステートフルですが、この状態は会話状態ではありません。
JdbcTemplate
クラス(および関連する NamedParameterJdbcTemplate
クラス)を使用する際の一般的な方法は、Spring 構成ファイルで DataSource
を構成し、その共有 DataSource
Bean を DAO クラスに依存性注入することです。JdbcTemplate
は、DataSource
の setter で作成されます。これは、次のような DAO につながります。
Java
Kotlin
public class JdbcCorporateEventDao implements CorporateEventDao {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}
class JdbcCorporateEventDao(dataSource: DataSource) : CorporateEventDao {
private val jdbcTemplate = JdbcTemplate(dataSource)
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}
次の例は、対応する XML 構成を示しています。
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://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>
明示的な構成の代わりに、コンポーネントスキャンと依存性注入のアノテーションサポートを使用します。この場合、クラスに @Repository
(コンポーネントスキャンの候補になります)でアノテーションを付け、DataSource
setter メソッドに @Autowired
でアノテーションを付けることができます。次の例は、その方法を示しています。
Java
Kotlin
@Repository (1)
public class JdbcCorporateEventDao implements CorporateEventDao {
private JdbcTemplate jdbcTemplate;
@Autowired (2)
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource); (3)
}
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}
1 | @Repository でクラスにアノテーションを付けます。 |
2 | DataSource setter メソッドに @Autowired でアノテーションを付けます。 |
3 | DataSource で新しい JdbcTemplate を作成します。 |
@Repository (1)
class JdbcCorporateEventDao(dataSource: DataSource) : CorporateEventDao { (2)
private val jdbcTemplate = JdbcTemplate(dataSource) (3)
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}
1 | @Repository でクラスにアノテーションを付けます。 |
2 | DataSource のコンストラクターインジェクション。 |
3 | DataSource で新しい JdbcTemplate を作成します。 |
次の例は、対応する XML 構成を示しています。
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- Scans within the base package of the application for @Component classes to configure as beans -->
<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
が必要であり、その後、複数の異なる構成の JdbcTemplate
インスタンスが必要です。
NamedParameterJdbcTemplate
を使用する
NamedParameterJdbcTemplate
クラスは、典型的なプレースホルダー('?'
)引数のみを使用した JDBC ステートメントのプログラミングとは対照的に、名前付きパラメーターを使用した JDBC ステートメントのプログラミングのサポートを追加します。NamedParameterJdbcTemplate
クラスは、JdbcTemplate
をラップし、ラップされた JdbcTemplate
に委譲して、その作業の多くを行います。このセクションでは、JdbcTemplate
自体とは異なる NamedParameterJdbcTemplate
クラスの領域、つまり、名前付きパラメーターを使用した JDBC ステートメントのプログラミングについてのみ説明します。次の例は、NamedParameterJdbcTemplate
の使用方法を示しています。
Java
Kotlin
// 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);
}
private val namedParameterJdbcTemplate = NamedParameterJdbcTemplate(dataSource)
fun countOfActorsByFirstName(firstName: String): Int {
val sql = "select count(*) from t_actor where first_name = :first_name"
val namedParameters = MapSqlParameterSource("first_name", firstName)
return namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Int::class.java)!!
}
sql
変数に割り当てられた値と、namedParameters
変数(型 MapSqlParameterSource
の)にプラグインされる対応する値での名前付きパラメーター表記の使用に注意してください。
または、Map
ベースのスタイルを使用して、名前付きパラメーターとそれに対応する値を NamedParameterJdbcTemplate
インスタンスに渡すことができます。NamedParameterJdbcOperations
によって公開され、NamedParameterJdbcTemplate
クラスによって実装される残りのメソッドは同様のパターンに従い、ここでは説明しません。
次の例は、Map
ベースのスタイルの使用を示しています。
Java
Kotlin
// 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);
}
// some JDBC-backed DAO class...
private val namedParameterJdbcTemplate = NamedParameterJdbcTemplate(dataSource)
fun countOfActorsByFirstName(firstName: String): Int {
val sql = "select count(*) from t_actor where first_name = :first_name"
val namedParameters = mapOf("first_name" to firstName)
return namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Int::class.java)!!
}
NamedParameterJdbcTemplate
(および同じ Java パッケージに存在する)に関連する優れた機能の 1 つは、SqlParameterSource
インターフェースです。前のコードスニペットの 1 つ(MapSqlParameterSource
クラス)で、このインターフェースの実装の例をすでに見ました。SqlParameterSource
は、NamedParameterJdbcTemplate
への名前付きパラメーター値のソースです。MapSqlParameterSource
クラスは、java.util.Map
の周囲のアダプターである単純な実装です。ここで、キーはパラメーター名で、値はパラメーター値です。
別の SqlParameterSource
実装は BeanPropertySqlParameterSource
クラスです。このクラスは、任意の JavaBean(つまり、JavaBean 規約 [Oracle] (英語) に準拠するクラスのインスタンス)をラップし、ラップされた JavaBean のプロパティを名前付きパラメーター値のソースとして使用します。
次の例は、典型的な JavaBean を示しています。
Java
Kotlin
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...
}
data class Actor(val id: Long, val firstName: String, val lastName: String)
次の例では、NamedParameterJdbcTemplate
を使用して、前の例に示したクラスのメンバーの数を返します。
Java
Kotlin
// 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);
}
// some JDBC-backed DAO class...
private val namedParameterJdbcTemplate = NamedParameterJdbcTemplate(dataSource)
private val namedParameterJdbcTemplate = NamedParameterJdbcTemplate(dataSource)
fun countOfActors(exampleActor: Actor): Int {
// notice how the named parameters match the properties of the above 'Actor' class
val sql = "select count(*) from t_actor where first_name = :firstName and last_name = :lastName"
val namedParameters = BeanPropertySqlParameterSource(exampleActor)
return namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Int::class.java)!!
}
NamedParameterJdbcTemplate
クラスは、典型的な JdbcTemplate
テンプレートをラップしていることに注意してください。JdbcTemplate
クラスにのみ存在する機能にアクセスするために、ラップされた JdbcTemplate
インスタンスにアクセスする必要がある場合、getJdbcOperations()
メソッドを使用して、JdbcOperations
インターフェースを介してラップされた JdbcTemplate
にアクセスできます。
アプリケーションのコンテキストで NamedParameterJdbcTemplate
クラスを使用する際のガイドラインについては、JdbcTemplate
ベストプラクティスも参照してください。
統合された JDBC クエリ / 更新操作: JdbcClient
6.1 では、NamedParameterJdbcTemplate
の名前付きパラメーターステートメントと通常の JdbcTemplate
の位置パラメーターステートメントは、流れるような対話モデルを備えた統合クライアント API を通じて利用できます。
例: 位置パラメーターを使用した場合:
private JdbcClient jdbcClient = JdbcClient.create(dataSource);
public int countOfActorsByFirstName(String firstName) {
return this.jdbcClient.sql("select count(*) from t_actor where first_name = ?")
.param(firstName)
.query(Integer.class).single();
}
例: 名前付きパラメーターの場合:
private JdbcClient jdbcClient = JdbcClient.create(dataSource);
public int countOfActorsByFirstName(String firstName) {
return this.jdbcClient.sql("select count(*) from t_actor where first_name = :firstName")
.param("firstName", firstName)
.query(Integer.class).single();
}
柔軟な結果解決を備えた RowMapper
機能も利用できます。
List<Actor> actors = this.jdbcClient.sql("select first_name, last_name from t_actor")
.query((rs, rowNum) -> new Actor(rs.getString("first_name"), rs.getString("last_name")))
.list();
カスタム RowMapper
の代わりに、マップ先のクラスを指定することもできます。例: Actor
に、レコードクラス、カスタムコンストラクター、Bean プロパティ、またはプレーンフィールドとして firstName
および lastName
プロパティがあると仮定します。
List<Actor> actors = this.jdbcClient.sql("select first_name, last_name from t_actor")
.query(Actor.class)
.list();
必要な単一オブジェクトの結果:
Actor actor = this.jdbcClient.sql("select first_name, last_name from t_actor where id = ?")
.param(1212L)
.query(Actor.class)
.single();
java.util.Optional
の結果:
Optional<Actor> actor = this.jdbcClient.sql("select first_name, last_name from t_actor where id = ?")
.param(1212L)
.query(Actor.class)
.optional();
そして更新ステートメントの場合:
this.jdbcClient.sql("insert into t_actor (first_name, last_name) values (?, ?)")
.param("Leonor").param("Watling")
.update();
または、名前付きパラメーターを含む update ステートメント:
this.jdbcClient.sql("insert into t_actor (first_name, last_name) values (:firstName, :lastName)")
.param("firstName", "Leonor").param("lastName", "Watling")
.update();
個々の名前付きパラメーターの代わりに、パラメーターソースオブジェクト (たとえば、レコードクラス、Bean プロパティを持つクラス、または上記の Actor
クラスなどの firstName
および lastName
プロパティを提供するプレーンフィールドホルダー) を指定することもできます。
this.jdbcClient.sql("insert into t_actor (first_name, last_name) values (:firstName, :lastName)")
.paramSource(new Actor("Leonor", "Watling")
.update();
パラメーターの自動 Actor
クラスマッピングと上記のクエリ結果は、直接使用することもできる暗黙的な SimplePropertySqlParameterSource
および SimplePropertyRowMapper
戦略を通じて提供されます。これらは、BeanPropertySqlParameterSource
および BeanPropertyRowMapper
/DataClassRowMapper
の一般的な代替として機能し、また JdbcTemplate
および NamedParameterJdbcTemplate
自体とも使用できます。
JdbcClient は、JDBC クエリ / 更新ステートメント用の柔軟かつ簡素化されたファサードです。バッチ挿入やストアドプロシージャ呼び出しなどの高度な機能には通常、追加のカスタマイズが必要です。JdbcClient で利用できないそのような機能については、Spring の SimpleJdbcInsert および SimpleJdbcCall クラス、または単純な直接 JdbcTemplate の使用を検討してください。 |
SQLExceptionTranslator
を使用する
SQLExceptionTranslator
は、SQLException
と Spring 独自の org.springframework.dao.DataAccessException
の間で変換できるクラスによって実装されるインターフェースであり、データアクセス戦略に関しては依存しません。実装は汎用 (たとえば、JDBC の SQLState コードを使用) にすることも、精度を高めるために独自仕様 (たとえば、Oracle エラーコードを使用) にすることもできます。この例外変換メカニズムは、SQLException
ではなく DataAccessException
を伝播する共通の JdbcTemplate
および JdbcTransactionManager
エントリポイントの背後で使用されます。
6.0 の時点で、デフォルトの例外トランスレーターは SQLExceptionSubclassTranslator で、いくつかの追加チェックと SQLStateSQLExceptionTranslator を介した SQLState イントロスペクションへのフォールバックで JDBC 4 SQLException サブクラスを検出します。通常、一般的なデータベースアクセスにはこれで十分であり、ベンダー固有の検出は必要ありません。下位互換性を確保するには、以下で説明するように、カスタムエラーコードマッピングを使用して SQLErrorCodeSQLExceptionTranslator を使用することを検討してください。 |
SQLErrorCodeSQLExceptionTranslator
は、sql-error-codes.xml
という名前のファイルがクラスパスのルートに存在する場合にデフォルトで使用される SQLExceptionTranslator
の実装です。この実装では、特定のベンダーコードを使用します。これは、SQLState
または SQLException
サブクラス変換よりも正確です。エラーコードの変換は、SQLErrorCodes
と呼ばれる JavaBean 型 クラスに保持されているコードに基づいています。このクラスは、SQLErrorCodesFactory
によって作成および設定されます。これは、(名前が示すように) sql-error-codes.xml
という名前の構成ファイルの内容に基づいて SQLErrorCodes
を作成するためのファクトリです。このファイルにはベンダーコードが入力され、DatabaseMetaData
から取得された DatabaseProductName
に基づいています。実際に使用しているデータベースのコードが使用されます。
SQLErrorCodeSQLExceptionTranslator
は、次の順序で一致ルールを適用します。
サブクラスによって実装されるカスタム変換。通常、提供された具体的な
SQLErrorCodeSQLExceptionTranslator
が使用されるため、この規則は適用されません。サブクラスの実装を実際に提供した場合にのみ適用されます。SQLErrorCodes
クラスのcustomSqlExceptionTranslator
プロパティとして提供されるSQLExceptionTranslator
インターフェースのカスタム実装。CustomSQLErrorCodesTranslation
クラスのインスタンスのリスト(SQLErrorCodes
クラスのcustomTranslations
プロパティに提供)で一致が検索されます。エラーコードの一致が適用されます。
フォールバックトランスレータを使用します。
SQLExceptionSubclassTranslator
はデフォルトのフォールバックトランスレータです。この変換が利用できない場合、次のフォールバックトランスレータはSQLStateSQLExceptionTranslator
です。
SQLErrorCodesFactory は、エラーコードとカスタム例外変換を定義するためにデフォルトで使用されます。これらはクラスパスから sql-error-codes.xml という名前のファイル内で検索され、使用中のデータベースのデータベースメタデータのデータベース名に基づいて、一致する SQLErrorCodes インスタンスが特定されます。 |
次の例に示すように、SQLErrorCodeSQLExceptionTranslator
を継承できます。
Java
Kotlin
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;
}
}
class CustomSQLErrorCodesTranslator : SQLErrorCodeSQLExceptionTranslator() {
override fun customTranslate(task: String, sql: String?, sqlEx: SQLException): DataAccessException? {
if (sqlEx.errorCode == -12345) {
return DeadlockLoserDataAccessException(task, sqlEx)
}
return null
}
}
前述の例では、特定のエラーコード (-12345
) が変換されますが、他のエラーはデフォルトのトランスレーター実装によって変換されたままになります。このカスタムトランスレーターを使用するには、メソッド setExceptionTranslator
を通じてそれを JdbcTemplate
に渡す必要があり、このトランスレーターが必要なすべてのデータアクセス処理にこの JdbcTemplate
を使用する必要があります。次の例は、このカスタムトランスレータの使用方法を示しています。
Java
Kotlin
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);
}
// create a JdbcTemplate and set data source
private val jdbcTemplate = JdbcTemplate(dataSource).apply {
// create a custom translator and set the DataSource for the default translation lookup
exceptionTranslator = CustomSQLErrorCodesTranslator().apply {
this.dataSource = dataSource
}
}
fun updateShippingCharge(orderId: Long, pct: Long) {
// 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
でエラーコードを検索するために、カスタムトランスレーターにデータソースが渡されます。
ステートメントの実行
SQL ステートメントの実行に必要なコードはごくわずかです。DataSource
と JdbcTemplate
が必要です(JdbcTemplate
で提供される便利なメソッドを含む)。次の例は、新しいテーブルを作成する最小限であるが完全に機能するクラスに含める必要があるものを示しています。
Java
Kotlin
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))");
}
}
import javax.sql.DataSource
import org.springframework.jdbc.core.JdbcTemplate
class ExecuteAStatement(dataSource: DataSource) {
private val jdbcTemplate = JdbcTemplate(dataSource)
fun doExecute() {
jdbcTemplate.execute("create table mytable (id integer, name varchar(100))")
}
}
クエリの実行
一部のクエリメソッドは、単一の値を返します。1 つの行からカウントまたは特定の値を取得するには、queryForObject(..)
を使用します。後者は、返された JDBC Type
を、引数として渡される Java クラスに変換します。型変換が無効な場合、InvalidDataAccessApiUsageException
がスローされます。次の例には、int
用と String
を照会する 2 つの照会メソッドが含まれています。
Java
Kotlin
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);
}
}
import javax.sql.DataSource
import org.springframework.jdbc.core.JdbcTemplate
class RunAQuery(dataSource: DataSource) {
private val jdbcTemplate = JdbcTemplate(dataSource)
val count: Int
get() = jdbcTemplate.queryForObject("select count(*) from mytable")!!
val name: String?
get() = jdbcTemplate.queryForObject("select name from mytable")
}
単一の結果クエリメソッドに加えて、いくつかのメソッドは、クエリが返した各行のエントリを含むリストを返します。最も一般的な方法は queryForList(..)
で、List
を返します。各要素は、列名をキーとして使用して、列ごとに 1 つのエントリを含む Map
です。前の例にメソッドを追加してすべての行のリストを取得する場合、次のようになります。
Java
Kotlin
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");
}
private val jdbcTemplate = JdbcTemplate(dataSource)
fun getList(): List<Map<String, Any>> {
return jdbcTemplate.queryForList("select * from mytable")
}
返されるリストは次のようになります。
[{name=Bob, id=1}, {name=Mary, id=2}]
データベースの更新
次の例では、特定の主キーの列を更新します。
Java
Kotlin
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);
}
}
import javax.sql.DataSource
import org.springframework.jdbc.core.JdbcTemplate
class ExecuteAnUpdate(dataSource: DataSource) {
private val jdbcTemplate = JdbcTemplate(dataSource)
fun setName(id: Int, name: String) {
jdbcTemplate.update("update mytable set name = ? where id = ?", name, id)
}
}
前の例では、SQL ステートメントに行パラメーターのプレースホルダーがあります。パラメーター値を varargs として、またはオブジェクトの配列として渡すことができます。プリミティブラッパークラスでプリミティブを明示的にラップするか、自動ボクシングを使用する必要があります。
自動生成されたキーの取得
update()
簡易メソッドは、データベースによって生成された主キーの取得をサポートしています。このサポートは、JDBC 3.0 標準の一部です。詳細については、仕様の 13.6 章を参照してください。このメソッドは最初の引数として PreparedStatementCreator
を受け取り、これが必要な挿入ステートメントの指定メソッドです。もう 1 つの引数は KeyHolder
で、更新から正常に戻ったときに生成されたキーが含まれます。適切な PreparedStatement
を作成するための標準的な単一のメソッドはありません(これにより、メソッドのシグネチャーがそのままである理由が説明されています)。次の例は Oracle で機能しますが、他のプラットフォームでは機能しない場合があります。
Java
Kotlin
final String INSERT_SQL = "insert into my_test (name) values(?)";
final String name = "Rob";
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(connection -> {
PreparedStatement ps = connection.prepareStatement(INSERT_SQL, new String[] { "id" });
ps.setString(1, name);
return ps;
}, keyHolder);
// keyHolder.getKey() now contains the generated key
val INSERT_SQL = "insert into my_test (name) values(?)"
val name = "Rob"
val keyHolder = GeneratedKeyHolder()
jdbcTemplate.update({
it.prepareStatement (INSERT_SQL, arrayOf("id")).apply { setString(1, name) }
}, keyHolder)
// keyHolder.getKey() now contains the generated key