Pool de conexões em Java
Pool de conexão significa um pool de objetos de conexão. O pool de conexões é baseado em um padrão de design de pool de objetos. O padrão de design de pool de objetos é usado quando o custo (tempo e recursos como CPU, rede e E/S) de criação de novos objetos é maior. De acordo com o padrão de design de pool de objetos, o aplicativo cria um objeto com antecedência e os coloca em um pool ou contêiner. Sempre que nosso aplicativo requer tais objetos, ele os adquire do pool em vez de criar um novo.
Um aplicativo que usa uma estratégia de pool de conexões já possui objetos de conexão de banco de dados que podem ser reutilizados. Assim, quando há necessidade de interagir com o banco de dados, a aplicação obtém instâncias de conexão do Pool. O pool de conexões melhora o desempenho do aplicativo que interage com o banco de dados.
Podemos criar nossas próprias implementações de pool de conexões. Qualquer estrutura de pooling de conexões precisa executar três tarefas.
- Criando objetos de conexão
- Gerenciar o uso de objetos criados e validá-los
- Liberar/Destruir Objetos
Com Java, temos um grande conjunto de bibliotecas que estão prontamente disponíveis. Só precisamos configurar algumas propriedades para usá-los.
Pool de conexão no aplicativo Java
Vamos dar uma olhada nas bibliotecas abaixo:
- Apache Commons DBCP 2
- HikariCP
- C3P0
Vamos dar uma olhada nos exemplos abaixo deles, um por um. Para fins de demonstração, usaremos o banco de dados MySQL e o Eclipse IDE. Também criaremos um projeto Java simples baseado no maven usando o JDK 1.8.
Scripts de banco de dados
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');
Exemplo de projeto
Siga os passos abaixo para criar um novo projeto.
- Abra o Eclipse IDE.
- Clique no menu Arquivo e selecione novo -> Projeto Maven
- A tela abaixo será exibida. Selecione criar uma opção de projeto simples e clique no botão Avançar.
- Insira qualquer ID de grupo, ID de artefato, nome e descrição.
Clique no botão Concluir.
- Adicione a seguinte dependência em seu pom.xml para MySQL.
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
- Clique com o botão direito do mouse em Projeto, selecione Maven -> Atualizar projeto -> Ok. Ele baixará todas as dependências.
1) Apache Commons DBCP 2
DBCP é do Apache Common Project. DBCP 2.7 requer Java 8. Para usar DBCP 2, você precisa adicionar a seguinte dependência em seu projeto.
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.7.0</version>
</dependency>
O Apache DBCP 2.0 fornece dois tipos de DataSource (BasicDataSource e PoolingDataSource).
BasicDataSource: Como o nome sugere, é simples e adequado para os casos de uso mais comuns. Ele cria internamente o PoolingDataSource para nós.
Vamos dar uma olhada nas etapas abaixo para inicializar o pool de conexão.
- Crie uma instância de BasicDataSource
- Especificar URL do JDBC, nome de usuário e senha do banco de dados
- Especifique o número mínimo de conexões inativas (número mínimo de conexões que precisam permanecer no pool a qualquer momento)
- Especifique o número máximo de conexões ociosas (Número máximo de conexões ociosas no pool)
- Especifique o número total de conexões máximas.
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();
}
}
}
Saída:
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: oferece mais flexibilidade. Você só precisa alterar o código que cria DataSource. O restante do código permanecerá o mesmo.
Vamos dar uma olhada nas etapas abaixo para inicializar o pool de conexão:
- Crie uma instância de ConnectionFactory usando URL JDBC.
- Crie uma instância de PoolableConnectionFactory usando uma instância de ConnectionFactory que foi criada na etapa 1
- Crie uma instância de GenericObjectPoolConfig e defina as propriedades de máximo de ociosidade, mínimo de ociosidade e máximo de conexão
- Agora inicialize o ObjectPool usando as instâncias criadas nas etapas 2 e 3
- Agora defina pool como uma instância de PoolableConnectionFactory
- Finalmente, inicialize uma instância de 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
O HikariCP é rápido, confiável e simples. É uma das soluções preferidas para pooling de conexões. Frameworks como o Spring Boot 2.x o utilizam como um gerenciador de conexões padrão.
Para usar o HikariCP, adicione a seguinte dependência no pom.xml do nosso projeto.
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>3.4.5</version>
</dependency>
Configuração HikariCP:
Podemos usar a configuração baseada em Java, conforme mostrado em nosso programa de exemplo abaixo, ou podemos usar o arquivo de propriedades para configurar o HikariCP. Vamos dar uma olhada nas propriedades abaixo.
- idleTimeout: Tempo em milissegundos pelo qual o objeto de conexão pode permanecer no pool como ocioso. Funciona com as propriedades MinimumIdle e maximumPoolSize. Após um tempo especificado, o objeto de conexão será liberado.
- connectionTimeout: Tempo em milissegundos que o cliente aguardará pelo objeto de conexão do Pool. Se o limite de tempo for atingido, a exceção SQL será lançada.
- autoCommit: Podemos especificar verdadeiro ou falso e se for definido como verdadeiro, ele confirmará automaticamente todas as instruções SQL que você executar e, se for definido como falso, precisamos confirmar as instruções SQL manualmente
- cachePrepStmts: Habilitar cache para Preparar Instrução
- minimumIdle: O número mínimo de objetos de conexão precisa permanecer no pool a qualquer momento.
- maximumPoolSize: Número máximo de conexões que podem permanecer no pool.
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();
}
}
}
Saída:
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 é uma das bibliotecas mais antigas. Geralmente, é usado com Hibernate. Para usar o C3P0, precisamos adicionar a seguinte dependência ao projeto.
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>
Podemos configurar as seguintes propriedades com C3P0.
- driverClass: Driver Jdbc preferido
- jdbcUrl: URL JDBC para o banco de dados.
- initialPoolSize: número de conexões criadas no pool na inicialização.
- acquireIncrement: O número de novas conexões precisa ser criado quando o tamanho atual não for suficiente.
- maxIdleTime: Número de segundos que a conexão pode permanecer no pool sem ser usada.
- maxPoolSize: Número máximo de conexões que podem permanecer no Pool.
- minPoolSize: O número mínimo de objetos de conexão precisa permanecer no Pool a qualquer momento.
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();
}
}
}
Saída:
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
Isso é tudo para o tutorial de exemplo do pool de conexão JDBC, espero que nada importante tenha sido perdido aqui.
Referências: C3P0