Comment établir un pool de connexions dans JDBC?

111

Quelqu'un peut-il fournir des exemples ou des liens sur la façon d'établir un pool de connexions JDBC?

En recherchant sur Google, je vois de nombreuses façons différentes de le faire et c'est plutôt déroutant.

En fin de compte, j'ai besoin du code pour renvoyer un java.sql.Connectionobjet, mais j'ai du mal à démarrer ... toutes les suggestions sont les bienvenues.

Mise à jour: les implémentations de connexion ne sont pas javax.sqlou java.sqln'ont pas été mises en commun? Pourquoi ne serait-il pas préférable de les utiliser?

llm
la source
8
Non, le JDBC stock ne fournit pas de regroupement de connexions. Vous avez besoin d'une bibliothèque séparée pour cela. La plupart des serveurs d'applications et des conteneurs de servlets ont des pools de connexion inclus. En outre, les implémentations JPA fournissent généralement également des implémentations.
Will Hartung
3
Une mise à jour pour les utilisateurs Java modernes. JDBC 3.0+ (qui, je crois, est utilisé dans Java 6?) A une implémentation pour les connexions DB mises en commun. Java 7 utilise JDBC 4 et Java 8 JDBC 4.1.
BRasmussen
1
Concernant l'API JDBC 3.0 pour la mise en pool des connexions: progress.com/tutorials/jdbc/jdbc-jdbc-connection-
Arto Bendiken

Réponses:

102

Si vous avez besoin d'un pool de connexion autonome, ma préférence va à C3P0 sur DBCP (que j'ai mentionné dans cette réponse précédente ), j'ai juste eu trop de problèmes avec DBCP sous une charge lourde. Utiliser C3P0 est extrêmement simple. De la documentation :

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 

Mais si vous utilisez un serveur d'applications, je vous recommande d'utiliser le pool de connexions intégré qu'il fournit. Dans ce cas, vous devrez le configurer (reportez-vous à la documentation de votre serveur d'application) et récupérer une DataSource via JNDI:

DataSource ds = (DataSource) new InitialContext().lookup("jdbc/myDS");
Pascal Thivent
la source
1
Idem, ça. J'observe une impasse DBCP sous une charge depuis des années maintenant. Version après version.
Vasiliy
ouais mais C3P0 aussi, j'ai eu la meilleure expérience avec BoneCP
Nicolas Mommaerts
1
On dirait que BoneCP a été abandonné au profit de HikariCP . HikariCP est également mentionné dans une réponse ci-dessous .
kaartic
19

Habituellement, si vous avez besoin d'un pool de connexions, vous écrivez une application qui s'exécute dans un environnement géré, c'est-à-dire que vous exécutez à l'intérieur d'un serveur d'applications. Si tel est le cas, assurez-vous de vérifier les fonctionnalités de regroupement de connexions fournies par votre serveur d'applications avant d'essayer d'autres options.

La solution prête à l'emploi sera la mieux intégrée au reste des installations de serveurs d'applications. Si toutefois vous n'utilisez pas un serveur d'applications, je recommanderais le composant Apache Commons DBCP . Il est largement utilisé et fournit toutes les fonctionnalités de mise en commun de base dont la plupart des applications ont besoin.

Tendayi Mawushe
la source
18

HikariCP

C'est moderne, c'est rapide, c'est simple. Je l'utilise pour chaque nouveau projet. Je le préfère beaucoup au C3P0, je ne connais pas trop bien les autres piscines.

tobijdc
la source
18

Ne réinventez pas la roue.

Essayez l'un des composants tiers facilement disponibles:

  • Apache DBCP - Celui-ci est utilisé en interne par Tomcat, et par le vôtre vraiment.
  • c3p0

Apache DBCP est livré avec différents exemples sur la façon de configurer un pooling javax.sql.DataSource . Voici un exemple qui peut vous aider à démarrer.

Alexander Pogrebnyak
la source
1
Il s'appelle C3P0. Il est d'ailleurs plus performant que DBCP dans les environnements multithread puisque DBCP verrouille l'accès à un seul thread.
BalusC
@BalusC. Merci pour la correction, j'ai disclecsiaeu raison de moi. Vous pouvez voir que le lien est correct. :)
Alexander Pogrebnyak
1
@Mudassir. Je recommanderais de regarder un remplacement instantané pour DBCP contribué à Tomcat de Spring -> static.springsource.com/projects/tc-server/2.0/admin/htmlsingle/… . Vous n'avez pas besoin de l'ensemble du serveur Tomcat pour l'utiliser, juste un seul pot tomcat-jdbc. Vous pouvez l'obtenir à partir de Maven Central -> org.apache.tomcat:tomcat-jdbc:jar:7.0.22-> search.maven.org
Alexander Pogrebnyak
@AlexanderPogrebnyak: Merci Alexander, c'est gentil de ta part. Je prévois d'utiliser CP dans un service Web Axis. Pensera à votre suggestion. - Mudassir il y a 7 minutes
Mudassir
17

Je recommanderais d'utiliser la bibliothèque commons-dbcp . Il existe de nombreux exemples listés sur la façon de l'utiliser, voici le lien vers le simple move . L'utilisation est très simple:

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

Vous n'avez besoin de créer la source de données qu'une seule fois, alors assurez-vous de lire la documentation si vous ne savez pas comment faire cela. Si vous ne savez pas comment écrire correctement des instructions JDBC afin de ne pas perdre de ressources, vous pouvez également consulter cette page Wikipédia .

Eric Hauser
la source
8
Cela crée-t-il réellement un pool de connexions?
llm
@llm Bien sûr! La javax.sql.DataSourcedéfinition de l'interface contient une implémentation de "Connection pooling" (en plus, je pense que vous savez déjà ce qu'est une interface JDBC)
Eddy
7

Dans le serveur d'applications que nous utilisons là où je travaille (Oracle Application Server 10g, si je me souviens bien), la mise en commun est gérée par le serveur d'applications. Nous récupérons un en javax.sql.DataSourceutilisant une recherche JNDI avec un javax.sql.InitialContext.

c'est fait quelque chose comme ça

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

(Nous n'avons pas écrit ce code, il est copié de cette documentation .)

Powerlord
la source
5

bassin

  • Le mécanisme de mise en commun est le moyen de créer les objets à l'avance. Quand une classe est chargée.
  • Il améliore l'application performance[en utilisant les mêmes objets pour effectuer toute action sur les données d'objets] & memory[l'allocation et la désallocation de nombreux objets crée une surcharge de gestion de la mémoire importante].
  • Le nettoyage des objets n'est pas nécessaire car nous utilisons le même objet, ce qui réduit la charge de récupération de place.

«Pooling [ Objectpool, StringConstant Pool, ThreadPool, Connection pool]

Piscine constante de chaîne

  • Le pool de littéraux de chaîne conserve une seule copie de chaque valeur de chaîne distincte. qui doit être immuable.
  • Lorsque la méthode interne est appelée, elle vérifie la disponibilité des objets avec le même contenu dans le pool à l'aide de la méthode equals. «Si String-copy est disponible dans le Pool alors renvoie la référence. «Sinon, l'objet String est ajouté au pool et renvoie la référence.

Exemple: Chaîne pour vérifier l' objet unique du 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));
    }
}

Piscine de connexion à l' aide de type 4 pilote en utilisant les bibliothèques 3ème partie [ 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

Dans le mécanisme de pool de connexion, lorsque la classe est chargée, elle récupère les physical JDBC connectionobjets et fournit un objet de connexion physique encapsulé à l'utilisateur. PoolableConnectionest un wrapper autour de la connexion réelle.

Exemple: utilisation de ~ DBCP2 Connection Pool avec 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    :

Application Web : Pour éviter les problèmes de connexion lorsque toutes les connexions sont fermées [MySQL "wait_timeout" par défaut 8 heures] afin de rouvrir la connexion avec la base de données sous-jacente.

Vous pouvez le faire pour tester chaque connexion en définissant testOnBorrow = true et validationQuery = "SELECT 1" et ne pas utiliser autoReconnect pour le serveur MySQL car il est obsolète. problème

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

A voir aussi:

Yash
la source
Dans l'exemple de pool de constantes de chaîne, je comprends quand vous avez écrit "Si la copie de chaîne est disponible [.equals ()] dans le pool renvoie alors la référence.« Sinon, l'objet String est ajouté au pool et renvoie la référence. " Mais dans le public class StringPoolTestjuste a 2 méthodes vides afin qu'elles ne retournent rien. Ce code passe-t-il réellement par le processus de gestion du pool de chaînes? Il ne semble même pas utiliser d'arguments.
jeffery_the_wind
@jeffery_the_wind: - c'est juste pour connaître le concept de pool, pour la vérification du pool de chaînes, je viens d'utiliser les méthodes hashCode, identityHashCode . a modifié le code ...
Yash
Désolé, s1n'est pas défini?
jeffery_the_wind
OK, je voulais juste m'assurer que je voyais tout. Je vais travailler dessus. Ce dont j'aurai besoin pour quelque chose de plus proche de votre ConnectionPoolclasse. Merci beaucoup.
jeffery_the_wind
5

À la fin de 2017, Proxool, BoneCP, C3P0, DBCP sont pour la plupart disparus en ce moment. HikariCP (créé en 2012) semble prometteur, souffle les portes de tout ce que je sais. http://www.baeldung.com/hikaricp

Proxool a un certain nombre de problèmes:
- Sous une charge élevée, il peut dépasser le nombre maximum de connexions et ne pas revenir en dessous du maximum
- Peut réussir à ne pas revenir aux connexions minimes même après l'expiration des connexions
- Peut verrouiller tout le pool (et tous les threads serveur / client) s'il a des difficultés à se connecter à la base de données pendant le thread HouseKeeper (n'utilise pas .setQueryTimeout)
- Le thread HouseKeeper, tout en ayant le verrouillage du pool de connexions pour son processus, demande au thread Prototyper de recréer les connexions (balayage), ce qui peut entraîner une condition de concurrence / blocage. Dans ces appels de méthode, le dernier paramètre doit toujours être sweep: false pendant la boucle, seulement sweep: true en dessous.
- HouseKeeper n'a besoin que du seul balayage PrototypeController à la fin et en a plus [mentionné ci-dessus]
- Le thread HouseKeeper vérifie le test des connexions avant de voir quelles connexions peuvent être expirées [certains risques de tester une connexion expirée qui peut être interrompue / interrompue par d'autres délais d'expiration vers la base de données dans le pare-feu, etc.]
- Le projet a du code inachevé (propriétés définies mais sans action)
- La durée de vie maximale par défaut de la connexion si elle n'est pas définie est de 4 heures (excessive)
- Le thread HouseKeeper s'exécute toutes les cinq secondes par pool (excessif)

Vous pouvez modifier le code et apporter ces améliorations. Mais comme il a été créé en 2003 et mis à jour en 2008, il lui manque près de 10 ans d'améliorations Java utilisées par des solutions comme hikaricp.

bluejaguar
la source
4

Comme d'autres l'ont répondu, vous serez probablement satisfait d' Apache Dbcp ou c3p0 . Les deux sont populaires et fonctionnent bien.

Concernant votre doute

Javax.sql ou java.sql n'ont-ils pas d'implémentations de connexion groupées? Pourquoi ne serait-il pas préférable de les utiliser?

Ils ne fournissent pas d'implémentations, mais plutôt des interfaces et certaines classes de support, uniquement valables pour les programmeurs qui implémentent des bibliothèques tierces (pools ou pilotes). Normalement, vous ne regardez même pas cela. Votre code doit traiter les connexions de votre pool comme il s'agissait de connexions «simples», de manière transparente.

Leonbloy
la source
4

Vibur DBCP est une autre bibliothèque à cet effet. Plusieurs exemples montrant comment le configurer pour une utilisation avec Hibernate, Spring + Hibernate, ou par programme, peuvent être trouvés sur son site Web: http://www.vibur.org/

Consultez également la clause de non-responsabilité ici .

Siméon Malchev
la source
3

Apache Commons a une bibliothèque à cet effet: DBCP . À moins que vous n'ayez des exigences étranges autour de vos piscines, j'utiliserais une bibliothèque car elle sera forcément plus délicate et plus subtile que vous ne l'espérez.

sblundy
la source
1

Vous devriez envisager d'utiliser UCP. Universal Connection Pool (UCP) est un pool de connexions Java. Il s'agit d'un pool de connexions riche en fonctionnalités et étroitement intégré aux bases de données Oracle Real Application Clusters (RAC), ADG, DG.

Reportez-vous à cette page pour plus de détails sur UCP.

Nirmala
la source
0

MiniConnectionPoolManager est une implémentation à un seul fichier java, si vous recherchez une solution intégrable et que vous n'êtes pas trop préoccupé par les performances (bien que je ne l'ai pas testé à cet égard).

Il s'agit de licences EPL , LGPL et MPL multi-licences .

Sa documentation propose également des alternatives à vérifier (en plus de DBCP et C3P0):

Matthieu
la source