ウェブサイト検索

Javaにおけるコネクションプーリング


コネクションプーリングとは、コネクションオブジェクトのプールを意味します。コネクションプーリングは、オブジェクトプールのデザインパターンに基づいています。オブジェクトプールデザインパターンは、新しいオブジェクトを作成するコスト(時間やCPU、ネットワーク、IOなどのリソース)が高いときに使用されます。オブジェクトプールデザインパターンに従って、アプリケーションは事前にオブジェクトを作成し、それらをプールまたはコンテナに配置します。アプリケーションがそのようなオブジェクトを必要とする際には、新しいオブジェクトを作成するのではなく、プールから取得します。

接続プール戦略を使用するアプリケーションは、再利用できるDB接続オブジェクトを既に持っています。したがって、データベースとやり取りする必要があるとき、アプリケーションはプールから接続インスタンスを取得します。接続プーリングは、データベースとやり取りするアプリケーションのパフォーマンスを向上させます。

コネクションプーリング

私たちは独自のコネクションプーリングの実装を作成できます。任意のコネクションプーリングフレームワークは、3つのタスクを実行する必要があります。

  • 接続オブジェクトの作成
  • 作成したオブジェクトの使用を管理し、それらを検証します
  • オブジェクトを解除/破壊する

Javaには、すぐに利用できる素晴らしいライブラリのセットがあります。私たちはそれらを使用するためにいくつかのプロパティを設定するだけで済みます。

Javaアプリケーションにおけるコネクションプーリング

以下のライブラリを見てみましょう:

  • アパッチ コモンズ DBCP 2
  • HikariCP
  • C3P0

以下の例を一つずつ見てみましょう。デモ目的でMySQLデータベースとEclipse IDEを使用します。また、JDK 1.8を使用してMavenに基づくシンプルなJavaプロジェクトを作成します。

データベーススクリプト

create database empdb;

use empdb;

create table tblemployee(
                    empId integer AUTO_INCREMENT primary key,
                    empName varchar(64),
                    dob date,
                    designation varchar(64)
);

insert into  tblemployee(empId,empName,dob,designation) values (default,'Adam','1998-08-15','Manager');
insert into  tblemployee(empId,empName,dob,designation) values (default,'Smith','2001-01-11','Clerk');
insert into  tblemployee(empId,empName,dob,designation) values (default,'James','1996-03-13','Officer');

例のプロジェクト

新しいプロジェクトを作成するには、以下の手順に従ってください。

  1. Eclipse IDEを開いてください。

  2. ファイルメニューをクリックして、新規 -> Mavenプロジェクトを選択します

  3. 以下の画面が表示されます。「シンプルなプロジェクトを作成」オプションを選択し、「次へ」ボタンをクリックしてください。

新しいMavenプロジェクト
  1. グループID、アーティファクトID、名前、および説明を入力してください。

Mavenプロジェクト設定

完了ボタンをクリックしてください。

  1. MySQLのためにpom.xmlに次の依存関係を追加してください。
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>5.1.49</version>
</dependency>
  1. プロジェクトを右クリックし、Maven -> プロジェクトを更新 -> OKを選択します。すべての依存関係がダウンロードされます。

1) アパッチ コモンズ DBCP 2

DBCPはApache Common Projectからのものです。DBCP 2.7はJava 8を要求します。DBCP 2を使用するには、プロジェクトに以下の依存関係を追加する必要があります。

<dependency>
	<groupId>org.apache.commons</groupId>
	<artifactId>commons-dbcp2</artifactId>
	<version>2.7.0</version>
</dependency>

Apache DBCP 2.0は、2種類のDataSource(BasicDataSourceとPoolingDataSource)を提供します。

基本データソース: 名前が示す通り、それはシンプルであり、ほとんどの一般的な使用ケースに適しています。それは内部で私たちのためにプールデータソースを作成します。

接続プールを初期化するための以下の手順を見てみましょう。

  1. BasicDataSourceのインスタンスを作成します。
  2. JDBC URL、データベースのユーザー名とパスワードを指定してください
  3. アイドル接続の最小数を指定してください(常にプールに残る必要がある接続の最小数)。
  4. プール内のアイドル接続の最大数を指定してください(プール内のアイドル接続の最大数)
  5. 最大接続数を指定してください。
package com.journaldev.example;

/**
 * Java JDBC Connection pool using Apache commons DBCP2 example program
 * 
 * @author pankaj
 */

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import org.apache.commons.dbcp2.BasicDataSource;

public class DBCP2Demo {

	private static BasicDataSource dataSource = null;

	static {
		dataSource = new BasicDataSource();
		dataSource.setUrl("jdbc:mysql://localhost:3306/empdb?useSSL=false");
		dataSource.setUsername("root");
		dataSource.setPassword("root");

		dataSource.setMinIdle(5);
		dataSource.setMaxIdle(10);
		dataSource.setMaxTotal(25);

	}

public static void main(String[] args) throws SQLException {
		Connection connection = null;
		Statement statement = null;
		ResultSet resultSet = null;
		try {
			connection = dataSource.getConnection();
			statement = connection.createStatement();
			resultSet = statement.executeQuery("select * from tblemployee");
			while (resultSet.next()) {
				System.out.println("empId:" + resultSet.getInt("empId"));
				System.out.println("empName:" + resultSet.getString("empName"));
				System.out.println("dob:" + resultSet.getDate("dob"));
				System.out.println("designation:" + resultSet.getString("designation"));
			}
		} finally {

			resultSet.close();
			statement.close();
			connection.close();
		}
	}

}

出力:

empId:1
empName:Adam
dob:1998-08-15
designation:Manager
empId:2
empName:Smith
dob:2001-01-11
designation:Clerk
empId:3
empName:James
dob:1996-03-13
designation:Officer

PoolingDataSource: より柔軟性を提供します。DataSourceを作成するコードだけを変更すれば、残りのコードはそのままです。

接続プールを初期化するための以下の手順を見てみましょう:

  1. JDBC URLを使用してConnectionFactoryのインスタンスを作成します。
  2. ステップ1で作成したConnectionFactoryのインスタンスを使用してPoolableConnectionFactoryのインスタンスを作成します。
  3. GenericObjectPoolConfigのインスタンスを作成し、最大アイドル数、最小アイドル数、および最大接続プロパティを設定します。
  4. 今、ステップ2とステップ3で作成したインスタンスを使用してObjectPoolを初期化します
  5. 今、プールをPoolableConnectionFactoryのインスタンスとして設定します。
  6. 最後に、DataSourceのインスタンスを初期化します。
private static DataSource dataSource = null;

	static {

		Properties properties = new Properties();
		properties.setProperty("user", "root");
		properties.setProperty("password", "root");

		ConnectionFactory connectionFactory = new DriverManagerConnectionFactory("jdbc:mysql://localhost:3306/empdb",
				properties);

		PoolableConnectionFactory poolableConnectionFactory = new PoolableConnectionFactory(connectionFactory, null);

		GenericObjectPoolConfig<PoolableConnection> config = new GenericObjectPoolConfig<>();
		config.setMaxTotal(25);
		config.setMaxIdle(10);
		config.setMinIdle(5);

		ObjectPool<PoolableConnection> connectionPool = new GenericObjectPool<>(poolableConnectionFactory, config);
		poolableConnectionFactory.setPool(connectionPool);

		dataSource = new PoolingDataSource<>(connectionPool);

	}

2) HikariCP

HikariCPは速く、信頼性が高く、シンプルです。接続プーリングのための優先的なソリューションの1つです。Spring Boot 2.xのようなフレームワークは、それをデフォルトの接続マネージャーとして使用します。

HikariCPを使用するには、プロジェクトのpom.xmlに次の依存関係を追加します。

<dependency>
	<groupId>com.zaxxer</groupId>
	<artifactId>HikariCP</artifactId>
	<version>3.4.5</version>
</dependency>

HikariCPの設定:

私たちは、以下の例プログラムに示されているようにJavaベースの設定を使用することもできますし、HikariCPを設定するためにプロパティファイルを使用することもできます。以下のプロパティを見てみましょう。

  • アイドルタイムアウト: 接続オブジェクトがプール内でアイドル状態でいられる時間(ミリ秒単位)。これは、最小アイドルおよび 最大プールサイズプロパティとともに機能します。指定された時間が経過すると、接続オブジェクトは解放されます。
  • connectionTimeout: プールから接続オブジェクトを取得するためにクライアントが待機するミリ秒単位の時間です。制限時間に達した場合、SQL例外がスローされます。
  • autoCommit: 真または偽を指定でき、真に設定されている場合は、実行するすべてのSQL文が自動的にコミットされ、偽に設定されている場合はSQL文を手動でコミットする必要があります。
  • cachePrepStmts: プレパレートステートメントのキャッシングを有効にする
  • minimumIdle: プールに常に保持される必要がある接続オブジェクトの最小数です。
  • 最大プールサイズ: プール内に保持できる接続の最大数。
package com.journaldev.example;

/**
 * Java JDBC Connection pool using HikariCP example program
 * 
 * @author pankaj
 */

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

public class HikariCPDemo {

	private static HikariDataSource dataSource = null;

	static {
		HikariConfig config = new HikariConfig();
		config.setJdbcUrl("jdbc:mysql://localhost:3306/empdb");
		config.setUsername("root");
		config.setPassword("root");
		config.addDataSourceProperty("minimumIdle", "5");
		config.addDataSourceProperty("maximumPoolSize", "25");

		dataSource = new HikariDataSource(config);
	}

	public static void main(String[] args) throws SQLException {
		Connection connection = null;
		Statement statement = null;
		ResultSet resultSet = null;
		try {
			connection = dataSource.getConnection();
			statement = connection.createStatement();
			resultSet = statement.executeQuery("select * from tblemployee");
			while (resultSet.next()) {
				System.out.println("empId:" + resultSet.getInt("empId"));
				System.out.println("empName:" + resultSet.getString("empName"));
				System.out.println("dob:" + resultSet.getDate("dob"));
				System.out.println("designation:" + resultSet.getString("designation"));
			}
		} finally {
			resultSet.close();
			statement.close();
			connection.close();
		}
	}
}

出力 :

empId:1
empName:Adam
dob:1998-08-15
designation:Manager
empId:2
empName:Smith
dob:2001-01-11
designation:Clerk
empId:3
empName:James
dob:1996-03-13
designation:Officer

3) C3P0

C3P0は最も古いライブラリの一つです。一般的に、Hibernateと一緒に使用されます。C3P0を使用するには、プロジェクトに次の依存関係を追加する必要があります。

<dependency>
	<groupId>com.mchange</groupId>
	<artifactId>c3p0</artifactId>
	<version>0.9.5.5</version>
</dependency>

C3P0を使用して以下のプロパティを設定できます。

  • driverClass: 推奨Jdbcドライバー
  • jdbcUrl: データベース用のJDBC URL。
  • initialPoolSize: スタートアップ時にプール内に作成される接続の数。
  • acquireIncrement: 現在のサイズが不足している場合に作成する必要がある新しい接続の数。
  • maxIdleTime: 接続が使用されずにプールに留まることができる秒数。
  • maxPoolSize: プールに保持できる接続の最大数。
  • minPoolSize: プール内に常に保持される必要がある接続オブジェクトの最小数。
package com.journaldev.example;

/**
 * Java JDBC Connection pool using C3PO example program
 * 
 * @author pankaj
 */

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3P0Demo {

	static ComboPooledDataSource comboPooledDataSource = null;

	static {
		comboPooledDataSource = new ComboPooledDataSource();

		comboPooledDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/empdb?useSSL=false");
		comboPooledDataSource.setUser("root");
		comboPooledDataSource.setPassword("root");

		comboPooledDataSource.setMinPoolSize(3);
		comboPooledDataSource.setAcquireIncrement(3);
		comboPooledDataSource.setMaxPoolSize(30);

	}

public static void main(String[] args) throws SQLException {
		Connection connection = null;
		Statement statement = null;
		ResultSet resultSet = null;
		try {
			connection = comboPooledDataSource.getConnection();
			statement = connection.createStatement();
			resultSet = statement.executeQuery("select * from tblemployee");
			while (resultSet.next()) {
				System.out.println("empId:" + resultSet.getInt("empId"));
				System.out.println("empName:" + resultSet.getString("empName"));
				System.out.println("dob:" + resultSet.getDate("dob"));
				System.out.println("designation:" + resultSet.getString("designation"));
			}
		} finally {
			resultSet.close();
			statement.close();
			connection.close();
		}
	}

}

出力 :

Aug 29, 2020 8:59:05 PM com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource 
INFO: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> 1hge9kqacgbp7hjpftse6|77a567e1, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> null, extensions -> {}, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, forceSynchronousCheckins -> false, forceUseNamedDriverClass -> false, identityToken -> 1hge9kqacgbp7hjpftse6|77a567e1, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost:3306/empdb?useSSL=false, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 30, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, preferredTestQuery -> null, privilegeSpawnedThreads -> false, properties -> {user=******, password=******}, propertyCycle -> 0, statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, userOverrides -> {}, usesTraditionalReflectiveProxies -> false ]

empId:1
empName:Adam
dob:1998-08-15
designation:Manager
empId:2
empName:Smith
dob:2001-01-11
designation:Clerk
empId:3
empName:James
dob:1996-03-13
designation:Officer

JDBC接続プールの例に関するチュートリアルはこれで終わりです。ここで重要なことが見落とされていないことを願っています。

参考文献: HikariCP、Apache Commons DBCP、C3P0

関連記事: