JDBC连接池设计--Mysql

时间:2022-09-19 23:35:30

JDBC连接数据库,并非是一件轻松的差事。数据库连接不仅仅在应用服务器和数据库服务器之间建立Socket Connection,还需要交换若干次数据用来验证等,以及系统资源和进程,日志的处理,所以数据库连接池在设计应用中是不可忽视的!缓解系统很大的压力!

思考:在创建好Connection之后,我们会习惯性地在用完后connection后,调用它的close()方法,有什么好的设计方法使其在调用close()方法和数据库连接池挂钩,即判断当前连接是否达到最大值?如果是,直接关闭,反之-将其加入数据库连接池中,待后续再用!

1.数据库连接池类DBConnection

public class DBPool {
//容器--装Connection
private static Vector<Connection> vector;
//设置的 连接池----最大连接数
private final int POOL_MAX_Size = 20;
//获取一个Connection实例
public synchronized Connection getConnection() throws Exception{
Connection con;
if(vector == null){
vector = new Vector<Connection>();
}
if(vector.isEmpty()){
con = createConnection();
}else{
int index = vector.size()-1;
//从连接池-- 获取一个Connection
con = vector.get(index);
//连接池--移除 被使用的Connection
vector.remove(index);
}
return con;
}
//处理一个Connection用完后的逻辑
public synchronized void releaseConnection(Connection con) throws Exception{
//连接池容器--是否满了
if(vector.size() == POOL_MAX_Size){
try {
//直接关闭连接
con.close();
} catch (SQLException e) {
throw new Exception("When closing a Connection throws exception");
}
}else{
//没有满,将该Connection放入连接池,后续再用
vector.add(con);
}
}

//创建一个Connection连接
private static Connection createConnection() throws Exception{
Connection con;
try{
Class.forName("com.mysql.jdbc.Driver");
con = DriverManager.getConnection("jdbc:mysql://192.168.125.107:3306/naladb?autoReconnect=true&useUnicode=true&characterEncoding=UTF8","naladb","nalanala");
} catch(ClassNotFoundException ex){
throw new Exception("ClassNotFoundException when loading jdbc driver");
} catch(SQLException ex){
throw new Exception("SQLException when loading jdbc driver");
}
return con;
}
}

=====在此,数据库连接池 基本写好了(当然是简单的,没有考虑连接时间处理等,但是这是 核心思想),那么在用完Connection后,我们可以选择调用DBPolol的 releaseConnection(Connection con)来处理,但是 上面" 思考",提到我们 习惯性是调用Connection的close()方法,这样看上去很 简洁规范(PreparedStatement,ResultSet也是调用close()来关闭连接)

肿麽办?就应该想到一些设计模式,有装饰,动态代理等,在这采用动态代理来完美处理这一问题!

动态代理----->三个前提:

1.接口:Connection  就是一个接口啊,有了。

2.接口的实现类:也有啊,上面的createConnection()不就得到一个实现了connection接口的类的实例么!ok

3.InvocationHandler接口的实现类:自己新创建一个类来实现该接口不就好了?也行


public class ConnectionHandler implements InvocationHandler {
//Connection的实例
private Connection con;
//数据库连接池----(一般一个数据库使用一个连接池)
private DBPool dbpool;

public ConnectionHandler(DBPool dbPool){
this.dbpool = dbPool;
}
//绑定当前的Connection实例
public Connection bind(Connection con){
this.con = con;
//创建Connection的代理对象
Connection proxyCon = (Connection) Proxy.newProxyInstance(con.getClass().getClassLoader(), con.getClass().getInterfaces(), this);
return proxyCon;
}
/**
* 动态代理的类---class sun.proxy.$Proxy0 实现了:Connection接口,也继承了实现该接口的类,所以它有真实对象con 所有的方法!
*/
@Override
public Object invoke(Object object, Method method, Object[] args)
throws Throwable {
Object obj = null;
//如果 执行的是:close()方法
if("close".equals(method.getName())){
//调用数据库连接池的releaseConnection(Connection con)方法
this.dbpool.releaseConnection(con);
}else{
//否则,调用自己的方法----这里反射机制,调用真实的Connection的实例con,而不是Proxy0代理对象
obj = method.invoke(con, args);
}
return obj;
}

}

当然也要修改连接池DBPool中的getConnection()方法里面内容-----》仅仅修改了这两句话

     //创建Connection的代理机制
     ConnectionHandler connectionHandler = new ConnectionHandler(this);
   //返回con代理对象
     return connectionHandler.bind(con);


public synchronized Connection getConnection() throws Exception{
Connection con;
if(vector == null){
vector = new Vector<Connection>();
}
if(vector.isEmpty()){
con = createConnection();
}else{
int index = vector.size()-1;
//从连接池-- 获取一个Connection
con = vector.get(index);
//连接池--移除 被使用的Connection
vector.remove(index);
}
//创建Connection的代理机制
ConnectionHandler connectionHandler = new ConnectionHandler(this);
//返回con代理对象
return connectionHandler.bind(con);
}

测试类:------

public class JDBCPoolJunit {
public static void main(String[] args) throws Exception {
DBPool dbPool = new DBPool();
Connection conProxy = dbPool.getConnection();
System.out.println("================================");
System.out.println(conProxy);
System.out.println(conProxy.getClass());
System.out.println("===============================");
conProxy.close();
long timeStart = new Date().getTime();
PreparedStatement statement = conProxy.prepareStatement("select t.id from trade t,user u where t.buyer_id=u.id and t.date_created>'2013-01-01 00:00:00' and u.username like '%qq%'");
ResultSet re = statement.executeQuery();
long timeEnd = new Date().getTime();
System.out.println((timeEnd-timeStart)/1000.0+"秒");
}
}

结果:

================================
com.mysql.jdbc.Connection@a3d4cf ---->代理对象  如果我们打印System(con)结果也是:com.mysql.jdbc.Connection@a3d4cf,但

是它的类:class com.mysql.jdbc.Connection


class sun.proxy.$Proxy0------>代理类
===============================
2.991秒

根据结果知道:调用close()方法后,该连接并没有关闭,还可以进行sql语句查询,这就是动态代理的好处!  我们要 认识  代理类被代理类关系区别