データベース接続の制御

DataSource を使用する

Spring は、DataSource を介してデータベースへの接続を取得します。DataSource は JDBC 仕様の一部であり、一般化された接続ファクトリです。コンテナーまたはフレームワークは、接続プールとトランザクション管理の課題をアプリケーションコードから隠すことができます。開発者として、データベースへの接続方法に関する詳細を知る必要はありません。それは、データソースを設定する管理者の責任です。ほとんどの場合、コードを開発およびテストするときに両方のロールを果たしますが、必ずしも本番データソースがどのように構成されているかを知る必要はありません。

Spring の JDBC レイヤーを使用する場合、JNDI からデータソースを取得するか、サードパーティが提供する接続プール実装を使用して独自のデータソースを構成することができます。従来の選択肢は、Bean スタイルの DataSource クラスを備えた Apache Commons DBCP と C3P0 です。最新の JDBC 接続プールの場合は、代わりにビルダースタイルの API を備えた HikariCP を検討してください。

DriverManagerDataSource および SimpleDriverDataSource クラス(Spring ディストリビューションに含まれる)は、テスト目的でのみ使用してください! これらのバリアントはプーリングを提供せず、接続に対して複数のリクエストが行われたときにパフォーマンスが低下します。

次のセクションでは、Spring の DriverManagerDataSource 実装を使用します。他のいくつかの DataSource バリアントについては後で説明します。

DriverManagerDataSource を構成するには:

  1. 通常は JDBC 接続を取得するため、DriverManagerDataSource との接続を取得します。

  2. DriverManager がドライバークラスをロードできるように、JDBC ドライバーの完全修飾クラス名を指定します。

  3. JDBC ドライバー間で異なる URL を提供します。(正しい値については、ドライバーのドキュメントを参照してください。)

  4. データベースに接続するためのユーザー名とパスワードを入力します。

次の例は、DriverManagerDataSource を構成する方法を示しています。

  • Java

  • Kotlin

  • XML

@Bean
DriverManagerDataSource dataSource() {
	DriverManagerDataSource dataSource = new DriverManagerDataSource();
	dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
	dataSource.setUrl("jdbc:hsqldb:hsql://localhost:");
	dataSource.setUsername("sa");
	dataSource.setPassword("");
	return dataSource;
}
@Bean
fun dataSource() = DriverManagerDataSource().apply {
	setDriverClassName("org.hsqldb.jdbcDriver")
	url = "jdbc:hsqldb:hsql://localhost:"
	username = "sa"
	password = ""
}
<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"/>

次の 2 つの例は、DBCP および C3P0 の基本的な接続と構成を示しています。プーリング機能の制御に役立つその他のオプションについては、それぞれの接続プーリング実装の製品ドキュメントを参照してください。

次の例は、DBCP 構成を示しています。

  • Java

  • Kotlin

  • XML

@Bean(destroyMethod = "close")
BasicDataSource dataSource() {
	BasicDataSource dataSource = new BasicDataSource();
	dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
	dataSource.setUrl("jdbc:hsqldb:hsql://localhost:");
	dataSource.setUsername("sa");
	dataSource.setPassword("");
	return dataSource;
}
@Bean(destroyMethod = "close")
fun dataSource() = BasicDataSource().apply {
	driverClassName = "org.hsqldb.jdbcDriver"
	url = "jdbc:hsqldb:hsql://localhost:"
	username = "sa"
	password = ""
}
<bean id="dataSource" class="org.apache.commons.dbcp2.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 構成を示しています。

  • Java

  • Kotlin

  • XML

@Bean(destroyMethod = "close")
ComboPooledDataSource dataSource() throws PropertyVetoException {
	ComboPooledDataSource dataSource = new ComboPooledDataSource();
	dataSource.setDriverClass("org.hsqldb.jdbcDriver");
	dataSource.setJdbcUrl("jdbc:hsqldb:hsql://localhost:");
	dataSource.setUser("sa");
	dataSource.setPassword("");
	return dataSource;
}
@Bean(destroyMethod = "close")
fun dataSource() = ComboPooledDataSource().apply {
	driverClass = "org.hsqldb.jdbcDriver"
	jdbcUrl = "jdbc:hsqldb:hsql://localhost:"
	user = "sa"
	password = ""
}
<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"/>

DataSourceUtils を使用する

DataSourceUtils クラスは、JNDI から接続を取得し、必要に応じて接続を閉じるための static メソッドを提供する便利で強力なヘルパークラスです。スレッドバインドされた JDBC Connection と DataSourceTransactionManager だけでなく、JtaTransactionManager および JpaTransactionManager もサポートします。

JdbcTemplate は DataSourceUtils 接続アクセスを暗黙的に示し、すべての JDBC 操作の背後でそれを使用し、進行中のトランザクションに暗黙的に参加することに注意してください。

SmartDataSource の実装

SmartDataSource インターフェースは、リレーショナルデータベースへの接続を提供できるクラスによって実装する必要があります。DataSource インターフェースを継承し、それを使用するクラスが、特定の操作後に接続を閉じる必要があるかどうかを照会できるようにします。この使用方法は、接続を再利用する必要があることがわかっている場合に効率的です。

AbstractDataSource の拡張

AbstractDataSource は、Spring の DataSource 実装の abstract 基本クラスです。すべての DataSource 実装に共通のコードを実装します。独自の DataSource 実装を作成する場合は、AbstractDataSource クラスを継承する必要があります。

SingleConnectionDataSource を使用する

SingleConnectionDataSource クラスは、使用後に閉じられない単一の Connection をラップする SmartDataSource インターフェースの実装です。これはマルチスレッド対応ではありません。

プールされた接続を想定してクライアントコードが close を呼び出す場合(永続性ツールを使用する場合など)、suppressClose プロパティを true に設定する必要があります。この設定は、物理接続をラップする緊密なプロキシを返します。これをネイティブ Oracle Connection または同様のオブジェクトにキャストできないことに注意してください。

SingleConnectionDataSource は主にテストクラスです。これにより、通常、単純な JNDI 環境と組み合わせて、アプリケーションサーバーの外部でコードを簡単にテストできます。DriverManagerDataSource とは対照的に、常に同じ接続を再利用し、物理的な接続の過度の作成を回避します。

DriverManagerDataSource を使用する

DriverManagerDataSource クラスは、Bean プロパティを介してプレーン JDBC ドライバーを構成し、毎回新しい Connection を返す標準 DataSource インターフェースの実装です。

この実装は、Spring IoC コンテナー内の DataSource Bean として、または単純な JNDI 環境と組み合わせて、Jakarta EE コンテナー外のテストおよびスタンドアロン環境に役立ちます。プールを想定した Connection.close() 呼び出しは接続を閉じるため、DataSource -aware 永続性コードはすべて機能するはずです。ただし、テスト環境であっても JavaBean スタイルの接続プール(commons-dbcp など)を使用するのは非常に簡単なので、そのような接続プールを DriverManagerDataSource よりも使用することがほぼ常に望ましいです。

TransactionAwareDataSourceProxy を使用する

TransactionAwareDataSourceProxy は、ターゲット DataSource のプロキシです。プロキシは、DataSource をターゲットとしてラップし、Spring が管理するトランザクションの認識を追加します。この点で、Jakarta EE サーバーによって提供されるトランザクション JNDI DataSource に似ています。

既存のコードを呼び出して標準 JDBC DataSource インターフェース実装を渡す必要がある場合を除いて、このクラスを使用することはめったにありません。この場合、このコードを引き続き使用可能にし、同時にこのコードを Spring 管理トランザクションに参加させることができます。一般に、JdbcTemplate や DataSourceUtils など、リソース管理用のより高いレベルの抽象化を使用して、独自の新しいコードを作成することをお勧めします。

詳細については、TransactionAwareDataSourceProxy javadoc を参照してください。

DataSourceTransactionManager / JdbcTransactionManager を使用する

DataSourceTransactionManager クラスは、単一の JDBC DataSource の PlatformTransactionManager 実装です。これは、指定された DataSource から現在実行中のスレッドに JDBC Connection をバインドし、DataSource ごとに 1 つのスレッドバインド Connection を可能にする可能性があります。

Java EE の標準 DataSource.getConnection ではなく、JDBC Connection から DataSourceUtils.getConnection(DataSource) を取得するには、アプリケーションコードが必要です。チェックされた SQLExceptions の代わりに、チェックされていない org.springframework.dao 例外がスローされます。すべてのフレームワーククラス ( JdbcTemplate など) はこの戦略を暗黙的に使用します。トランザクションマネージャーで使用しない場合、検索戦略は DataSource.getConnection とまったく同じように動作するため、どのような場合でも使用できます。

DataSourceTransactionManager クラスは、セーブポイント (PROPAGATION_NESTED)、カスタム分離レベル、適切な JDBC ステートメントクエリタイムアウトとして適用されるタイムアウトをサポートします。後者をサポートするには、アプリケーションコードで JdbcTemplate を使用するか、作成されたステートメントごとに DataSourceUtils.applyTransactionTimeout(..) メソッドを呼び出す必要があります。

単一リソースの場合は、コンテナーが JTA トランザクションコーディネーターをサポートする必要がないため、JtaTransactionManager の代わりに DataSourceTransactionManager を使用できます。必要な接続ルックアップパターンを守っていれば、これらのトランザクションマネージャー間の切り替えは構成だけの問題です。JTA はセーブポイントやカスタム分離レベルをサポートしておらず、異なるタイムアウトメカニズムを備えていますが、それ以外の点では、JDBC リソースおよび JDBC コミット / ロールバック管理に関して同様の動作を公開することに注意してください。

実際のリソース接続の JTA スタイルの遅延取得のために、Spring はターゲット接続プールに対応する DataSource プロキシクラスを提供します。LazyConnectionDataSourceProxy (Javadoc) を参照してください。これは、実際のステートメント実行のない潜在的に空のトランザクション (このようなシナリオでは実際のリソースをフェッチしない) の場合や、トランザクション同期読み取り専用フラグや分離レベル (たとえば、IsolationLevelDataSourceRouter) を考慮するルーティング DataSource の前で特に役立ちます。

LazyConnectionDataSourceProxy は、読み取り専用トランザクション中に使用する読み取り専用接続プールの特別なサポートも提供し、プライマリ接続プールから JDBC 接続を取得するときに、各トランザクションの開始時と終了時に JDBC 接続の読み取り専用フラグを切り替えるオーバーヘッドを回避します ( JDBC ドライバーによってはコストがかかる場合があります)。

5.3 の時点で、Spring は、コミット / ロールバック時の例外変換機能を追加する拡張 JdbcTransactionManager バリアントを提供します ( JdbcTemplate と連携)。DataSourceTransactionManager は TransactionSystemException (JTA に類似) のみをスローしますが、JdbcTransactionManager はデータベースロックの失敗などを対応する DataAccessException サブクラスに変換します。アプリケーションコードは、TransactionSystemException だけを想定するのではなく、そのような例外に備えて準備する必要があることに注意してください。このようなシナリオでは、JdbcTransactionManager が推奨される選択肢です。

例外動作の点では、JdbcTransactionManager は JpaTransactionManager とほぼ同等であり、また R2dbcTransactionManager とも同等であり、互いの直接のコンパニオン / 置換として機能します。一方、DataSourceTransactionManager は JtaTransactionManager と同等であり、JtaTransactionManager の直接の代替として機能します。