1. Introduction
La technologie de mise en commun est largement utilisée en Java. En bref, il utilise un pool d'objets pour stocker une instance avec un nombre limité d'instances. Les développeurs obtiennent des instances du pool d'objets, puis reviennent au pool d'objets après utilisation, réduisant ainsi la surcharge de la création fréquente d'objets par le système et détruisant des objets dans une certaine mesure. Le pool de threads Java et le pool de connexions de base de données sont des applications typiques, mais tous les objets ne conviennent pas à la mise en commun. La mise en commun des objets avec des frais généraux relativement faibles affectera les performances, car la maintenance des pools d'objets nécessite également une certaine surcharge de ressources. Pour les objets avec des frais généraux élevés et une création fréquente et une utilisation, l'utilisation de la technologie de mise en commun améliorera considérablement les performances.
Il existe de nombreux pools de connexions de base de données matures dans l'industrie, tels que C3P0, DBCP, Proxool et Alibaba's Druid. Il y a beaucoup et open source, et vous pouvez trouver le code source sur GitHub. Les développeurs peuvent choisir en fonction de leurs propres besoins, de leurs caractéristiques et de leurs performances. Cet article est simplement pour comprendre la technologie d'apprentissage de la mise en commun et implémenter un pool de connexions de base de données simple. S'il y a des erreurs, j'espère les critiquer et les corriger.
2. Conception
Classes et interfaces principales
.ConnectionParam - Classe de paramètres de pool de connexion de base de données, responsable de la configuration des connexions de base de données et des paramètres liés au pool de connexion. Implémentation à l'aide de Builder.
Mot de passe utilisateur de l'URL du pilote - requis pour se connecter à la base de données
MinConnection - Nombre minimum de connexions
MaxConnection - Nombre maximum de connexions
MINIDLE - Nombre minimum de connexions inactives
Maxwait - temps d'attente maximum
pilote de chaîne final privé; URL de chaîne finale privée; utilisateur de chaîne finale privée; Mot de passe de chaîne final privé; finale privée int MinConnection; Final privé int maxconnection; Final privé Int Minidle; Final privé long Maxwait;
.ConnectionPool - Pool de connexion de la base de données
Le constructeur ConnectionPool est déclaré comme protection, interdit la création externe et est géré uniformément par ConnectionPoolFactory.
ConnectionPool implémente l'interface DataSource et la méthode regetConnection ().
ConnectionPool contient deux conteneurs - une file d'attente stocke la connexion inactive et l'autre vecteur (en considération de synchronisation) stocke la connexion utilisée.
Lorsque le développeur utilise une connexion de base de données, il est récupéré de la file d'attente, et s'il n'y a pas, il revient vide; Une fois la connexion à clôture terminée, elle est remise au vecteur.
ConnectionPool fournit un mécanisme d'extension dynamique simple basé sur Minidle et MaxConnection.
Final statique privé int initial_size = 5; chaîne finale statique privée close_method = "close"; Logueur statique privé; Taille INT privée; ConnectionParam Private ConnectionParam; Private ArrayBlockingQueue <Connexion> idleConnectionQueue; Vector privé <Connexion> BusyConnection Vector;
.ConnectionPoolFactory - Classe de gestion de pool de connexion
ConnectionPoolFactory détient un statique concurrenthashmap pour stocker des objets de pool de connexion.
ConnectionPoolFactory permet de créer plusieurs pools de connexion avec différentes configurations de différentes bases de données.
Le développeur doit enregistrer (lier) le pool de connexion avec un nom spécifique pour la première fois, puis obtenir la connexion à partir du pool de connexion spécifié à chaque fois.
Si le pool de connexions n'est plus utilisé, le développeur peut déconnecter (délier) le pool de connexion.
Carte statique privée <String, ConnectionPool> poolMap = new ConcurrentHashMap <> (); Connexion statique publique getConnection (String PoolName) lève Sqlexception {nameCheck (PoolName); ConnectionPool ConnectionPool = PoolMap.get (PoolName); return ConnectionPool.getConnection (); } public static void registerConnectionPool (nom de chaîne, connexion ConnectionParam) {registerCheck (name); poolMap.put (nom, nouveau ConnectionPool (ConnectionParam)); } // Laissez GC public static void UnregisterConnectionPool (String name) {nameCheck (name); ConnectionPool final ConnectionPool = poolMap.get (name); PoolMap.Remove (nom); nouveau thread (new Runnable () {@Override public void run () {connectionPool.clear ();}}). start (); }Code de base
Le code central du pool de connexion de la base de données se trouve dans la méthode getConnection (). Habituellement, une fois que le développeur a traité les opérations de base de données, la méthode Close () sera appelée. La connexion doit être fermée et les ressources doivent être publiées. Dans le pool de connexions de la base de données, lorsque l'utilisateur appelle la méthode Close (), la connexion ne doit pas être fermée directement, mais doit être remise dans le pool et réutilisé. Ici, le mécanisme de proxy dynamique Java est utilisé. La GetConnection ne renvoie pas une "vraie" connexion, mais une classe de proxy personnalisée (la classe anonyme est utilisée ici). Lorsque l'utilisateur appelle la méthode Close (), il intercepte et le remet dans le pool. Pour Dynamic Proxy, vous pouvez vous référer à un autre blog "Java Dynamic Proxy Simple Application"
@Override Public Connection getConnection () lève SQException {try {Final Connection Connection = idleConnectionQueue.poll (ConnectionParam.getMaxWait (), TimeUnit.Milliseconds); if (connection == null) {logger.info (videmsg ()); Assurecapacity (); retourner null; } BusyConnectionVector.add (connexion); return (connexion) proxy.newproxyinstance (this.getClass (). getClassOader (), new class [] {connection.class}, new invocationhandler () {@Override public objet invoke (objet proxy, méthode, méthode, objet [] args) lance le throws {if (! méthode.getName (). Args);} else {idleConnectionque.offer (connexion); } catch (InterruptedException e) {e.printStackTrace (); } return null; }2. Utiliser
Tout d'abord, l'utilisateur construit les paramètres de pool de connexion de la base de données (ConnectionParam), y compris le pilote, l'URL, l'utilisateur, les éléments requis du mot de passe requis. Vous pouvez personnaliser des options telles que MinConnection, MaxConnection, etc. Si elle n'est pas définie, utilisez la valeur par défaut du système. C'est l'avantage d'utiliser Builder pour créer un grand nombre d'attributs, y compris les attributs requis et les attributs facultatifs. Ensuite, enregistrez le pool de connexion avec le ConnectionPoolFactory avec un nom spécifique, et enfin obtenez la connexion en appelant la méthode d'usine statique de ConnectionPoolFactory.
String Driver = "com.mysql.jdbc.driver"; String url = "jdbc: mysql: // localhost: 3306 / test"; String user = "root"; Chaîne mot de passe = "root"; ConnectionParam ConnectionParam = new ConnectionParam.ConnectionParambuilder (pilote, URL, utilisateur, mot de passe) .build (); ConnectionPoolFactory.RegisterConnectionPool ("test", ConnectionParam); Connexion connexion = connectionPoolFactory.getConnection ("test");3. Code
.Paramconfiguration
Package Database.Config; Importer Java.io.Serializable; / ** * Paramètres de connexion de la base de données * Créé par Michael Wong le 2016/1/18. * / classe publique Paramconfiguration implémente Serializable {public static final int min_connection = 5; public static final int max_connection = 50; public static final int min_idle = 5; public statique final long max_wait = 30000; privé paramconfiguration () {}}.Constructeur
Base de données de packages; / ** * Builder * Créé par Michael Wong le 2016/1/18. * / Public Interface Builder <T> {T Build ();}.ConnectionParam
Base de données de packages; Importer la base de données.config.paramconfiguration; / ** * Paramètres de connexion de la base de données * Créé par Michael Wong le 2016/1/18. * / classe publique ConnectionParam {Private Final String Driver; URL de chaîne finale privée; utilisateur de chaîne finale privée; Mot de passe de chaîne final privé; finale privée int MinConnection; Final privé int maxconnection; Final privé Int Minidle; Final privé long Maxwait; ConnectionParam privé (ConnectionParambuilder Builder) {this.driver = builder.driver; this.url = builder.url; this.user = builder.user; this.password = builder.password; this.minconnection = builder.Minconnection; this.maxConnection = builder.maxConnection; this.minidle = builder.minidle; this.maxwait = builder.maxwait; } public String getDriver () {return this.driver; } public String getUrl () {return this.url; } public String getuser () {return this.user; } public String getPassword () {return this.password; } public int getMinconnection () {return this.Minconnection; } public int getMaxConnection () {return this.maxConnection; } public int getMinIdle () {return this.minidle; } public long getmaxwait () {return this.maxwait; } classe publique Statique ConnectionParambuilder implémente le builder <ConnexionParam> {// Paramètres requis Private Final String Driver; URL de chaîne finale privée; utilisateur de chaîne finale privée; Mot de passe de chaîne final privé; // Paramètres facultatifs - initialisé à la valeur par défaut private int minconnection = paramconfiguration.min_connection; private int maxconnection = paramconfiguration.max_connection; private int MinIdle = paramconfiguration.min_idle; // Obtention du temps d'attente de connexion privé long maxwait = paramconfiguration.max_wait; Connexion PublicParambuilder (pilote de chaîne, URL de chaîne, utilisateur de chaîne, mot de passe de chaîne) {this.driver = driver; this.url = url; this.user = utilisateur; this.password = mot de passe; } public ConnectionParambuilder MinConnection (int MinConnection) {this.Minconnection = MinConnection; retourner ceci; } public ConnectionParambuilder MaxConnection (int maxconnection) {this.maxConnection = maxConnection; retourner ceci; } Connexion publiqueParambuilder minidle (int minidle) {this.minidle = minidle; retourner ceci; } Connexion publiqueParambuilder Maxwait (int maxwait) {this.maxwait = maxwait; retourner ceci; } @Override public ConnectionParam build () {return new ConnectionParam (this); }}}.ConnectionPool
Package Database.Factory; Importer Database.ConnectionParam; Importer Javax.Sql.Datasource; Importer Java.io.printwriter; Importer java.lang.reflect.invocationhandler; import java.lang.reflect.method; import java.lang.reflect.proxy; import import Java.sql.connection; java.sql.DiverManager; import java.sql.sqlexception; import java.sql.sqlfeaturenotsupportedException; import java.util.vector; import java.util.concurrent.arrayblockingqueue; java.util.logging.logger; / ** * Pool de connexion * créé par Michael Wong le 2016/1/18. * / public class ConnectionPool implémente dataSource {private static final int initial_size = 5; chaîne finale statique privée close_method = "close"; Logueur statique privé; Taille INT privée; ConnectionParam Private ConnectionParam; Private ArrayBlockingQueue <Connexion> idleConnectionQueue; Vector privé <Connexion> BusyConnection Vector; ConnectionPool protégé (ConnectionParam ConnectionParam) {this.connectionParam = ConnectionParam; int maxConnection = connectionParam.getMaxConnection (); idleConnectionQueue = new ArrayBlockingQueue <> (maxconnection); busyConnectionVector = new Vector <> (); logger = logger.getLogger (this.getClass (). getName ()); initConnection (); } private void initConnection () {int minConnection = connectionParam.getMinconnection (); int initialSize = initial_size <minconnection? MinConnection: initial_size; essayez {class.forname (connectionParam.getDriver ()); pour (int i = 0; i <initialSize + ConnectionParam.getMinconnection (); i ++) {idleConnectionQueue.put (newConnection ()); taille ++; }} catch (exception e) {lancer une nouvelle exceptionIninitializerError (e); }} @Override Public Connection getConnection () lève sqlexception {try {final connection connection = idleconnectionqueue.poll (connectionparam.getMaxWait (), timeunit.milliseconds); if (connection == null) {logger.info (videmsg ()); Assurecapacity (); retourner null; } BusyConnectionVector.add (connexion); return (connexion) proxy.newproxyinstance (this.getClass (). getClassOader (), new class [] {connection.class}, new invocationhandler () {@Override public objet invoke (objet proxy, méthode, méthode, objet [] args) lance le throws {if (! méthode.getName (). Args);} else {idleConnectionque.offer (connexion); } catch (InterruptedException e) {e.printStackTrace (); } return null; } connexion privée newConnection () lève sqlexception {String url = connectionParam.getUrl (); String User = ConnectionParam.getUser (); String mot de passe = ConnectionParam.getPassword (); return driverManager.getConnection (URL, utilisateur, mot de passe); } protégée int size () {return size; } protégé int idleconnectionquantity () {return idleConnectionQueue.size (); } protégé int busyConnectionquantity () {return busyConnectionVector.size (); } private void assurecapacity () lève sqlexception {int minidle = connectionParam.getMinIdle (); int maxConnection = connectionParam.getMaxConnection (); int newcapacity = size + minidle; newCapacity = newCapacity> maxConnection? MaxConnection: NewCapacity; int culpte = 0; if (size <newcapacity) {try {for (int i = 0; i <newcapacity - size; i ++) {idleConnectionQueue.put (newConnection ()); GrowCount ++; }} catch (InterruptedException e) {e.printStackTrace (); }} size = size + GrowCount; } protégé void clear () {try {while (size--> 0) {connection connection = idleconnectionqueue.take (); connection.close (); }} catch (InterruptedException | sqlexception e) {e.printStackTrace (); }} chaîne privée videmsg () {return "La base de données est occupée, veuillez patienter ..."; } @Override Public Connection getConnection (String Username, String Motway) lève SQLEXception {return null; } @Override public Printwriter getLogWriter () lève sqlexception {return null; } @Override public void SetLogWriter (printwriter out) lève SQELLEXception {} @Override public void setLogInTimeout (int Seconds) lève SQELLEXception {} @Override public int getLogInTimeout () lance sqlexception {return 0; } @Override Public Logger getParentLogger () lève sqlFeAreRenotsupporTedException {return null; } @Override public <T> t Unfap (class <T> iface) lève sqlexception {return null; } @Override public boolean iswrapperfor (class <?> Iface) lève sqlexception {return false; }}.ConnectionPoolFactory
Package Database.Factory; Importer Database.ConnectionParam; Importer Java.Sql.Connection; Importer java.sql.sqlexception; import java.util.map; import java.util.concurrent.concurrenthashmap; / ** * connexion pool factory * créé par Michael Wong sur 2016/1/18. * / classe publique ConnectionPoolFactory {private ConnectionPoolFactory () {} map statique privée <String, ConnectionPool> poolMap = new ConcurrentHashMap <> (); Connexion statique publique getConnection (String PoolName) lève Sqlexception {nameCheck (PoolName); ConnectionPool ConnectionPool = PoolMap.get (PoolName); return ConnectionPool.getConnection (); } public static void registerConnectionPool (nom de chaîne, connexion ConnectionParam) {registerCheck (name); poolMap.put (nom, nouveau ConnectionPool (ConnectionParam)); } // Laissez GC public static void UnregisterConnectionPool (String name) {nameCheck (name); ConnectionPool final ConnectionPool = poolMap.get (name); PoolMap.Remove (nom); nouveau thread (new Runnable () {@Override public void run () {connectionPool.clear ();}}). start (); } public static int size (string poolName) {nameCheck (poolName); return poolmap.get (poolName) .size (); } public static int getIdleConnectionquantity (String PoolName) {nameCheck (PoolName); return poolmap.get (poolName) .IdleConnectionquantity (); } public static int getBusyConnectionquantity (String PoolName) {nameCheck (PoolName); return poolmap.get (poolName) .BusyConnectionquantity (); } private static void registercheck (string name) {if (name == null) {throw new illégalargumentException (nullName ()); }} private static void nameCheck (name de chaîne) {if (name == null) {throw new illégalArgumentException (nullName ()); } if (! PoolMap.ContainsKey (name)) {Throw New illégalArgumentException (NotExists (nom)); }} chaîne statique privée nullName () {return "Le nom du pool ne doit pas être nul"; } Nothexists de chaîne statique privés (nom de chaîne) {return "Pool de connexion nommé" + name + "n'existe pas"; }} 4. Test
Test de l'unité JUnit
Package Database.Factory; Importer Database.ConnectionParam; import org.junit.test; import java.sql.connection; import java.sql.sqlexception; importer java.util.arraylist; import java.util.list; import static org.junit.assert. 2016/1/20. * / classe publique ConnectionPoolFactoryTest {@Test public void testGetConnection () lève SQELLEXception {String driver = "com.mysql.jdbc.driver"; String url = "jdbc: mysql: // localhost: 3306 / test"; String user = "root"; Chaîne mot de passe = "root"; ConnectionParam ConnectionParam = new ConnectionParam.ConnectionParambuilder (pilote, URL, utilisateur, mot de passe) .build (); ConnectionPoolFactory.RegisterConnectionPool ("test", ConnectionParam); List <Connexion> ConnectionList = new ArrayList <> (); pour (int i = 0; i <12; i ++) {connectionList.add (connexionpoolfactory.getConnection ("test")); } imprimer(); Close (ConnectionList); imprimer(); connectionList.Clear (); pour (int i = 0; i <12; i ++) {connectionList.add (connexionpoolfactory.getConnection ("test")); } imprimer(); Close (ConnectionList); ConnectionPoolFactory.UnRegisterConnectionPool ("test"); } @Test (attendu = illégalArgumentException.class) public void testException () {try {ConnectionPoolFactory.getConnection ("test"); } catch (sqlexception e) {e.printStackTrace (); }} private void close (list <Connection> ConnectionList) lève SQException {for (connection Conn: ConnectionList) {if (Conn! = null) {Conn.close (); }}} private void print () {System.out.println ("Idle:" + ConnectionPoolfactory.getIdleConnectionquantity ("test")); System.out.println ("Busy:" + ConnectionPoolFactory.getBusyConnectionquantity ("test")); System.out.println ("Size:" + ConnectionPoolFactory.size ("Test")); }}Ce qui précède concerne cet article, j'espère qu'il sera utile à l'apprentissage de tout le monde.