Como estabelecer um pool de conexão em JDBC?

111

Alguém pode fornecer exemplos ou links sobre como estabelecer um pool de conexão JDBC?

Ao pesquisar no Google, vejo muitas maneiras diferentes de fazer isso e é bastante confuso.

Em última análise, preciso que o código retorne um java.sql.Connection objeto, mas estou tendo problemas para começar ... todas as sugestões são bem-vindas.

Atualização: não tem javax.sqlou java.sqltem implementações de conexão em pool? Por que não seria melhor usar isso?

llm
fonte
8
Não, o JDBC de estoque não fornece pool de conexão. Você precisa de uma biblioteca separada para isso. A maioria dos servidores de aplicativos e contêineres de servlet têm pools de conexão incluídos com eles. Além disso, as implementações JPA geralmente fornecem implementações também.
Will Hartung de
3
Uma atualização para os usuários modernos de Java. JDBC 3.0+ (que acredito ser usado no Java 6?) Tem uma implementação para conexões de banco de dados em pool. Java 7 usa JDBC 4 e Java 8 JDBC 4.1.
BRasmussen de
1
Em relação à API JDBC 3.0 para pool de conexão: progress.com/tutorials/jdbc/jdbc-jdbc-connection-pooling
Arto Bendiken

Respostas:

102

Se você precisar de um pool de conexão autônomo, minha preferência vai para C3P0 em vez de DBCP (que mencionei na resposta anterior ). Eu simplesmente tive muitos problemas com DBCP sob carga pesada. Usar o C3P0 é muito simples. Da documentação :

ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass( "org.postgresql.Driver" ); //loads the jdbc driver
cpds.setJdbcUrl( "jdbc:postgresql://localhost/testdb" );
cpds.setUser("swaldman");
cpds.setPassword("test-password");

// the settings below are optional -- c3p0 can work with defaults
cpds.setMinPoolSize(5);
cpds.setAcquireIncrement(5);
cpds.setMaxPoolSize(20);

// The DataSource cpds is now a fully configured and usable pooled DataSource 

Mas se você estiver executando dentro de um servidor de aplicativos, eu recomendaria usar o pool de conexão embutido que ele fornece. Nesse caso, você precisará configurá-lo (consulte a documentação do seu servidor de aplicativos) e recuperar um DataSource via JNDI:

DataSource ds = (DataSource) new InitialContext().lookup("jdbc/myDS");
Pascal Thivent
fonte
1
Idem, isso. Tenho observado o bloqueio do DBCP sob carga há anos. Versão após versão.
Vasiliy
sim, mas C3P0 também, tive a melhor experiência com BoneCP
Nicolas Mommaerts
1
Parece que o BoneCP foi preterido em favor do HikariCP . O HikariCP também é mencionado em uma resposta abaixo .
Kaartic
19

Normalmente, se você precisa de um pool de conexão, está escrevendo um aplicativo que é executado em algum ambiente gerenciado, ou seja, está executando dentro de um servidor de aplicativos. Se este for o caso, certifique-se de verificar quais facilidades de pool de conexão seu servidor de aplicação oferece antes de tentar qualquer outra opção.

A solução out-of-the-box será a mais bem integrada com o resto das instalações dos servidores de aplicativos. Se, no entanto, você não estiver executando dentro de um servidor de aplicativos, eu recomendaria o componente Apache Commons DBCP . É amplamente utilizado e fornece todas as funcionalidades básicas de pool de que a maioria dos aplicativos exige.

Tendayi Mawushe
fonte
18

HikariCP

É moderno, é rápido, é simples. Eu o uso para cada novo projeto. Eu prefiro muito mais que C3P0, não conheço muito bem as outras piscinas.

Tobijdc
fonte
18

Não reinvente a roda.

Experimente um dos componentes de terceiros disponíveis:

O Apache DBCP vem com um exemplo diferente de como configurar um javax.sql.DataSource em pool . Aqui está um exemplo que pode ajudá-lo a começar.

Alexander Pogrebnyak
fonte
1
É chamado C3P0. A propósito, é mais eficiente do que o DBCP em ambientes multithread, pois o DBCP bloqueia o acesso a um único thread.
BalusC de
@BalusC. Obrigado pela correção, eu disclecsialevou o melhor de mim. Você pode ver que o link está correto. :)
Alexander Pogrebnyak
1
@Mudassir. Eu recomendaria olhar para um substituto drop-in para DBCP contribuído para Tomcat do Spring -> static.springsource.com/projects/tc-server/2.0/admin/htmlsingle/… . Você não precisa de todo o servidor Tomcat para usá-lo, apenas um único jar tomcat-jdbc. Você pode obtê-lo no Maven Central -> org.apache.tomcat:tomcat-jdbc:jar:7.0.22-> search.maven.org/…
Alexander Pogrebnyak
@AlexanderPogrebnyak: Obrigado Alexander, isso é legal da sua parte. Estou planejando usar o CP em um serviço da web da Axis. Vou pensar em sua sugestão. - Mudassir 7 minutos atrás
Mudassir
17

Eu recomendaria usar a biblioteca commons-dbcp . Existem vários exemplos listados sobre como usá-lo, aqui está o link para o simples mover . O uso é muito simples:

 BasicDataSource ds = new BasicDataSource();
 ds.setDriverClassName("oracle.jdbc.driver.OracleDriver")
 ds.setUsername("scott");
 ds.setPassword("tiger");
 ds.setUrl(connectURI);
 ...
 Connection conn = ds.getConnection();

Você só precisa criar a fonte de dados uma vez, portanto, certifique-se de ler a documentação se não souber como fazer isso. Se você não está ciente de como escrever instruções JDBC adequadamente para não vazar recursos, também pode ler esta página da Wikipedia .

Eric Hauser
fonte
8
Isso realmente cria um pool de conexão?
11 de
@llm Claro! A javax.sql.DataSourcedefinição da interface contém uma implementação de "pool de conexão". (Além disso, acho que você já sabe o que é uma interface JDBC)
Eddy
7

No servidor de aplicativos que usamos onde trabalho (Oracle Application Server 10g, se bem me lembro), o pooling é feito pelo servidor de aplicativos. Recuperamos um javax.sql.DataSourceusando uma pesquisa JNDI com um javax.sql.InitialContext.

fez algo assim

try {     
   context = new InitialContext();
   jdbcURL = (DataSource) context.lookup("jdbc/CachedDS");
   System.out.println("Obtained Cached Data Source ");
}
catch(NamingException e)   
{  
    System.err.println("Error looking up Data Source from Factory: "+e.getMessage());
}

(Não escrevemos este código, ele é copiado desta documentação .)

Powerlord
fonte
5

Piscina

  • O mecanismo de pooling é a maneira de criar os objetos antecipadamente. Quando uma classe é carregada.
  • Ele melhora a aplicação performance[Ao reutilizar os mesmos objetos para realizar qualquer ação nos Dados-Objeto] & memory[alocar e desalocar muitos objetos cria uma sobrecarga de gerenciamento de memória significativa].
  • A limpeza do objeto não é necessária, pois estamos usando o mesmo objeto, reduzindo a carga da coleta de lixo.

«Pooling [ Objectpool, pool Stringconstante, Threadpool, pool de conexão]

Pool Constant String

  • O conjunto de literais de string mantém apenas uma cópia de cada valor de string distinto. que deve ser imutável.
  • Quando o método interno é invocado, ele verifica a disponibilidade do objeto com o mesmo conteúdo no pool usando o método equals. «Se String-copy está disponível no Pool, então retorna a referência. «Caso contrário, o objeto String é adicionado ao pool e retorna a referência.

Exemplo: String para verificar o Objeto Único do pool.

public class StringPoolTest {
    public static void main(String[] args) { // Integer.valueOf(), String.equals()
        String eol = System.getProperty("line.separator"); //java7 System.lineSeparator();

        String s1 = "Yash".intern();
        System.out.format("Val:%s Hash:%s SYS:%s "+eol, s1, s1.hashCode(), System.identityHashCode(s1));
        String s2 = "Yas"+"h".intern();
        System.out.format("Val:%s Hash:%s SYS:%s "+eol, s2, s2.hashCode(), System.identityHashCode(s2));
        String s3 = "Yas".intern()+"h".intern();
        System.out.format("Val:%s Hash:%s SYS:%s "+eol, s3, s3.hashCode(), System.identityHashCode(s3));
        String s4 = "Yas"+"h";
        System.out.format("Val:%s Hash:%s SYS:%s "+eol, s4, s4.hashCode(), System.identityHashCode(s4));
    }
}

Pool de conexão usando tipo-4 driver usando bibliotecas 3o partido [ DBCP2, c3p0, Tomcat JDBC]

Type 4 - The Thin driver converts JDBC calls directly into the vendor-specific database protocol Ex[Oracle - Thick, MySQL - Quora]. wiki

No mecanismo de pool de conexão, quando a classe é carregada, ela obtém os physical JDBC connectionobjetos e fornece um objeto de conexão física encapsulado ao usuário. PoolableConnectioné um invólucro em torno da conexão real.

  • getConnection()escolha uma das conexões empacotadas livres do pool de objetos de conexão e a retorne.
  • close() em vez de fechá-lo, retorna a conexão agrupada de volta ao pool.

Exemplo: Usando o pool de conexão ~ DBCP2 com Java 7 [ try-with-resources]

public class ConnectionPool {
    static final BasicDataSource ds_dbcp2 = new BasicDataSource();
    static final ComboPooledDataSource ds_c3p0 = new ComboPooledDataSource();
    static final DataSource ds_JDBC = new DataSource();

    static Properties prop = new Properties();
    static {
        try {
            prop.load(ConnectionPool.class.getClassLoader().getResourceAsStream("connectionpool.properties"));

            ds_dbcp2.setDriverClassName( prop.getProperty("DriverClass") );
            ds_dbcp2.setUrl( prop.getProperty("URL") );
            ds_dbcp2.setUsername( prop.getProperty("UserName") );
            ds_dbcp2.setPassword( prop.getProperty("Password") );
            ds_dbcp2.setInitialSize( 5 );

            ds_c3p0.setDriverClass( prop.getProperty("DriverClass") );
            ds_c3p0.setJdbcUrl( prop.getProperty("URL") );
            ds_c3p0.setUser( prop.getProperty("UserName") );
            ds_c3p0.setPassword( prop.getProperty("Password") );
            ds_c3p0.setMinPoolSize(5);
            ds_c3p0.setAcquireIncrement(5);
            ds_c3p0.setMaxPoolSize(20);

            PoolProperties pool = new PoolProperties();
            pool.setUrl( prop.getProperty("URL") );
            pool.setDriverClassName( prop.getProperty("DriverClass") );
            pool.setUsername( prop.getProperty("UserName") );
            pool.setPassword( prop.getProperty("Password") );
            pool.setValidationQuery("SELECT 1");// SELECT 1(mysql) select 1 from dual(oracle)

            pool.setInitialSize(5);
            pool.setMaxActive(3);
            ds_JDBC.setPoolProperties( pool );
        } catch (IOException e) {   e.printStackTrace();
        } catch (PropertyVetoException e) { e.printStackTrace(); }
    }

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

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

    public static Connection getJDBCConnection() throws SQLException {
        return ds_JDBC.getConnection();
    }
}
public static boolean exists(String UserName, String Password ) throws SQLException {
    boolean exist = false;
    String SQL_EXIST = "SELECT * FROM users WHERE username=? AND password=?";
    try ( Connection connection = ConnectionPool.getDBCP2Connection();
          PreparedStatement pstmt = connection.prepareStatement(SQL_EXIST); ) {
        pstmt.setString(1, UserName );
        pstmt.setString(2, Password );

        try (ResultSet resultSet = pstmt.executeQuery()) {
            exist = resultSet.next(); // Note that you should not return a ResultSet here.
        }
    }
    System.out.println("User : "+exist);
    return exist;
}

jdbc:<DB>:<drivertype>:<HOST>:<TCP/IP PORT>:<dataBaseName> jdbc:oracle:thin:@localhost:1521:myDBName jdbc:mysql://localhost:3306/myDBName

connectionpool.properties

URL         : jdbc:mysql://localhost:3306/myDBName
DriverClass : com.mysql.jdbc.Driver
UserName    : root
Password    :

Aplicação Web : Para evitar problemas de conexão quando todas as conexões forem fechadas [MySQL "wait_timeout" padrão 8 horas] para reabrir a conexão com o banco de dados subjacente.

Você pode fazer isso para testar todas as conexões definindo testOnBorrow = true e validationQuery = "SELECT 1" e não use autoReconnect para o servidor MySQL, pois ele está obsoleto. questão

===== ===== context.xml ===== =====
<?xml version="1.0" encoding="UTF-8"?>
<!-- The contents of this file will be loaded for a web application -->
<Context>
    <Resource name="jdbc/MyAppDB" auth="Container" 
        factory="org.apache.tomcat.jdbc.pool.DataSourceFactory" 
        type="javax.sql.DataSource" 

        initialSize="5" minIdle="5" maxActive="15" maxIdle="10"

        testWhileIdle="true"
            timeBetweenEvictionRunsMillis="30000"

        testOnBorrow="true"
            validationQuery="SELECT 1"
            validationInterval="30000"


        driverClassName="com.mysql.jdbc.Driver" 
        url="jdbc:mysql://localhost:3306/myDBName" 
        username="yash" password="777"
    />
</Context>

===== ===== web.xml ===== =====
<resource-ref>
    <description>DB Connection</description>
    <res-ref-name>jdbc/MyAppDB</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
</resource-ref>
===== ===== DBOperations ===== =====
servlet «   init() {}
Normal call used by sevlet  « static {}

static DataSource ds;
static {
    try {
        Context ctx=new InitialContext();
        Context envContext = (Context)ctx.lookup("java:comp/env");
        ds  =   (DataSource) envContext.lookup("jdbc/MyAppDB");
    } catch (NamingException e) {   e.printStackTrace();    }
}

Veja também:

Yash
fonte
No exemplo do pool String Constant, eu entendo quando você escreveu "Se String-copy está disponível [.equals ()] no Pool, então retorna a referência.« Caso contrário, o objeto String é adicionado ao pool e retorna a referência. " Mas há public class StringPoolTestapenas 2 métodos nulos para que eles não retornem nada. Esse código realmente passa pelo processo de gerenciamento do pool de strings? Nem parece usar nenhum argumento.
jeffery_the_wind
@jeffery_the_wind: - é apenas para conhecer o conceito de pool, para verificação de pool de strings, acabei de usar os métodos hashCode, identityHashCode . modificou o código ...
Yash
Desculpe, s1não está definido?
jeffery_the_wind
OK, só queria ter certeza de que estava vendo tudo. Eu vou trabalhar nisso. O que vou precisar para algo mais próximo da sua ConnectionPoolclasse. Muito obrigado.
jeffery_the_wind
5

No final de 2017, Proxool, BoneCP, C3P0, DBCP estão praticamente extintos neste momento. HikariCP (criado em 2012) parece promissor, abre portas para qualquer outra coisa que eu conheça. http://www.baeldung.com/hikaricp

Proxool tem uma série de problemas:
- Sob carga pesada pode exceder o número máximo de conexões e não retornar abaixo do máximo
- Pode gerenciar para não retornar às conexões mínimas, mesmo depois que as conexões expiram
- Pode bloquear todo o pool (e todos os encadeamentos de servidor / cliente) se tiver problemas para se conectar ao banco de dados durante o thread HouseKeeper (não usa .setQueryTimeout)
- o thread HouseKeeper, embora tenha bloqueio de pool de conexão para seu processo, solicita que o thread Prototyper recrie conexões (varredura) que podem resultar em condição de corrida / bloqueio. Nessas chamadas de método, o último parâmetro deve ser sempre sweep: false durante o loop, apenas sweep: true abaixo dele.
- O HouseKeeper só precisa de uma única varredura de PrototypeController no final e tem mais [mencionado acima]
- Verificações de thread do HouseKeeper para teste de conexões antes de ver quais conexões podem ter expirado [algum risco de teste de conexão expirada que pode ser interrompida / encerrada por meio de outros tempos limite para o banco de dados no firewall, etc.]
- O projeto tem código inacabado (propriedades que são definidas mas sem ação)
- A vida útil máxima da conexão padrão, se não definida, é 4 horas (excessiva)
- O encadeamento do HouseKeeper é executado a cada cinco segundos por pool (excessivo).

Você pode modificar o código e fazer essas melhorias. Mas como foi criado em 2003 e atualizado em 2008, faltam quase 10 anos de melhorias em java que soluções como o hikaricp utilizam.

onça-azul
fonte
4

Conforme respondido por outros, você provavelmente ficará feliz com o Apache Dbcp ou c3p0 . Ambos são populares e funcionam bem.

Em relação à sua dúvida

Javax.sql ou java.sql não tem implementações de conexão em pool? Por que não seria melhor usar isso?

Eles não fornecem implementações, em vez de interfaces e algumas classes de suporte, apenas relevantes para os programadores que implementam bibliotecas de terceiros (pools ou drivers). Normalmente você nem olha para isso. Seu código deve lidar com as conexões de seu pool da mesma forma que se tratam de conexões "simples", de forma transparente.

leonbloy
fonte
4

O Vibur DBCP é outra biblioteca para esse fim. Vários exemplos mostrando como configurá-lo para uso com Hibernate, Spring + Hibernate ou programaticamente podem ser encontrados em seu site: http://www.vibur.org/

Além disso, veja o aviso legal aqui .

Simeon Malchev
fonte
3

O Apache Commons possui uma biblioteca para esse fim: DBCP . A menos que você tenha requisitos estranhos em torno de suas piscinas, eu usaria uma biblioteca, pois ela deve ser mais complicada e sutil do que você espera.

sblundy
fonte
1

Você deve considerar o uso de UCP. Universal Connection Pool (UCP) é um pool de conexão Java. É um pool de conexão rico em recursos e totalmente integrado aos bancos de dados Real Application Clusters (RAC), ADG e DG da Oracle.

Consulte esta página para obter mais detalhes sobre o UCP.

Nirmala
fonte
0

MiniConnectionPoolManager é uma implementação de um arquivo java, se você está procurando uma solução incorporável e não está muito preocupado com o desempenho (embora eu não tenha testado a esse respeito).

É EPL , LGPL e MPL multi-licenciado .

Sua documentação também fornece alternativas que valem a pena verificar (além de DBCP e C3P0):

Matthieu
fonte