7.1 接続プール

今日は、データベースをさらに専門的に操作する方法を学びます。ここで、スレッド プール (英語では接続プール)について説明します。

データベースへの接続には時間がかかります。特にデータベースがリモートにある場合はそうです。リクエストごとにデータベースに接続すると、アプリケーションの応答速度が信じられないほど遅くなります。消費するリソースは言うまでもありません。

このような問題の解決策として、ベースへの接続をスレッド プールと呼ばれる何らかのセットに保存することが提案されました。

新しい接続をリクエストすると、接続プールはその接続を作成し、閉じるときは閉じずに接続プールに保存します。また、接続プールからの接続を再度リクエストすると、新しい接続を作成する代わりに、古い接続の 1 つが提供されます。

実際、アプリケーションは特別なドライバーを介して動作します。このドライバーは、通常の JDBC ドライバーのラッパーであり、プールを操作するための追加機能を備えています。この状況は次のように表すことができます。

接続プール

プログラマは、アクティブな接続の数、タイムアウトなど、接続プールの設定を手動で行うことができます。

特に極端な状況では、独自の接続プール、つまり接続のリストを持つクラスを作成できます。これは、接続をリストに戻す close 関数をオーバーライドします。また、オープン接続タイマーなど、他にも多くの便利な機能があります。長期間接続がない場合、接続は切断されます。

7.2* インターフェース DataSource および ConnectionPoolDataSource

スレッド プールは通常、DataSource オブジェクトと連携して動作します。このオブジェクトは、リモート データベースを抽象化したものと考えることができます。データ ソースという名前自体は、文字通りデータ ソースと翻訳できます。それは一種の暗示です。

DataSource オブジェクトは、データベースへの物理接続を取得するために使用され、DriverManager の代替となります。JDBCドライバーを登録する必要はありません。適切なパラメータを設定するだけで接続を確立し、getConnection() メソッドを実行する

DataSource タイプのオブジェクトをローカルに (アプリケーション内で直接) 作成する場合、JDBC ドライバー プロバイダーが提供する適切なメソッドによって接続パラメーターが設定されます。異なるベンダーの DBMS に接続するためのパラメータはタイプも数も異なる可能性があるため、これらのメソッドは DataSource インターフェイスによって定義されません (たとえば、すべての DBMS でドライバまたはネットワーク プロトコルのタイプを指定する必要があるわけではありません)。

したがって、Oracle DBMS を使用する場合、対応する set メソッドと get メソッドを使用するには、DataSource インタフェースを実装する同じ名前のクラスのインスタンスである OracleDataSource タイプのオブジェクトを取得する必要があります。したがって、DataSource 型のオブジェクトを作成するこの方法では、コードの移植性が低くなり、ドライバーの特定の実装への依存度が低くなります。

以下は、DataSource 型のオブジェクトのローカル使用を示すコード例です。

import java.sql.*;
import javax.sql.*;
import oracle.jdbc.driver.*;
import oracle.jdbc.pool.*;

public class DataSource {
    public static void main(String[] args) {
    	try {
        	OracleDataSource ods = new OracleDataSource();

        	ods.setUser("root");
        	ods.setPassword("secret");
        	ods.setDriverType("thin");
        	ods.setDatabaseName("test");
        	ods.setServerName("localhost");
        	ods.setPortNumber(1521);

        	Connection connection = ods.getConnection();
        	System.out.println("Connection successful!!!");

    	} catch (SQLException se) {
        	se.printStackTrace();
    	}
    	System.out.println("Goodbye!");
	}
}

7.3* JNDI インターフェース

DataSource タイプのオブジェクトの全機能は、JNDI インターフェイスの使用と組み合わせて明らかにされます。JNDI は、あるサービスが別のサービスとの接続を確立できるようにする、大規模なサーバー アプリケーション用の特別なサービス (ディレクトリのようなもの) です。

名前およびディレクトリ サービスを使用すると、事前定義された接続パラメータを使用してシステム管理者によって以前に作成された DataSource タイプのオブジェクトを保存できます。以下は、Sun によって開発された標準プロパティ (パラメータ) 名の一部です。

プロパティ名 Javaのデータ型 目的
データベース名 データベース名
サーバーの名前 サーバーの名前
ユーザー ユーザー名(ID)
パスワード ユーザーのパスワード
ポート番号 整数 DBMSサーバーのポート番号

DataSource と JNDI インターフェイスの組み合わせは、J2EE コンポーネント テクノロジに基づく多層エンタープライズ アプリケーションの開発において重要な役割を果たします。

アプリケーション コードで DataSource インターフェイスと JNDI インターフェイスを組み合わせて使用​​する場合は、ネーミング サービスとディレクトリ サービスから DataSource タイプのオブジェクトを要求するだけで済みます。この場合、接続の詳細と、ドライバーの特定の実装に依存するプログラム コードは、名前およびディレクトリ サービスに格納されているオブジェクト内でアプリケーションから隠蔽されます。

したがって、DataSource タイプと JNDI タイプのオブジェクトの共有には、互いに独立して実行される 2 つの手順が含まれます。

  1. DataSource タイプの名前付きオブジェクトは、Context.bind()を使用してネーミングおよびディレクトリ サービスに保存する必要がありますjavax.naming
  2. を使用して、アプリケーションのネーミングおよびディレクトリ サービスから DataSource 型のオブジェクトを要求しますContext.lookup()。その後、そのメソッドを使用してDataSource.getConnection()データベースへの接続を取得できます。

次に、JNDI インタフェースと OracleDataSource 型のオブジェクトを一緒に使用する例を示します。

// Create a key JNDI object
Hashtable env = new Hashtable();
env.put (Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory");
env.put (Context.PROVIDER_URL, "file:JNDI");
Context ctx = new InitialContext(env);

// Get the DataSource object by its name
DataSource ods = (DataSource) ctx.lookup("myDatabase");

// Get the Connection from the DataSource object
Connection connection = ods.getConnection();
System.out.println("Connection successful!!!");

JNDI の作業は分析しません。これはこのコースの範囲外です。このアプローチは大規模なアプリケーションでは非常に一般的であることを知っておいていただきたいのです。今後このようなコードを目にしたとしても、迷わないでください。

それがどのように機能するかは関係ありません。JNDI サービスを使用すると、サービス ディレクトリに登録されているサービスのプロキシ オブジェクトを取得できることだけを知っておく必要があります。これは、DataSource オブジェクトを取得し、それを使用してデータベースを操作する方法です。

7.4 接続プールの例

最初の接続プールに戻りましょう。

Java プログラマーは、プログラム内で、接続プールのさまざまな実装を含むライブラリを使用することがよくあります。その中で最も人気のあるのは次の 3 つです。

  • Apache Commons DBCP
  • C3PO
  • ヒカリCP

Apache プロジェクトは、優れた接続プールを最初に作成したプロジェクトであり、Tomcat Web サーバー内でも使用されています。それを使用した例:

public class DBCPDataSource {

    private static BasicDataSource ds = new BasicDataSource();
    static {
    	ds.setUrl("jdbc:h2:mem:test");
    	ds.setUsername("user");
    	ds.setPassword("password");
    	ds.setMinIdle(5);
    	ds.setMaxIdle(10);
    	ds.setMaxOpenPreparedStatements(100);
    }

    public static Connection getConnection() throws SQLException {
    	return ds.getConnection();
    }

    private DBCPDataSource(){ }
}

2 番目に人気のあるプールはC3POです。奇妙な名前ですね、私もそう思います。C3POという名前は、スター・ウォーズに登場するc3poロボットに由来しています。また、CP は接続プールの略です。

public class C3p0DataSource {

    private static ComboPooledDataSource cpds = new ComboPooledDataSource();
    static {
    	try {
        	cpds.setDriverClass("org.h2.Driver");
        	cpds.setJdbcUrl("jdbc:h2:mem:test");
        	cpds.setUser("user");
        	cpds.setPassword("password");
    	} catch (PropertyVetoException e) {
            // handle the exception
    	}
    }

    public static Connection getConnection() throws SQLException {
    	return cpds.getConnection();
    }

    private C3p0DataSource(){}
}

そのドキュメントは公式ページにあります。

そして最後に、現代で最も人気のある接続プールは、HikariCPです。

public class HikariCPDataSource {

    private static HikariConfig config = new HikariConfig();
    private static HikariDataSource ds;

    static {
    	config.setJdbcUrl("jdbc:h2:mem:test");
    	config.setUsername("user");
    	config.setPassword("password");
    	config.addDataSourceProperty("cachePrepStmts", "true");
    	config.addDataSourceProperty("prepStmtCacheSize", "250");
    	config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
    	ds = new HikariDataSource(config);
    }

    public static Connection getConnection() throws SQLException {
    	return ds.getConnection();
    }

    private HikariCPDataSource(){}
}

こちらが彼の公式GitHubページです。

構成に関する優れた記事もあります。