7.1 Pool de conexão

Hoje vamos aprender a trabalhar com banco de dados de forma ainda mais profissional. E agora falaremos sobre o pool de threads, ou pool de conexões em inglês.

A conexão com o banco de dados leva algum tempo. Especialmente se o banco de dados for remoto. Se fizermos uma conexão com o banco de dados para cada solicitação, a resposta de nosso aplicativo será incrivelmente lenta em velocidade. Sem falar nos recursos que vai consumir.

Como solução para tais problemas, foi proposto armazenar as conexões com a base em algum conjunto, que é chamado de pool de threads.

Quando solicitamos uma nova conexão, o pool de conexão a cria e, quando fechado, não a fecha, mas a salva no pool de conexão. E se solicitarmos uma conexão do pool de conexões novamente, ele nos fornecerá uma das antigas em vez de criar uma nova.

Na verdade, o aplicativo funciona por meio de um driver especial, que é um wrapper para um driver JDBC comum e possui funcionalidade adicional para trabalhar com o pool. Esta situação pode ser representada da seguinte forma:

pool de conexões

O programador pode definir manualmente as configurações do pool de conexões: o número de conexões ativas, o tempo limite, etc.

Para situações especialmente extremas, você pode escrever seu próprio pool de conexões: uma classe que terá uma lista de conexões. Ele substituirá a função de fechamento, que retornará a conexão de volta à lista, e haverá muitas outras vantagens, como o temporizador de conexão aberta. Quando não há conexão por muito tempo, a conexão é encerrada.

7.2* Interface DataSource e ConnectionPoolDataSource

O pool de threads geralmente funciona em conjunto com o objeto DataSource. Esse objeto pode ser pensado como uma abstração de um banco de dados remoto. O próprio nome Data Source pode ser traduzido literalmente como Data Source. O que é uma espécie de insinuação.

Os objetos DataSource são usados ​​para obter uma conexão física com um banco de dados e são uma alternativa ao DriverManager. Não há necessidade de registrar o driver JDBC. Você só precisa definir os parâmetros apropriados para estabelecer uma conexão eexecute o método getConnection().

Ao criar um objeto do tipo DataSource localmente (diretamente no aplicativo), os parâmetros de conexão são definidos pelos métodos apropriados fornecidos pelo provedor do driver JDBC. Esses métodos não são definidos pela interface DataSource, pois os parâmetros para conexão a SGBDs de diferentes fornecedores podem diferir tanto em tipo quanto em número (por exemplo, nem todos os SGBDs requerem a especificação do tipo de driver ou protocolo de rede).

Assim, ao trabalhar com o SGBD Oracle, para utilizar os métodos set e get correspondentes, é necessário obter um objeto do tipo OracleDataSource, que é uma instância da classe de mesmo nome que implementa a interface DataSource. Portanto, essa forma de criar objetos do tipo DataSource torna seu código menos portátil e menos dependente da implementação específica do driver.

A seguir está um exemplo de código que ilustra o uso local de objetos do tipo 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* Interface JNDI

Os recursos completos de objetos do tipo DataSource são manifestados em combinação com o uso da interface JNDI. JNDI é um serviço especial (algo como um diretório) para grandes aplicativos de servidor que permite que um serviço estabeleça uma conexão com outro.

A utilização do serviço de nome e diretório permite armazenar objetos do tipo DataSource previamente criados pelo administrador do sistema com parâmetros de conexão predefinidos. A seguir estão alguns dos nomes de propriedade (parâmetro) padrão desenvolvidos pela Sun:

Nome da propriedade tipo de dados Java Propósito
nome do banco de dados Corda Nome do banco de dados
nome do servidor Corda Nome do servidor
do utilizador Corda Nome de usuário / ID)
senha Corda Senha do usuário
número da porta int Número da porta do servidor DBMS

A combinação de interfaces DataSource e JNDI desempenha um papel fundamental no desenvolvimento de aplicativos corporativos multicamadas baseados na tecnologia de componentes J2EE.

Se você usar uma combinação das interfaces DataSource e JNDI em seu código de aplicativo, precisará apenas solicitar um objeto do tipo DataSource do serviço de nomenclatura e diretório. Nesse caso, os detalhes da conexão e o código do programa dependente de uma implementação específica do driver são ocultados do aplicativo no objeto armazenado no nome e no serviço de diretório.

Assim, o compartilhamento de objetos do tipo DataSource e JNDI envolve duas etapas que são executadas independentemente uma da outra:

  1. Você deve armazenar o objeto nomeado do tipo DataSource no serviço de nomenclatura e diretório usando Context.bind()a extensão javax.naming.
  2. Solicite um objeto do tipo DataSource do serviço de nomenclatura e diretório no aplicativo usando o arquivo Context.lookup(). Depois disso, você pode usar seu método DataSource.getConnection()para obter uma conexão com o banco de dados.

A seguir está um exemplo de uso da interface JNDI e um objeto do tipo OracleDataSource juntos.

// 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!!!");

Não analisaremos o trabalho da JNDI. Isso está fora do escopo deste curso. Eu só quero que você saiba que essa abordagem é muito comum em grandes aplicações. Não se perca se vir um código como este no futuro.

Não importa como funciona. Você só precisa saber que com um serviço JNDI você pode obter o objeto proxy de qualquer serviço registrado no diretório de serviços. É assim que você obtém o objeto DataSource e o utiliza para trabalhar com o banco de dados.

7.4 Exemplos de Pool de Conexões

Vamos voltar para onde começamos - pools de conexão.

Os programadores Java em seus programas geralmente usam bibliotecas que contêm várias implementações de pools de conexão. Entre eles, há três mais populares:

  • Apache Commons DBCP
  • C3PO
  • HikariCP

O projeto Apache foi o primeiro a criar um bom pool de conexão, também é usado dentro do servidor web Tomcat. Um exemplo de como trabalhar com ele:

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(){ }
}

O segundo pool muito popular é o C3PO . Nomes estranhos, concordo. O nome C3PO é uma referência ao robô c3po de Star Wars. E também CP é a abreviação de Connection Pool.

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(){}
}

A documentação para ele pode ser encontrada na página oficial .

E, finalmente, o Connection Pool mais popular em nosso tempo é o 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(){}
}

Aqui está sua página oficial do GitHub .

E um bom artigo sobre configuração.