一、数据库连接池
1.概念
数据库连接是一种关键的、有限的、昂贵的资源,这一点在多用户的WEB应用中体现得尤为突出。对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标。数据库连接池正是针对这个问题提出来的。
2.原理
数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,这些数据库连接的数量是由最小数据库连接数制约。无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量。连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中。数据库连接池的最小连接数和最大连接数的设置要考虑到下列几个因素:
1)最小连接数:
连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费。
2)最大连接数:
连接池能申请的最大连接数,如果数据库连接请求超过此数,后面的数据库连接请求将被加入到等待队列中,这会影响之 后的数据库操作。
3)最小连接数与最大连接数差距:
最小连接数与最大连接数相差太大,那么最先的连接请求将会获利,之后超过最小连接数量的连接请求等价于建立一个新 的数据库连接。不过,这些大于最小连接数的数据库连接在使用完不会马上被释放,它将被放到连接池中等待重复使用或 是空闲超时后被释放。
3.使用连接池(以C3P0为例)
在hibernate.cfg.xml配置
<!-- 指定hibernate使用连接池实现方,
不同的连接池有不同的实现,这里使用C3P0连接池 -->
<property name="hibernate.connection.provider_class">org.hibernate.c3p0.internal.C3P0ConnectionProvider</property>
<!-- 最大连接数 -->
<property name="hibernate.c3p0.max_size">20</property>
<!-- 最小连接数 -->
<property name="hibernate.c3p0.min_size">5</property>
<!-- 获得连接的超时时间,如果超过这个时间,会抛出异常,单位毫秒 -->
<property name="hibernate.c3p0.timeout">120</property>
<!-- 最大的PreparedStatement的数量 -->
<property name="hibernate.c3p0.max_statements">100</property>
<!-- 每隔120秒检查连接池里的空闲连接 ,单位是秒-->
<property name="hibernate.c3p0.idle_test_period">120</property>
<!-- 当连接池里面的连接用完的时候,C3P0一下获取的新的连接数 -->
<property name="hibernate.c3p0.acquire_increment">2</property>
<!-- 每次都验证连接是否可用 -->
1.概念
数据库连接是一种关键的、有限的、昂贵的资源,这一点在多用户的WEB应用中体现得尤为突出。对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标。数据库连接池正是针对这个问题提出来的。
2.原理
数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,这些数据库连接的数量是由最小数据库连接数制约。无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量。连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中。数据库连接池的最小连接数和最大连接数的设置要考虑到下列几个因素:
1)最小连接数:
连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费。
2)最大连接数:
连接池能申请的最大连接数,如果数据库连接请求超过此数,后面的数据库连接请求将被加入到等待队列中,这会影响之 后的数据库操作。
3)最小连接数与最大连接数差距:
最小连接数与最大连接数相差太大,那么最先的连接请求将会获利,之后超过最小连接数量的连接请求等价于建立一个新 的数据库连接。不过,这些大于最小连接数的数据库连接在使用完不会马上被释放,它将被放到连接池中等待重复使用或 是空闲超时后被释放。
3.使用连接池(以C3P0为例)
在hibernate.cfg.xml配置
<!-- 指定hibernate使用连接池实现方,
不同的连接池有不同的实现,这里使用C3P0连接池 -->
<property name="hibernate.connection.provider_class">org.hibernate.c3p0.internal.C3P0ConnectionProvider</property>
<!-- 最大连接数 -->
<property name="hibernate.c3p0.max_size">20</property>
<!-- 最小连接数 -->
<property name="hibernate.c3p0.min_size">5</property>
<!-- 获得连接的超时时间,如果超过这个时间,会抛出异常,单位毫秒 -->
<property name="hibernate.c3p0.timeout">120</property>
<!-- 最大的PreparedStatement的数量 -->
<property name="hibernate.c3p0.max_statements">100</property>
<!-- 每隔120秒检查连接池里的空闲连接 ,单位是秒-->
<property name="hibernate.c3p0.idle_test_period">120</property>
<!-- 当连接池里面的连接用完的时候,C3P0一下获取的新的连接数 -->
<property name="hibernate.c3p0.acquire_increment">2</property>
<!-- 每次都验证连接是否可用 -->
<property name="hibernate.c3p0.validate">true</property>
二、代码实现连接池
ConnectionInvocationHandler类
package org.nf.pool;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.LinkedList;
/**
* 回调处理器,这个处理器可以在执行目标对象也就是
* Connection对象里的任意一个方法时,可以在执行的
* 之前或者之后做一些事情
* @author xxf
*/
public class ConnectionInvocationHandler implements InvocationHandler{
//被代理的对象
private Connection conn;
//计数器,在放回连接池的时候必须要递减
private int num;
//连接池
private LinkedList<Connection> pool;
//构造方法
public ConnectionInvocationHandler(Connection conn, int num, LinkedList<Connection> pool) {
this.conn = conn;
this.num = num;
this.pool = pool;
}
//回调方法,目的就是调用目标对象的方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//判断当前调用的方法是否是close方法,如果是则将连接放回连接池
//而不是释放连接,这样就可以达到连接的重复利用
if("close".equals(method.getName())){
pool.addLast(conn);
//归还连接后,将计数器减1️
num--;
return null;
}else{
//否则,其他方法将正常放行调用
return method.invoke(conn, args);
}
}
}
ConnectionPool类:
package org.nf.pool; import java.sql.Connection; import java.util.LinkedList; /** * 连接池,池可以使用集合来充当 * 注意:从连接池获取的连接必须经过代理, * 否则将无法监听用户对Connection对象的操作 * @author xxf * */ public class ConnectionPool { //连接池 private LinkedList<Connection> pool = new LinkedList<>(); //最小连接数 private int minSize; //最大连接数 private int maxSize; //连接计数器 private int num; public void setMinSize(int minSize) { this.minSize = minSize; } public void setMaxSize(int maxSize) { this.maxSize = maxSize; } //初始化连接池,连接池中存放最小连接数 public void initPool(){ for(int i=0; i< minSize; i++){ pool.add(DBUtil.getConnection()); } } //从连接池获取连接,然后将连接生成一个代理对象返回给用户使用 public Connection getConnection(){ //先判断池中是否还有连接 if(pool.size() > 0){ Connection conn = pool.removeFirst(); //从连接池中获取一个连接,就要做一次记录 num++; //注意,这里返回的conn对象就是已经被代理过的了 conn = ConnectionProxy.proxy(conn, num, pool); return conn; }else{ if(num < maxSize){ pool.add(DBUtil.getConnection()); }else{ System.out.println("已达到连接池上限"); } return null; } } //连接池大小 public int size(){ return pool.size(); } }
DBUtils连接数据库类
package org.nf.pool; import java.sql.Connection; import java.sql.DriverManager; public class DBUtil { private static String driver = "com.mysql.jdbc.Driver"; private static String url = "jdbc:mysql://localhost:3306/book?useUnicode=true&characterEncoding=UTF-8"; private static String user = "root"; private static String password = "root"; static { try{ Class.forName(driver); }catch(Exception e){ e.printStackTrace(); } } public static Connection getConnection(){ Connection conn = null; try{ conn = DriverManager.getConnection(url, user, password); }catch(Exception e){ e.printStackTrace(); } return conn; } }
ConnectionProxy类:
package org.nf.pool; import java.lang.reflect.Proxy; import java.sql.Connection; import java.util.LinkedList; /** * 连接对象的代理类,为每一个连接对象创建一个代理 * @author xxf * */ public class ConnectionProxy { //这个方法将为connection对象创建代理 //使用JDK自带的动态代理机制 static Connection proxy(Connection conn, int num, LinkedList<Connection> pool){ //创建一个回调处理器 ConnectionInvocationHandler handler = new ConnectionInvocationHandler(conn, num, pool); //创建代理对象,这个代理对象是在运行时动态创建的,同时也实现了Connection接口 conn = (Connection)Proxy.newProxyInstance(ConnectionProxy.class.getClassLoader(), new Class[]{Connection.class}, handler); return conn; } }
Test类:
package org.nf.pool.test; import java.sql.Connection; import java.sql.SQLException; import org.nf.pool.ConnectionPool; public class Test { public static void main(String[] args) throws SQLException { //创建一个连接池 ConnectionPool pool = new ConnectionPool(); //设置最小连接数 pool.setMinSize(5); //设置最大连接数 pool.setMaxSize(10); //初始化 pool.initPool(); //从连接池中获取连接 Connection c1 = pool.getConnection(); System.out.println("连接池剩余:"+pool.size()); Connection c2 = pool.getConnection(); System.out.println("连接池剩余:"+pool.size()); //由于Connection对象是经过代理的,也就是被监听中 //一旦调用了close方法,会自动将连接放回连接池 c1.close(); c2.close(); System.out.println("连接池剩余:"+pool.size()); } }