Come stabilire un pool di connessioni in JDBC?


111

Qualcuno può fornire esempi o collegamenti su come stabilire un pool di connessioni JDBC?

Dalla ricerca su Google vedo molti modi diversi per farlo ed è piuttosto confuso.

Alla fine ho bisogno del codice per restituire un java.sql.Connectionoggetto, ma non riesco ad iniziare .. qualsiasi suggerimento è benvenuto.

Aggiornamento: non non javax.sqlo java.sqlhanno implementazioni connessione in pool? Perché non sarebbe meglio usarli?


8
No, il JDBC stock non fornisce il pool di connessioni. Hai bisogno di una libreria separata per questo. La maggior parte dei server delle app e dei contenitori servlet hanno pool di connessioni inclusi. Inoltre, le implementazioni JPA in genere forniscono anche implementazioni.
Will Hartung

3
Un aggiornamento per gli utenti Java moderni. JDBC 3.0+ (che credo sia utilizzato in Java 6?) Ha un'implementazione per le connessioni DB in pool. Java 7 utilizza JDBC 4 e Java 8 JDBC 4.1.
BRasmussen

1
Per quanto riguarda l'API JDBC 3.0 per il pool di connessioni: progress.com/tutorials/jdbc/jdbc-jdbc-connection-pool
Arto Bendiken

Risposte:


102

Se hai bisogno di un pool di connessioni autonomo, la mia preferenza va a C3P0 su DBCP (che ho menzionato in questa risposta precedente ), ho avuto troppi problemi con DBCP sotto carico pesante. Usare C3P0 è semplicissimo. Dalla documentazione :

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 

Ma se si esegue all'interno di un server delle applicazioni, consiglierei di utilizzare il pool di connessioni integrato che fornisce. In tal caso, sarà necessario configurarlo (fare riferimento alla documentazione del server delle applicazioni) e recuperare un DataSource tramite JNDI:

DataSource ds = (DataSource) new InitialContext().lookup("jdbc/myDS");

1
Idem, quello. Sono anni che osservo il deadlock di DBCP sotto carico. Versione dopo versione.
Vasiliy

sì, ma anche C3P0, ho avuto la migliore esperienza con BoneCP
Nicolas Mommaerts

1
Sembra che BoneCP sia stato deprecato a favore di HikariCP . HikariCP è anche menzionato in una risposta di seguito .
kaartic

19

Di solito, se hai bisogno di un pool di connessioni, stai scrivendo un'applicazione che viene eseguita in un ambiente gestito, ovvero stai eseguendo all'interno di un server delle applicazioni. Se questo è il caso, assicurarsi di controllare quali servizi di pool di connessioni fornisce il server delle applicazioni prima di provare qualsiasi altra opzione.

La soluzione pronta all'uso sarà la migliore integrata con il resto delle funzionalità dei server delle applicazioni. Se tuttavia non stai eseguendo all'interno di un server delle applicazioni, consiglierei il componente DBCP di Apache Commons . È ampiamente utilizzato e fornisce tutte le funzionalità di pooling di base richieste dalla maggior parte delle applicazioni.


18

HikariCP

È moderno, è veloce, è semplice. Lo uso per ogni nuovo progetto. Lo preferisco molto al C3P0, non conosco troppo bene le altre piscine.


18

Non reinventare la ruota.

Prova uno dei componenti di terze parti immediatamente disponibili:

  • Apache DBCP - Questo è utilizzato internamente da Tomcat e dal tuo veramente.
  • c3p0

Apache DBCP viene fornito con diversi esempi su come impostare un pool di javax.sql.DataSource . Ecco un esempio che può aiutarti a iniziare.


1
Si chiama C3P0. È comunque più performante di DBCP in ambienti multithread poiché DBCP blocca l'accesso a un singolo thread.
BalusC

@BalusC. Grazie per la correzione, ho disclecsiaavuto la meglio su di me. Puoi vedere che il collegamento è corretto. :)
Alexander Pogrebnyak

1
@Mudassir. Consiglierei di guardare un sostituto drop-in per DBCP contribuito a Tomcat da Spring -> static.springsource.com/projects/tc-server/2.0/admin/htmlsingle/… . Non è necessario l'intero server Tomcat per utilizzarlo, solo un singolo jar tomcat-jdbc. Puoi ottenerlo da Maven Central -> org.apache.tomcat:tomcat-jdbc:jar:7.0.22-> search.maven.org/…
Alexander Pogrebnyak

@AlexanderPogrebnyak: Grazie Alexander, è gentile da parte tua. Ho intenzione di utilizzare CP in un servizio web Axis. Penserà al tuo suggerimento. - Mudassir 7 minuti fa
Mudassir

17

Suggerirei di utilizzare la libreria commons-dbcp . Ci sono numerosi esempi elencati su come usarlo, ecco il link al semplice spostamento . L'utilizzo è molto semplice:

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

Hai solo bisogno di creare l'origine dati una volta, quindi assicurati di leggere la documentazione se non sai come farlo. Se non sei a conoscenza di come scrivere correttamente le istruzioni JDBC in modo da non perdere risorse, potresti anche voler leggere questa pagina di Wikipedia .


8
Questo crea effettivamente un pool di connessioni?
llm

@llm Sure! La javax.sql.DataSourcedefinizione dell'interfaccia contiene un'implementazione di "Connection pooling". (Inoltre, penso che tu sappia già cos'è un'interfaccia JDBC)
Eddy

7

Nell'app server che usiamo dove lavoro (Oracle Application Server 10g, se ricordo bene), il pooling è gestito dall'app server. Recuperiamo un javax.sql.DataSourceutilizzando una ricerca JNDI con a javax.sql.InitialContext.

ha fatto qualcosa di simile

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

(Non abbiamo scritto questo codice, è copiato da questa documentazione .)


5

Piscina

  • Il meccanismo di pooling è il modo per creare gli oggetti in anticipo. Quando viene caricata una classe.
  • Migliora l'applicazione performance[riutilizzando gli stessi oggetti per eseguire qualsiasi azione sui dati oggetto] e memory[l'allocazione e la de-allocazione di molti oggetti crea un notevole sovraccarico di gestione della memoria].
  • La pulizia degli oggetti non è richiesta poiché utilizziamo lo stesso oggetto, riducendo il carico di Garbage Collection.

«Pooling [ Objectpool, pool Stringcostante, Threadpool, pool di connessione]

String Costante pool

  • Il pool letterale stringa conserva solo una copia di ogni valore stringa distinto. che deve essere immutabile.
  • Quando viene richiamato il metodo intern, verifica la disponibilità dell'oggetto con lo stesso contenuto nel pool utilizzando il metodo equals. «Se String-copy è disponibile nel Pool, restituisce il riferimento. «Altrimenti, l'oggetto String viene aggiunto al pool e restituisce il riferimento.

Esempio: stringa per verificare un oggetto univoco dal 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 di connessioni con tipo-4 driver utilizzando le librerie 3rd party [ 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

Nel meccanismo del pool di connessioni, quando la classe viene caricata, riceve gli physical JDBC connectionoggetti e fornisce all'utente un oggetto di connessione fisica avvolto. PoolableConnectionè un wrapper attorno alla connessione effettiva.

  • getConnection()seleziona una delle connessioni wrapping gratuite dal pool di oggetti di connessione e la restituisce.
  • close() invece di chiuderlo restituisce la connessione con wrapping al pool.

Esempio: utilizzo del pool di connessioni ~ DBCP2 con 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    :

Applicazione Web : per evitare problemi di connessione quando tutte le connessioni sono chiuse [MySQL "wait_timeout" predefinito 8 ore] per riaprire la connessione con il DB sottostante.

Puoi farlo per testare ogni connessione impostando testOnBorrow = true e validationQuery = "SELECT 1" e non utilizzare autoReconnect per il server MySQL poiché è deprecato. problema

===== ===== 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();    }
}

Vedi anche questi:


Nell'esempio del pool String Constant, ho capito quando hai scritto "Se String-copy è disponibile [.equals ()] nel Pool, restituisce il riferimento.« Altrimenti, l'oggetto String viene aggiunto al pool e restituisce il riferimento. " Ma in public class StringPoolTestha solo 2 metodi void quindi non restituiscono nulla. Quel codice esegue effettivamente il processo di gestione del pool di stringhe? Non sembra nemmeno utilizzare argomenti.
jeffery_the_wind

@ jeffery_the_wind: - è solo per conoscere il concetto di pool, per la verifica del pool di stringhe ho appena usato i metodi hashCode, identityHashCode . modificato il codice ...
Yash

Scusa, s1non è definito?
jeffery_the_wind

OK, volevo solo assicurarmi di vedere tutto. Ci lavorerò. Quello di cui ho bisogno per qualcosa di più vicino alla tua ConnectionPoolclasse. Grazie mille.
jeffery_the_wind

5

Alla fine del 2017 Proxool, BoneCP, C3P0, DBCP sono per lo più defunti in questo momento. HikariCP (creato nel 2012) sembra promettente, fa saltare le porte a qualsiasi altra cosa che io sappia. http://www.baeldung.com/hikaricp

Proxool ha una serie di problemi:
- Sotto carico pesante può superare il numero massimo di connessioni e non tornare al di sotto del massimo
- Può riuscire a non tornare alle connessioni min anche dopo la scadenza delle connessioni
- Può bloccare l'intero pool (e tutti i thread del server / client) se ha problemi di connessione al database durante il thread HouseKeeper (non utilizza .setQueryTimeout)
- Il thread HouseKeeper , pur avendo un blocco del pool di connessioni per il suo processo, richiede al thread Prototyper di ricreare le connessioni (sweep), il che può causare una condizione di competizione / blocco. In queste chiamate di metodo l'ultimo parametro dovrebbe essere sempre sweep: false durante il ciclo, solo sweep: true sotto di esso.
- HouseKeeper ha bisogno solo del singolo sweep PrototypeController alla fine e ne ha di più [menzionato sopra]
- Il thread di HouseKeeper verifica la verifica delle connessioni prima di vedere quali connessioni potrebbero essere scadute [alcuni rischi di testare una connessione scaduta che potrebbe essere interrotta / terminata tramite altri timeout al DB nel firewall, ecc.]
- Il progetto ha un codice incompleto (proprietà definite ma non eseguito)
- La durata massima della connessione predefinita se non definita è di 4 ore (eccessiva)
- Il thread di HouseKeeper viene eseguito ogni cinque secondi per pool (eccessivo)

È possibile modificare il codice e apportare questi miglioramenti. Ma poiché è stato creato nel 2003 e aggiornato nel 2008, mancano quasi 10 anni di miglioramenti Java utilizzati da soluzioni come hikaricp.


4

Come risposto da altri, probabilmente sarai soddisfatto di Apache Dbcp o c3p0 . Entrambi sono popolari e funzionano bene.

Per quanto riguarda il tuo dubbio

Javax.sql o java.sql non hanno implementazioni di connessione in pool? Perché non sarebbe meglio usarli?

Non forniscono implementazioni, piuttosto interfacce e alcune classi di supporto, rivelatori solo per i programmatori che implementano librerie di terze parti (pool o driver). Normalmente non lo guardi nemmeno. Il codice dovrebbe gestire le connessioni dal pool proprio come se fossero connessioni "semplici", in modo trasparente.


4

Vibur DBCP è un'altra libreria a tale scopo. Diversi esempi che mostrano come configurarlo per l'uso con Hibernate, Spring + Hibernate o in modo programmatico, possono essere trovati sul suo sito web: http://www.vibur.org/

Inoltre, vedere il disclaimer qui .


3

Apache Commons ha una libreria per questo scopo: DBCP . A meno che tu non abbia requisiti strani intorno alle tue piscine, userei una libreria poiché è destinata ad essere più complicata e più sottile di quanto speri.


1

Dovresti considerare l'utilizzo di UCP. Universal Connection Pool (UCP) è un pool di connessioni Java. È un pool di connessioni ricco di funzionalità e strettamente integrato con i database Oracle Real Application Clusters (RAC), ADG e DG.

Fare riferimento a questa pagina per maggiori dettagli su UCP.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.