Java의 연결 풀링
연결 풀링은 연결 개체의 풀을 의미합니다. 연결 풀링은 개체 풀 디자인 패턴을 기반으로 합니다. 개체 풀링 디자인 패턴은 새 개체를 만드는 비용(시간 및 CPU, 네트워크 및 IO와 같은 리소스)이 높을 때 사용됩니다. 개체 풀링 디자인 패턴에 따라 응용 프로그램은 미리 개체를 만들어 풀 또는 컨테이너에 배치합니다. 애플리케이션에 이러한 객체가 필요할 때마다 새 객체를 생성하는 대신 풀에서 객체를 가져옵니다.
연결 풀링 전략을 사용하는 응용 프로그램에는 재사용할 수 있는 DB 연결 개체가 이미 있습니다. 따라서 데이터베이스와 상호 작용할 필요가 있을 때 응용 프로그램은 풀에서 연결 인스턴스를 가져옵니다. 연결 풀링은 데이터베이스와 상호 작용하는 애플리케이션 성능을 향상시킵니다.
연결 풀링의 자체 구현을 만들 수 있습니다. 모든 연결 풀링 프레임워크는 세 가지 작업을 수행해야 합니다.
- 연결 개체 만들기
- 생성된 개체의 사용 관리 및 유효성 검사
- 개체 해제/파기
Java에는 쉽게 사용할 수 있는 훌륭한 라이브러리 세트가 있습니다. 몇 가지 속성만 구성하면 사용할 수 있습니다.
Java 애플리케이션의 연결 풀링
아래 라이브러리를 살펴보겠습니다.
- Apache Commons DBCP 2
- 히카리CP
- 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');
예제 프로젝트
아래 단계에 따라 새 프로젝트를 만듭니다.
- 이클립스 IDE를 엽니다.
- 파일 메뉴를 클릭하고 새로 만들기 -> Maven 프로젝트를 선택합니다.\n
- 아래 화면이 표시됩니다. 간단한 프로젝트 만들기 옵션을 선택하고 다음 버튼을 클릭합니다.\n
- 그룹 ID, 유물 ID, 이름 및 설명을 입력합니다.
마침 버튼을 클릭합니다.
- MySQL용 pom.xml에 다음 종속성을 추가합니다.
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
- 프로젝트를 마우스 오른쪽 버튼으로 클릭하고 Maven -> 프로젝트 업데이트 -> 확인을 선택합니다. 모든 종속성을 다운로드합니다.
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은 두 가지 유형의 DataSource(BasicDataSource 및 PoolingDataSource)를 제공합니다.
BasicDataSource: 이름에서 알 수 있듯이 간단하고 가장 일반적인 사용 사례에 적합합니다. 내부적으로 우리를 위해 PoolingDataSource를 생성합니다.
연결 풀을 초기화하는 단계를 아래에서 살펴보겠습니다.
- BasicDataSource의 인스턴스 생성
- JDBC Url, 데이터베이스 사용자 이름 및 암호 지정
- 최소 유휴 연결 수 지정(언제든지 풀에 남아 있어야 하는 최소 연결 수)
- 최대 유휴 연결 수 지정(풀의 최대 유휴 연결 수)
- 최대 연결의 총 수를 지정합니다.
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를 생성하는 코드만 변경하면 됩니다. 나머지 코드는 동일하게 유지됩니다.
연결 풀을 초기화하는 단계를 아래에서 살펴보겠습니다.
- JDBC URL을 사용하여 ConnectionFactory 인스턴스를 생성합니다.
- 1단계에서 만든 ConnectionFactory 인스턴스를 사용하여 PoolableConnectionFactory 인스턴스를 만듭니다.
- GenericObjectPoolConfig 인스턴스 생성 및 최대 유휴, 최소 유휴 및 최대 연결 속성 설정
- 이제 2단계와 3단계에서 생성된 인스턴스를 사용하여 ObjectPool을 초기화합니다.
- 이제 풀을 PoolableConnectionFactory의 인스턴스로 설정
- 마지막으로 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) 히카리CP
HikariCP는 빠르고 안정적이며 간단합니다. 연결 풀링에 대한 기본 솔루션 중 하나입니다. Spring Boot 2.x와 같은 프레임워크는 이를 기본 연결 관리자로 사용합니다.
HikariCP를 사용하려면 프로젝트의 pom.xml에 다음 종속성을 추가하십시오.
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>3.4.5</version>
</dependency>
HikariCP 구성:
아래 예제 프로그램과 같이 Java 기반 구성을 사용하거나 속성 파일을 사용하여 HikariCP를 구성할 수 있습니다. 아래 속성을 살펴보겠습니다.
- idleTimeout: 연결 개체가 풀에서 유휴 상태로 유지될 수 있는 시간(밀리초)입니다. minimumIdle 및 maximumPoolSize 속성과 함께 작동합니다. 지정된 시간이 지나면 연결 개체가 해제됩니다.
- connectionTimeout: 클라이언트가 풀에서 연결 개체를 기다리는 시간(밀리초)입니다. 시간 제한에 도달하면 SQL 예외가 발생합니다.
- autoCommit: true 또는 false를 지정할 수 있으며 true로 설정하면 실행하는 모든 SQL 문을 자동으로 커밋하고 false로 설정하면 SQL 문을 수동으로 커밋해야 합니다.
- cachePrepStmts: Prepare Statement를 위한 캐싱 활성화
- minimumIdle: 언제든지 풀에 남아 있어야 하는 연결 개체의 최소 수입니다.
- maximumPoolSize: 풀에 머물 수 있는 최대 연결 수입니다.
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 연결 풀 예제 자습서의 전부입니다. 여기에서 중요한 내용이 누락되지 않았으면 합니다.
참조: C3P0