1. Introducción
La tecnología de agrupación se usa ampliamente en Java. En resumen, utiliza una piscina de objetos para almacenar una instancia con un número limitado de instancias. Los desarrolladores obtienen instancias del grupo de objetos y luego vuelven a la piscina de objetos después de su uso, reduciendo así la sobrecarga de la creación frecuente de objetos del sistema y destruyendo objetos hasta cierto punto. El grupo de hilos de Java y el grupo de conexión de base de datos son aplicaciones típicas, pero no todos los objetos son adecuados para la agrupación. La agrupación de objetos con sobrecarga relativamente pequeña afectará el rendimiento, porque el mantenimiento de los grupos de objetos también requiere una cierta sobrecarga de recursos. Para los objetos con altos gastos generales y la creación frecuente y el uso del uso, el uso de la tecnología de agrupación mejorará en gran medida el rendimiento.
Hay muchos grupos de conexión de bases de datos maduras en la industria, como C3P0, DBCP, Proxool y Alibaba's Druid. Hay muchos y código abierto, y puede encontrar el código fuente en GitHub. Los desarrolladores pueden elegir en función de sus propias necesidades y sus características y rendimiento. Este artículo es solo para comprender la tecnología de agrupación de aprendizaje e implementar un grupo de conexión de base de datos simple. Si hay algún error, espero criticarlos y corregirlos.
2. Diseño
Clases e interfaces principales
.ConnectionParam - Clase de parámetros del grupo de conexión de base de datos, responsable de configurar conexiones de base de datos y parámetros relacionados con el grupo de conexión. Implementación utilizando el constructor.
Contraseña de usuario de URL del controlador: requerido para conectarse a la base de datos
Minconnection - Número mínimo de conexiones
MaxConnection - Número máximo de conexiones
Minidle: número mínimo de conexiones inactivas
MaxWait - Tiempo de espera máximo
controlador de cadena final privado; URL de cadena final privada; Usuario privado de cadena final; contraseña de cadena final privada; Private Final Int Minconnection; Private final int maxconnection; Private final int minidle; Private Final Long Maxwait;
.ConnectionPool - grupo de conexión de base de datos
El constructor ConnectionPool se declara como protección, prohíbe la creación externa y es manejado de manera uniforme por ConnectionPoolFactory.
ConnectionPool implementa el método de interfaz DataSource y regetConnection ().
ConnectionPool contiene dos contenedores: una cola almacena la conexión inactiva y el otro vector (considerando la sincronización) almacena la conexión en uso.
Cuando el desarrollador usa una conexión de base de datos, se recupera de la cola, y si no hay no, devuelve vacío; Cuando se completa la conexión cerrada, se vuelve a colocar a Vector.
ConnectionPool proporciona un mecanismo de expansión dinámico simple basado en Minidle y MaxConnection.
Private static final int inicial_size = 5; cadena final estática privada Close_method = "Cerrar"; Logger de registrador estático privado; tamaño privado int; ConnectionParam ConnectionParam privado; privado arrayblockingqueue <Enection> idleconnectionQueue; Vector privado <Enection> BusyConnectionVector;
.ConnectionPoolFactory - Clase de gestión del grupo de conexión
ConnectionPoolFactory posee un concurrto statichashmap para almacenar objetos de piscina de conexión.
ConnectionPoolFactory permite crear múltiples grupos de conexión con diferentes configuraciones de diferentes bases de datos.
El desarrollador necesita registrar (vincular) el grupo de conexión con un nombre específico por primera vez y luego obtener la conexión del grupo de conexión especificado cada vez.
Si el grupo de conexión ya no está en uso, el desarrollador puede iniciar sesión (desabrochar) el grupo de conexión.
Mapa estático privado <String, ConnectionPool> PoolMap = nuevo ConcurrenthashMap <> (); Public Static Connection GetConnection (String PoolName) lanza SQLException {namecheck (piscina); ConnectionPool ConnectionPool = PoolMap.get (PoolName); return ConnectionPool.getConnection (); } public static void RegisterConnectionPool (nombre de cadena, ConnectionParam ConnectionParam) {RegisterCheck (nombre); PoolMap.put (nombre, nuevo ConnectionPool (ConnectionParam)); } // deja que GC publique estático void unregisterConnectionPool (nombre de cadena) {namecheck (nombre); ConnectionPool final ConnectionPool = PoolMap.get (nombre); Poolmap.remove (nombre); new Thread (new Runnable () {@Override public void run () {ConnectionPool.Clear ();}}). Start (); }Código central
El código central del grupo de conexión de la base de datos está en el método getConnection (). Por lo general, después de que el desarrollador haya procesado las operaciones de la base de datos, se llamará al método Close (). La conexión debe cerrarse y los recursos deben liberarse. En el grupo de conexión de la base de datos, cuando el usuario llama al método Close (), la conexión no debe cerrarse directamente, sino que debe volver a colocarlo en la piscina y reutilizar. Aquí, se utiliza el mecanismo de proxy dinámico Java. GetConnection no devuelve una conexión "real", sino una clase de proxy personalizada (la clase anónima se usa aquí). Cuando el usuario llama al método Close (), lo intercepta y lo vuelve a colocar en la piscina. Para el proxy dinámico, puede consultar otro blog "Java Dynamic Proxy Aplicación simple"
@Override Public Connection getConnection () lanza SQLException {try {final conexión de conexión = idleconnectionQueue.poll (ConnectionParam.getMaxWait (), TimeUnit.milliseConds); if (conexión == null) {logger.info (showymsg ()); EnsureCapacity (); regresar nulo; } BusyConnectionVector.Add (conexión); return (conexión) proxy.newproxyInstance (this.getClass (). getClassLoader (), nueva clase [] {Connection.Class}, New InvocationHandler () {@Override public Object Invoke (OBJET ProXy, Method Method, Object [] Args) lanza el lanzamiento de {Ife (! Method.getName (). EQUALS (EQUALS (COSE_METHOD)) args) } catch (InterruptedException e) {E.PrintStackTrace (); } return null; }2. Use
Primero, el usuario construye los parámetros del grupo de conexión de la base de datos (ConnectionParam), incluidos el controlador, la URL, el usuario, los elementos requeridos por la contraseña. Puede personalizar opciones como minconnection, maxconnection, etc. Si no está configurado, use el valor predeterminado del sistema. Este es el beneficio de usar Builder para construir una gran cantidad de atributos, incluidos los atributos requeridos y los atributos opcionales. Luego registre el grupo de conexión con ConnectionPoolFactory con un nombre específico y finalmente obtenga la conexión llamando al método de fábrica Static Staticy de ConnectionPoolFactory.
String Driver = "com.mysql.jdbc.driver"; String url = "jdbc: mysql: // localhost: 3306/test"; String user = "root"; Cadena contraseña = "root"; ConnectionParam ConnectionParam = new ConnectionParam.ConnectionParambuilder (controlador, URL, usuario, contraseña) .Build (); ConnectionPoolFactory.RegisterConnectionPool ("Test", ConnectionParam); Conexión Connection = ConnectionPoolFactory.getConnection ("Test");3. Código
.ParamConfiguración
paquete database.config; import java.io.Serializable;/** * Parámetros de conexión de base de datos * Creado por Michael Wong el 2016/1/18. */public class ParamConfiguration implementa serializable {public static final int min_connection = 5; public static final int max_connection = 50; Public estática final int min_idle = 5; Público estático final Long max_wait = 30000; ParamConfiguration privado () {}}.Constructor
base de datos de paquetes;/** * constructor * creado por Michael Wong el 2016/1/18. */Public Interface Builder <T> {t build ();}.ConnectionParam
Base de datos de paquetes; importar base de datos.config.paramconfiguration;/** * Parámetros de conexión de base de datos * Creados por Michael Wong el 2016/1/18. */public class ConnectionParam {controlador de cadena final privada; URL de cadena final privada; Usuario privado de cadena final; contraseña de cadena final privada; Private Final Int Minconnection; Private final int maxconnection; Private final int minidle; Private Final Long Maxwait; ConnectionParam privado (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 Long getMaxWait () {return this.maxwait; } public class static ConnectionParambuilder implementa el constructor <nectionParam> {// Parámetros requeridos controlador de cadena final privada; URL de cadena final privada; Usuario privado de cadena final; contraseña de cadena final privada; // Parámetros opcionales: inicializados al valor predeterminado private int minconnection = paramconfiguration.min_connection; private int maxconnection = paramconfiguration.max_connection; private int minidle = paramconfiguration.min_idle; // Tiempo de espera de conexión de conexión Private Long maxwait = paramconfiguration.max_wait; Public ConnectionParambuilder (controlador de cadena, url de cadena, user de cadena, contraseña de cadena) {this.driver = controlador; this.url = url; this.user = usuario; this.password = contraseña; } public ConnectionParambuilder minconnection (int minconnection) {this.minconnection = minconnection; devolver esto; } public ConnectionParambuilder maxconnection (int maxconnection) {this.maxconnection = maxconnection; devolver esto; } Public ConnectionParambuilder minidle (int minidle) {this.minidle = minidle; devolver esto; } Public ConnectionParambuilder maxWait (int maxwait) {this.maxwait = maxWait; devolver esto; } @Override public ConnectionParam Build () {return New ConnectionParam (this); }}}.ConnectionPool
paquete database.factory; importe database.connectionParam; import javax.sql.dataSource; import java.io.printwriter; import java.lang.reflect.invocationHandler; import java.lang.reflect.method; import java.lang.reflect.proxy; import java.sql.sql.setts; java.sql.drivermanager; import java.sql.sqlexception; import java.sql.sqlfeaturenotsupportedException; import java.util.vector; import java.util.concurrent.arrayblockqueue; import java.util.concurrent.timeunit; import java.util.logging.logger;/** * Grupo de conexión * creado por Michael Wong el 2016/1/18. */public class ConnectionPool implementa DataSource {private static final int inicial_size = 5; cadena final estática privada Close_method = "Cerrar"; Logger de registrador estático privado; tamaño privado int; ConnectionParam ConnectionParam privado; privado arrayblockingqueue <Enection> idleconnectionQueue; Vector privado <Enection> BusyConnectionVector; ConnectionPool protegido (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 inicialSize = inicial_size <minconnection? minconnection: inicial_size; prueba {class.forname (ConnectionParam.getDriver ()); for (int i = 0; i <inicialSize+ConnectionParam.getMinconnection (); i ++) {idleconnectionqueue.put (newConnection ()); tamaño ++; }} Catch (Exception e) {tire nuevo excepcionInInitializerError (e); }} @Override Public Connection getConnection () lanza SQLException {try {final conexión de conexión = idleconnectionQueue.poll (ConnectionParam.getMaxWait (), TimeUnit.MilliseConds); if (conexión == null) {logger.info (showymsg ()); EnsureCapacity (); regresar nulo; } BusyConnectionVector.Add (conexión); return (conexión) proxy.newproxyInstance (this.getClass (). getClassLoader (), nueva clase [] {Connection.Class}, New InvocationHandler () {@Override public Object Invoke (OBJET ProXy, Method Method, Object [] Args) lanza el lanzamiento de {Ife (! Method.getName (). EQUALS (EQUALS (COSE_METHOD)) args) } catch (InterruptedException e) {E.PrintStackTrace (); } return null; } Conexión privada NewConnection () lanza SQLException {String url = ConnectionParam.getUrl (); String user = ConnectionParam.getUser (); String Password = ConnectionParam.getPassword (); return drivermanager.getConnection (url, usuario, contraseña); } protegido int size () {size de retorno; } protegido int idleconnectionQuantity () {return idleconnectionqueue.size (); } protegido int } private void setureCapacity () lanza SQLException {int minidle = ConnectionParam.getminidle (); int maxConnection = ConnectionParam.getMaxConnection (); int newcapacity = size + minidle; NewCapacity = NewCapacity> MaxConnection? MaxConnection: NewCapacity; int growCount = 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; } protegido void clear () {try {while (size--> 0) {Connection Connection = idleconnectionqueue.take (); Connection.Close (); }} Catch (InterruptedException | SQLException e) {E.PrintStackTrace (); }} string private string shateMsg () {return "La base de datos está ocupada, por favor espere ..."; } @Override Public Connection getConnection (String UserName, String Password) lanza SQLException {return null; } @Override public PrintWriter getLogWriter () lanza SQLException {return null; } @Override public void setLogwriter (printWriter out) lanza SQLException {} @Override public void setLogInTimeOut (int segundos) lanza SQLException {} @Override public int getLogInTimeOut () lanza SQLException {return 0; } @Override public logger getParentLogger () lanza sqlfeatureNotsupportedException {return null; } @Override public <T> t Unwrap (clase <T> iface) lanza SQLException {return null; } @Override public boolean iswrapperfor (class <?> Iface) lanza SQLException {return false; }}.ConnectionPoolFactory
paquete database.factory; importe database.connectionParam; import java.sql.connection; import java.sql.sqlexception; import java.util.map; import java.util.concurrent.concurrententhashmap;/** * Factory del grupo de conexión * creado por Michael Wong en 2016/1/18. */public class ConnectionPoolFactory {private ConnectionPoolFactory () {} Mapa estático privado <String, ConnectionPool> PoolMap = new ConcurrentHashMap <> (); Public Static Connection getConnection (String PoolName) lanza SQLException {namecheck (piscina); ConnectionPool ConnectionPool = PoolMap.get (PoolName); return ConnectionPool.getConnection (); } public static void RegisterConnectionPool (nombre de cadena, ConnectionParam ConnectionParam) {RegisterCheck (nombre); PoolMap.put (nombre, nuevo ConnectionPool (ConnectionParam)); } // deja que GC publique estático void unregisterConnectionPool (nombre de cadena) {namecheck (nombre); ConnectionPool final ConnectionPool = PoolMap.get (nombre); Poolmap.remove (nombre); new 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 (nombre de cadena) {if (name == null) {Throw New IlegalArGumentException (nullName ()); }} private static void nameCheck (nombre de cadena) {if (name == null) {throw new IlegalArGumentException (nullName ()); } if (! PoolMap.ContainsKey (Nombre)) {arrojar una nueva IlleLArGumentException (NotExists (nombre)); }} cadena estática privada nullname () {return "El nombre del grupo no debe ser nulo"; } NotExists de cadena estática privada (nombre de cadena) {return "El grupo de conexión llamado" + name + "no existe"; }} 4. Prueba
Prueba de unidades de Junit
paquete database.factory; importe database.connectionParam; import org.junit.test; import java.sql.connection; import java.sql.sqlexception; import java.util.arrayList; import java.util.list; import og.junit.assert. *** *** ConnectionPoolfactory test* creado por Michael creó por Michael. 2016/1/20. */public class ConnectionPoolFactoryTest {@Test public void testgetConnection () lanza SQLException {String Driver = "com.mysql.jdbc.driver"; String url = "jdbc: mysql: // localhost: 3306/test"; String user = "root"; Cadena contraseña = "root"; ConnectionParam ConnectionParam = new ConnectionParam.ConnectionParambuilder (controlador, url, usuario, contraseña) .Build (); ConnectionPoolFactory.RegisterConnectionPool ("Test", ConnectionParam); List <Enection> ConnectionList = new ArrayList <> (); for (int i = 0; i <12; i ++) {ConnectionList.Add (ConnectionPoolFactory.getConnection ("Test")); } imprimir(); Cerrar (ConnectionList); imprimir(); ConnectionList.Clear (); for (int i = 0; i <12; i ++) {ConnectionList.Add (ConnectionPoolFactory.getConnection ("Test")); } imprimir(); Cerrar (ConnectionList); ConnectionPoolFactory.UnregisterConnectionPool ("Test"); } @Test (esperado = ilegalArGumentException.class) public void testException () {try {ConnectionPoolFactory.getConnection ("test"); } Catch (Sqlexception e) {E.PrintStackTrace (); }} private void Close (List <Enection> ConnectionList) lanza SQLException {for (Connection Conn: ConnectionList) {if (Conn! = NULL) {conn.close (); }}} private void print () {System.out.println ("Idle:" + ConnectionPoolFactory.getIdLeconnectionQuantity ("Test")); System.out.println ("ocupado:" + ConnectionPoolFactory.getBusyConnectionQuantity ("Test")); System.out.println ("Size:" + ConnectionPoolFactory.Size ("Test")); }}Lo anterior se trata de este artículo, espero que sea útil para el aprendizaje de todos.