JDBC运作方式
对于JDBC Driver来说,连接数据库却并非一件轻松差事。数据库连接不仅仅是在应用服务器与数据库服务器之间建立一个Socket Connection,连
接建立之后,应用服务器和数据库服务器之间还需要交换若干次数据(验证用户密码、权限等),然后,数据库开始化连接会话句柄,记录联机日志,
为此连接分配相应的处理进程和系统资源
数据库连接池的基本原理是在内部对象池中维护一定数量的数据库连接,并对外暴露数据库连接获取和返回方法。外部使用都可通过getConnection方
法获取连接,使用完毕后再通过releaseConnection方法将连接返回,注意此时连接并没有关闭,而是由连接池管理器回收,并为下一次使用做好准备。
一个最小化的数据库连接池实现:
/**/
/**
*获取数据库连接,如果当前池中有可用连接,则将池中最后一个返回,如果没有,则新建一个返回
*/
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
public
synchronized Connection getConnection()throws DBException
...
{
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
if(pool==null)...{
pool=new Vector();
}
Connection conn;
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
if(pool.isEmpty())...{
conn=createConnection();
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
}else...{
int last_idx=pool.size()-1;
conn=(Connection)pool.get(last_idx);
pool.remove(pool.get(last_idx));
}
return conn;
}
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
/**/
/**
*将使用完毕的数据库连接池放回备用池
*判断当前池中连接数是否已经超过阈值(POOL_MAX_SIZE)如果超,则关闭该连接,否则施加池中以备下次重用
*/
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
public
synchronized
void
releaseConnection(Connection conn)
...
{
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
if(pool.size()>POOL_MAX_SIZE)...{
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
try...{
conn.close();
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
}catch(SQLException e)...{
e.printStackTrace();
}
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
}else...{
pool.add(conn);
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
/**/
/**
* 读取数据库配置信息,并从数据库连接池中获得数据库连接
* @return
* @throws DBException
*/
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
priavte
static
Connection createConnection()throws DBException
...
{
Connection conn;
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
try...{
Class.forName("oracle.jdbc.driver.OracleDriver");
conn=DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:oracle","personal","personal");
return conn;
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
}catch(ClassNotFoundException e)...{
throw new DBException("ClassNotFounDException when loading JDBC Driver");
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
}catch(SQLException e)...{
throw new DBException("SQLException when loading JDBC Driver");
}
}
}
先脱离连接池本身的具体实现,我们看看这段代码在实际应用中可能产生的问题,注意到,由于getConnection方法返回的是一个标准的JDBC
Connection,程序员由于编程习惯,可能会习惯性的调用其close方法关闭连接。如此一来,连接无法得到重用,数据库连接池机制开同虚设。为了
解决这个问题,比较好的途径有:
1。 Decorator模式
2。 Dynamic Proxy模式
Decorator模式
“Decorator模式的主要目的是利用一个对象,透明地为另一个对象添加新的功能”,简单来说,就是通过一个Decorator对原有对象进行封装
,同时实现与原有对象相同的接口,从而得到一个基于原有对象的,对既有接口的增强性实现。
对于前面讨论的Connection释放的问题,理所当然,我们首先想到的是,如果能让JDBC Connection在执行close操作时自动将自己返回到
数据库连接池中。但是,JDBC Connection自己显然无法根据实际情况判断何去何从。此时,引入Decorator模式来解决我们所面对的问题:
public
class
ConnectionDecorator
implements
Connection
...
{
Connection dbConn;
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public ConnectionDecorator(Connection conn)...{
this.dbConn=conn;
}
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public void close()throws SQLException...{
this.dbConn.close();
}
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public void commit()throws SQLException...{
this.dbConn.commit();//调用实际连接的commit方法
}
}
目标很清楚,通过这样的封装,我们的ConnectionDecorator对于外部的程序员而言,调用方法与普通的JDBC Connection完全相同,而在
内部通过对ConnectionDecorator的修改,我们就可以透明地改变现有实现,为之增加新特性:
public
class
PooledConnection
extends
ConnectionDecorator
implements
Connection
...
{
private ConnectionPool connPool;
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public PooledConnection(ConnectionPool pool,Connection conn)...{
super(conn);
connPool=pool;
}
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public void close()throws SQLException...{
connPool.releaseConnection(this.dbconn);
}
}
为了应用新的PooledConnection,我们需要对原本的DBConnectionPool.getConnection和releaseConnection方法稍做改造:
public
synchronized
Connection getConnection()
throws
DBException
...
{
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
if(pool==null)...{
poo=new Vector();
}
Connection conn;
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
if(pool.isEmpty())...{
conn=createConnection();
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
}else...{
int last_idx=pool.size()-1;
conn=(Connection)pool.get(last_idx);
pool.remove(pool.get(last_idx);
}
return new PooledConnection(this,conn);//就这不同啦
}
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
public
synchronized
void
releaseConnection(Connection conn)
...
{
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
if(conn instanceof PooledConnection||pool.size()>POOL_MAX_SIZE)...{
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
try...{
conn.close();
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
}catch(SQLException e)...{
e.printStackTrace();
}
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
}else...{
pool.add(conn);
}
}
Dynamic Proxy模式
我们的目标是引入数据库连接池机制的同时,保持JDBC Connection对外接口不变。前面通过Decorator模式实现了这一目标,但由于
Decorator模式要求实现与目标对象一致的接口,Connection接口中定义的方法众多,我们也只能照单全收。
Dynamic Proxy模式则良好地解决了这一问题。通过实现一个绑定到Connection对象的InvocationHandler接口实现,我们可以在
Connection.close方法被调用时将其截获,并以我们自己实现的close方法将其替代,使连接返回到数据库连接池等待下次重用,而不是直接关
闭。
下面是ConnectionHandler类,它实现了InvocationHandler接口,按照Dynamic Proxy机制的定义,invoke方法将截获所有代理对象的
方法调用操作,这里我们通过invoke方法截获close并进行处理:
public
class
ConnectionHandler
implements
InvocationHandler
...
{
Connection dbconn;
ConnectionPool pool;
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public ConnectionHandler(ConnectionPool connPool)...{
this.pool=connPool;
}
//将动态代理绑定到指定Connection
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public Connection bind(Connection conn)...{
this.dbconn=conn;
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
//绑定到Connection对象的InvocationHandler接口实现(我的理解,可能有误)
Connection proxyConn=(Connection)Proxy.newProxyInstance(conn.getClass().getClassLoader(),
conn.getClass().getInterfaces(),this);
return proxyConn;
}
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
/** *//**
*方法调用拦截器
*判断当前调用的方法是否“close”方法,如是,调用pool.releaseConnection方法作为标准close方法的替代
*/
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public Object invoke(Object proxy,Method method,Object[] args)throws Throwable...{
Object obj=null;
//如果调用的是close方法,则用pool.releaseConnection方法将其替换
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
if("close".equals(method.getName)))...{
pool.releaseConnection(dbconn);
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
}else...{
obj=method.invoke(dbconn,args);
}
return obj;
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
配合Dynamic Proxy模式,我们的DBConnectionPool.getConnection方法也做了一点小小的修改:
public
synchronized
Connection getConnection()
throws
DBException
...
{
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
if(pool==null)...{
pool=new Vector();
}
Connection conn;
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
if(pool.isEmpty())...{
conn=createConnection();
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
}else...{
int last_idx=pool.size()-1;
conn=(Connection)pool.get(last_idx);
pool.remove(pool.get(last_idx);
}
ConnectionHandler connHandler=new ConnectionHandler(this);
return connHandler.bind(conn);
}
摘自:《深入浅出Hibernate》