31.10. 接続プールとデータソース

JDBC2 付属のAPIに、標準の接続プール機能が導入されました。 このアドオンAPIはJDBC2.0オプションパッケージ(または JDBC 2.0標準拡張)と呼ばれます。 以来、これらの機能はコアJDBC3 APIに含まれています。 PostgreSQL JDBC ドライバは、JDK 1.3.x とともに動作する JDBC 2.0 オプションパッケージ (JDBC 2) または JDK 1.4以降 (JDBC 3) でこれらの機能をサポートしています。 JDBC 2.0 オプションパッケージはほとんどのアプリケーションサーバに組み込まれていますが、Sun の JDBC ダウンロードサイト から個別に入手することも可能です。

31.10.1. 概要

JDBC APIには、接続プール用のクライアントインタフェースおよびサーバインタフェースが用意されています。 クライアントインタフェースは javax.sql.DataSource です。 これは通常、アプリケーションコードがプールされたデータベース接続を取得するために使用します。 サーバインタフェースは javax.sql.ConnectionPoolDataSource で、ほとんどのアプリケーションサーバはこれを使用して PostgreSQL JDBCドライバとのインタフェースをとります。

アプリケーションサーバ環境では、アプリケーションサーバ構成は通常 PostgreSQL ConnectionPoolDataSource の実装を参照し、アプリケーションコンポーネントコードは通常、アプリケーションサーバ (PostgreSQL ではなく) が提供する DataSource 実装を獲得します。

アプリケーションサーバを使用しない環境では、PostgreSQL は、アプリケーションが直接使用できる DataSource を 2 つ実装しています。 1 つの実装が接続プールを実行し、もう 1 つの実装はプールは行わずに DataSource インタフェースを介して単にデータベース接続へのアクセスを提供します。 繰り返しますが、これらの実装は、アプリケーションサーバがConnectionPoolDataSource インタフェースをサポートしていない場合を除き、アプリケーションサーバ環境で使用すべきではありません 。

31.10.2. アプリケーションサーバ: ConnectionPoolDataSource

PostgreSQL は、表31-1に示すように、JDBC 2 用と JDBC 3 用に ConnectionPoolDataSource をそれぞれ 1 つずつ実装しています。

表 31-1. ConnectionPoolDataSource の実装

JDBC実装クラス
2org.postgresql.jdbc2.optional.ConnectionPool
3org.postgresql.jdbc3.Jdbc3ConnectionPool

どちらの実装も同じ構成スキーマを使用しています。 JDBCではConnectionPoolDataSource表31-2に示すようにJavaBeanプロパティを使用して構成する必要があります。 したがってこれらのプロパティのそれぞれに get メソッドと set メソッドができます。

表 31-2. ConnectionPoolDataSource 構成プロパティ

プロパティ説明
serverNameStringPostgreSQL データベースサーバのホスト名
databaseNameStringPostgreSQLデータベース名
portNumberintPostgreSQLデータベースサーバが監視するTCPポート (デフォルトのポートを使用する場合は0)
userStringデータベース接続の作成に使用するユーザ
passwordStringデータベース接続の作成に使用するパスワード
defaultAutoCommitboolean接続が呼び出し元に渡される際に自動コミットを有効にするか無効にするかを指定。 デフォルトは自動コミットを無効にする false です。

多くのアプリケーションサーバは、プロパティ形式の構文を使用してこれらのプロパティを構成するので、プロパティをテキストのブロックとして入力することはめずらしくありません。 アプリケーションサーバが、単一の全プロパティ入力領域を提供している場合、以下のように列挙されます。

serverName=localhost
databaseName=test
user=testuser
password=testpassword

改行の代わりにセミコロンが使用される場合は以下にようになります。

serverName=localhost;databaseName=test;user=testuser;password=testpassword

31.10.3. アプリケーション: DataSource

表31-3に示すように、PostgreSQLは、JDBC2用とJDBC3用にDataSourceをそれぞれ2つずつ実装しています。 クライアントがcloseメソッドを呼び出すと、プールの実装は実際に接続を閉じるのではなく、その接続を使用可能な接続のプールに返して他のクライアントが使用できるようにします。 これにより、接続を何度もオープンしクローズすることによるオーバヘッドをなくすとともに、多数のクライアントが少数のデータベース接続を共有することが可能になります。

このプールデータソース実装は、必ずしも優れた機能を持っているとは言えません。 例えば、接続はプール自体が閉じるまでは決して閉ざされないので、プールを縮小することができません。 また、デフォルトで構成されたユーザ以外のユーザによって要求された接続はプールされません。 多くのアプリケーションサーバではより高度なプール機能を提供しており、代わりに ConnectionPoolDataSource 実装を使用しています。

表 31-3. DataSource 実装

JDBCプール実装クラス
2なしorg.postgresql.jdbc2.optional.SimpleDataSource
2ありorg.postgresql.jdbc2.optional.PoolingDataSource
3なしorg.postgresql.jdbc3.Jdbc3SimpleDataSource
3ありorg.postgresql.jdbc3.Jdbc3PoolingDataSource

すべての実装で同じ構成スキーマを使用しています。 表31-4に示すようにJDBCでは DataSourceをJavaBeanプロパティを使用して構成する必要があります。 したがってこれらのプロパティのそれぞれに getメソッドとsetメソッドがあります。

表 31-4. DataSource 構成パラメータ

プロパティ説明
serverNameStringPostgreSQL データベースサーバのホスト名
databaseNameStringPostgreSQLデータベース名
portNumberint PostgreSQL データベースサーバが監視するTCPポートです。(デフォルトのポートを使用する場合は0)
userStringデータベース接続の作成に使用するユーザ
passwordStringデータベース接続の作成に使用するパスワード

このプール実装に構成プロパティをいくつか追加する必要があります。 表31-5に示します。

表 31-5. 追加のプールDataSource構成プロパティ

プロパティ説明
dataSourceNameStringプールするDataSourceはすべて固有の名前を持つ必要があります
initialConnectionsintプールの開始時に作成されるデータベース接続数
maxConnectionsintオープンできるデータベース接続の最大数。これより多くの接続が要求されると、接続がプールに返されるまで呼び出し側がハングします。

例31-9は、プール DataSource を使用する典型的なアプリケーションコードの例を示します。

例 31-9. DataSource コード例

プールDataSourceの初期化用コードは以下のようになります。

Jdbc3PoolingDataSource source = new Jdbc3PoolingDataSource();
source.setDataSourceName("A Data Source");
source.setServerName("localhost");
source.setDatabaseName("test");
source.setUser("testuser");
source.setPassword("testpassword");
source.setMaxConnections(10);

そして、このプールから接続を使用するコードは以下のようになります。 接続を度毎に閉ざすことが重要であることに注意してください。 さもないと、プールは"漏出"した接続になり、すべてのクライアントを締め出すことになります。

Connection con = null;
try {
    con = source.getConnection();
    // 接続を使用する
} catch (SQLException e) {
    // log error
} finally {
    if (con != null) {
        try { con.close(); } catch (SQLException e) {}
    }
}

31.10.4. データソースとJNDI

ConnectionPoolDataSource および DataSource の実装はすべて、JNDI に格納することができます。. 非プール実装の場合、JNDI からオブジェクトが抽出される度に、格納済みのインスタンスと同じ設定の新しいインスタンスが作成されます。 プール実装については、同じインスタンスが使用可能であればそのインスタンスが抽出され (異なる JVMJNDI からプールを抽出するのではなく) 、使用可能でない場合は、同じ設定で新しいインスタンスが作成されます。

アプリケーションサーバ環境では、アプリケーションサーバの DataSource インスタンスは通常 PostgreSQL ConnectionPoolDataSource 実装ではなく、JNDI に格納されます。

アプリケーション環境では、アプリケーションは JNDIDataSource を格納する場合があります。 こうすることにより、アプリケーションは、DataSource への参照をこれを使用するすべてのアプリケーションコンポーネントに対して使用可能にする必要がなくなります。 この例を例31-10に示します。

例 31-10. DataSource JNDI のコード例

プールDataSourceの初期化とJNDIへの登録を行なうアプリケーションコードは以下のようになります。

Jdbc3PoolingDataSource source = new Jdbc3PoolingDataSource();
source.setDataSourceName("A Data Source");
source.setServerName("localhost");
source.setDatabaseName("test");
source.setUser("testuser");
source.setPassword("testpassword");
source.setMaxConnections(10);
new InitialContext().rebind("DataSource", source);

そして、プールからの接続を使用するコードは以下のようになります。

Connection con = null;
try {
    DataSource source = (DataSource)new InitialContext().lookup("DataSource");
    con = source.getConnection();
    // 接続の使用
} catch (SQLException e) {
    // エラーを記録する
} catch (NamingException e) {
    // JNDI内にDataSourceが見つからない
} finally {
    if (con != null) {
        try { con.close(); } catch (SQLException e) {}
    }
}