|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
在ruby里才是一切皆对象。当然我不并不是很了解ruby,但是ruby确实是将语法简化得很好。静态|数据|数据库|数据库毗连数据库毗连池在编写使用服务是常常必要用到的模块,太甚频仍的毗连数据库对服务功能来说是一个瓶颈,利用缓冲池手艺能够来打消这个瓶颈。我们能够在互联网上找到良多关于数据库毗连池的源程序,可是都发明如许一个配合的成绩:这些毗连池的完成办法都分歧水平地增添了与利用者之间的耦合度。良多的毗连池都请求用户经由过程其划定的办法猎取数据库的毗连,这一点我们能够了解,究竟今朝一切的使用服务器取数据库毗连的体例都是这类体例完成的。可是别的一个配合的成绩是,它们同时不同意利用者显式的挪用Connection.close()办法,而必要用其划定的一个办法来封闭毗连。这类做法有两个弱点: 第一:改动了用户利用习气,增添了用户的利用难度。
起首我们来看看一个一般的数据库操纵历程:
intexecuteSQL(Stringsql)throwsSQLException
{
Connectionconn=getConnection();//经由过程某种体例猎取数据库毗连
PreparedStatementps=null;
intres=0;
try{
ps=conn.prepareStatement(sql);
res=ps.executeUpdate();
}finally{
try{
ps.close();
}catch(Exceptione){}
try{
conn.close();//
}catch(Exceptione){}
}
returnres;
}
利用者在用完数据库毗连后一般是间接挪用毗连的办法close来开释数据库资本,假如用我们后面提到的毗连池的完成办法,那语句conn.close()将被某些特定的语句所替换。
第二:使毗连池没法对当中的一切毗连举行独有把持。因为毗连池不同意用户间接挪用毗连的close办法,一旦利用者在利用的过程当中因为习气成绩间接封闭了数据库毗连,那末毗连池将没法一般保护一切毗连的形态,思索毗连池和使用由分歧开辟职员完成时这类成绩更简单呈现。
综合下面提到的两个成绩,我们来会商一下怎样办理这两个要命的成绩。
起首我们先设身处地的思索一下用户是想怎样来利用这个数据库毗连池的。用户能够经由过程特定的办法来猎取数据库的毗连,同时这个毗连的范例应当是尺度的java.sql.Connection。用户在猎取到这个数据库毗连后能够对这个毗连举行恣意的操纵,包含封闭毗连等。
经由过程对用户利用的形貌,如何能够接受Connection.close办法就成了我们这篇文章的主题。
为了接受数据库毗连的close办法,我们应当有一品种似于钩子的机制。比方在Windows编程中我们能够使用HookAPI来完成对某个WindowsAPI的接受。在JAVA中一样也有如许一个机制。JAVA供应了一个Proxy类和一个InvocationHandler,这两个类都在java.lang.reflect包中。我们先来看看SUN公司供应的文档是怎样形貌这两个类的。
publicinterfaceInvocationHandler
InvocationHandleristheinterfaceimplementedbytheinvocationhandlerofaproxyinstance.
Eachproxyinstancehasanassociatedinvocationhandler.
Whenamethodisinvokedonaproxyinstance,
themethodinvocationisencodedanddispatchedtotheinvokemethodofitsinvocationhandler.
SUN的API文档中关于Proxy的形貌良多,这里就不排列出来。经由过程文档对接口InvocationHandler的形貌我们能够看到当挪用一个Proxy实例的办法时会触发Invocationhanlder的invoke办法。从JAVA的文档中我们也同时懂得到这类静态代办署理机制只能接受接口的办法,而对一样平常的类有效,思索到java.sql.Connection自己也是一个接口由此就找到懂得决怎样接受close办法的前途。
起首,我们先界说一个数据库毗连池参数的类,界说了数据库的JDBC驱动程序类名,毗连的URL和用户名口令等等一些信息,该类是用于初始化毗连池的参数,详细界说以下:
publicclassConnectionParamimplementsSerializable
{
privateStringdriver;//数据库驱动程序
privateStringurl;//数据毗连的URL
privateStringuser;//数据库用户名
privateStringpassword;//数据库暗码
privateintminConnection=0;//初始化毗连数
privateintmaxConnection=50;//最年夜毗连数
privatelongtimeoutValue=600000;//毗连的最年夜余暇工夫
privatelongwaitTime=30000;//取毗连的时分假如没有可用毗连最年夜的守候工夫
其次是毗连池的工场类ConnectionFactory,经由过程该类来将一个毗连池对象与一个称号对应起来,利用者经由过程该称号就能够猎取指定的毗连池对象,详细代码以下:
/**
*毗连池类厂,该类经常使用来保留多个数据源称号合数据库毗连池对应的哈希
*@authorliusoft
*/
publicclassConnectionFactory
{
//该哈希表用来保留数据源名和毗连池对象的干系表
staticHashtableconnectionPools=null;
static{
connectionPools=newHashtable(2,0.75F);
}
/**
*从毗连池工场中猎取指命名称对应的毗连池对象
*@paramdataSource毗连池对象对应的称号
*@returnDataSource前往称号对应的毗连池对象
*@throwsNameNotFoundException没法找到指定的毗连池
*/
publicstaticDataSourcelookup(StringdataSource)
throwsNameNotFoundException
{
Objectds=null;
ds=connectionPools.get(dataSource);
if(ds==null||!(dsinstanceofDataSource))
thrownewNameNotFoundException(dataSource);
return(DataSource)ds;
}
/**
*将指定的名字和数据库毗连设置绑定在一同并初始化数据库毗连池
*@paramname对应毗连池的称号
*@paramparam毗连池的设置参数,详细请见类ConnectionParam
*@returnDataSource假如绑定乐成后前往毗连池对象
*@throwsNameAlreadyBoundException必定名字name已绑定章抛出该非常
*@throwsClassNotFoundException没法找到毗连池的设置中的驱动程序类
*@throwsIllegalAccessException毗连池设置中的驱动程序类有误
*@throwsInstantiationException没法实例化驱动程序类
*@throwsSQLException没法一般毗连指定的数据库
*/
publicstaticDataSourcebind(Stringname,ConnectionParamparam)
throwsNameAlreadyBoundException,ClassNotFoundException,
IllegalAccessException,InstantiationException,SQLException
{
DataSourceImplsource=null;
try{
lookup(name);
thrownewNameAlreadyBoundException(name);
}catch(NameNotFoundExceptione){
source=newDataSourceImpl(param);
source.initConnection();
connectionPools.put(name,source);
}
returnsource;
}
/**
*从头绑定命据库毗连池
*@paramname对应毗连池的称号
*@paramparam毗连池的设置参数,详细请见类ConnectionParam
*@returnDataSource假如绑定乐成后前往毗连池对象
*@throwsNameAlreadyBoundException必定名字name已绑定章抛出该非常
*@throwsClassNotFoundException没法找到毗连池的设置中的驱动程序类
*@throwsIllegalAccessException毗连池设置中的驱动程序类有误
*@throwsInstantiationException没法实例化驱动程序类
*@throwsSQLException没法一般毗连指定的数据库
*/
publicstaticDataSourcerebind(Stringname,ConnectionParamparam)
throwsNameAlreadyBoundException,ClassNotFoundException,
IllegalAccessException,InstantiationException,SQLException
{
try{
unbind(name);
}catch(Exceptione){}
returnbind(name,param);
}
/**
*删除一个数据库毗连池对象
*@paramname
*@throwsNameNotFoundException
*/
publicstaticvoidunbind(Stringname)throwsNameNotFoundException
{
DataSourcedataSource=lookup(name);
if(dataSourceinstanceofDataSourceImpl){
DataSourceImpldsi=(DataSourceImpl)dataSource;
try{
dsi.stop();
dsi.close();
}catch(Exceptione){
}finally{
dsi=null;
}
}
connectionPools.remove(name);
}
}
ConnectionFactory次要供应了用户将将毗连池绑定到一个详细的称号上和作废绑定的操纵。利用者只必要体贴这两个类便可利用数据库毗连池的功效。上面我们给出一段怎样利用毗连池的代码:
Stringname="pool";
Stringdriver="sun.jdbc.odbc.JdbcOdbcDriver";
Stringurl="jdbc:odbc:datasource";
ConnectionParamparam=newConnectionParam(driver,url,null,null);
param.setMinConnection(1);
param.setMaxConnection(5);
param.setTimeoutValue(20000);
ConnectionFactory.bind(name,param);
System.out.println("binddatasourceok.");
//以上代码是用来挂号一个毗连池对象,该操纵能够在程序初始化只做一次便可
//以下入手下手就是利用者真正必要写的代码
DataSourceds=ConnectionFactory.lookup(name);
try{
for(inti=0;i<10;i++){
Connectionconn=ds.getConnection();
try{
testSQL(conn,sql);
}finally{
try{
conn.close();
}catch(Exceptione){}
}
}
}catch(Exceptione){
e.printStackTrace();
}finally{
ConnectionFactory.unbind(name);
System.out.println("unbinddatasourceok.");
System.exit(0);
}
从利用者的示例代码就能够看出,我们已办理了惯例毗连池发生的两个成绩。可是我们最最体贴的是怎样办理接受close办法的举措。接受事情次要在ConnectionFactory中的两句代码:
source=newDataSourceImpl(param);
source.initConnection();
DataSourceImpl是一个完成了接口javax.sql.DataSource的类,该类保护着一个毗连池的对象。因为该类是一个受回护的类,因而它表露给利用者的办法只要接口DataSource中界说的办法,其他的一切办法对利用者来讲都是不成视的。我们先来体贴用户可会见的一个办法getConnection
/**
*@seejavax.sql.DataSource#getConnection(String,String)
*/
publicConnectiongetConnection(Stringuser,Stringpassword)throwsSQLException
{
//起首从毗连池中找出余暇的对象
Connectionconn=getFreeConnection(0);
if(conn==null){
//判别是不是凌驾最年夜毗连数,假如凌驾最年夜毗连数
//则守候必定工夫检察是不是有余暇毗连,不然抛出非常告知用户无可用毗连
if(getConnectionCount()>=connParam.getMaxConnection())
conn=getFreeConnection(connParam.getWaitTime());
else{//没有凌驾毗连数,从头猎取一个数据库的毗连
connParam.setUser(user);
connParam.setPassword(password);
Connectionconn2=DriverManager.getConnection(connParam.getUrl(),
user,password);
//代办署理将要前往的毗连对象
_Connection_conn=new_Connection(conn2,true);
synchronized(conns){
conns.add(_conn);
}
conn=_conn.getConnection();
}
}
returnconn;
}
/**
*从毗连池中取一个余暇的毗连
*@paramnTimeout假如该参数值为0则没有毗连时只是前往一个null
*不然的话守候nTimeout毫秒看是不是另有余暇毗连,假如没有抛出非常
*@returnConnection
*@throwsSQLException
*/
protectedsynchronizedConnectiongetFreeConnection(longnTimeout)
throwsSQLException
{
Connectionconn=null;
Iteratoriter=conns.iterator();
while(iter.hasNext()){
_Connection_conn=(_Connection)iter.next();
if(!_conn.isInUse()){
conn=_conn.getConnection();
_conn.setInUse(true);
break;
}
}
if(conn==null&&nTimeout>0){
//守候nTimeout毫秒以便看是不是有余暇毗连
try{
Thread.sleep(nTimeout);
}catch(Exceptione){}
conn=getFreeConnection(0);
if(conn==null)
thrownewSQLException("没有可用的数据库毗连");
}
returnconn;
}
DataSourceImpl类中完成getConnection办法的跟一般的数据库毗连池的逻辑是分歧的,起首判别是不是有余暇的毗连,假如没有的话判别毗连数是不是已凌驾最年夜毗连数等等的一些逻辑。可是有一点分歧的是经由过程DriverManager失掉的数据库毗连并非实时前往的,而是经由过程一个叫_Connection的类中介一下,然后挪用_Connection.getConnection前往的。假如我们没有经由过程一其中介也就是JAVA中的Proxy来接受要前往的接口对象,那末我们就没有举措截住Connection.close办法。
终究到了中心地点,我们先来看看_Connection是怎样完成的,然后再先容是客户端挪用Connection.close办法时走的是如何一个流程,为何并没有真实的封闭毗连。
/**
*数据毗连的自封装,屏障了close办法
*@authorLiudong
*/
class_ConnectionimplementsInvocationHandler
{
privatefinalstaticStringCLOSE_METHOD_NAME="close";
privateConnectionconn=null;
//数据库的忙形态
privatebooleaninUse=false;
//用户最初一次会见该毗连办法的工夫
privatelonglastAccessTime=System.currentTimeMillis();
_Connection(Connectionconn,booleaninUse){
this.conn=conn;
this.inUse=inUse;
}
/**
*Returnstheconn.
*@returnConnection
*/
publicConnectiongetConnection(){
//前往数据库毗连conn的接受类,以便截住close办法
Connectionconn2=(Connection)Proxy.newProxyInstance(
conn.getClass().getClassLoader(),
conn.getClass().getInterfaces(),this);
returnconn2;
}
/**
*该办法真实的封闭了数据库的毗连
*@throwsSQLException
*/
voidclose()throwsSQLException{
//因为类属性conn是没有被接受的毗连,因而一旦挪用close办法后就间接封闭毗连
conn.close();
}
/**
*ReturnstheinUse.
*@returnboolean
*/
publicbooleanisInUse(){
returninUse;
}
/**
*@seejava.lang.reflect.InvocationHandler#invoke(java.lang.Object,java.lang.reflect.Method,java.lang.Object)
*/
publicObjectinvoke(Objectproxy,Methodm,Object[]args)
throwsThrowable
{
Objectobj=null;
//判别是不是挪用了close的办法,假如挪用close办法则把毗连置为无用形态
if(CLOSE_METHOD_NAME.equals(m.getName()))
setInUse(false);
else
obj=m.invoke(conn,args);
//设置最初一次会见工夫,以便实时扫除超时的毗连
lastAccessTime=System.currentTimeMillis();
returnobj;
}
/**
*ReturnsthelastAccessTime.
*@returnlong
*/
publiclonggetLastAccessTime(){
returnlastAccessTime;
}
/**
*SetstheinUse.
*@paraminUseTheinUsetoset
*/
publicvoidsetInUse(booleaninUse){
this.inUse=inUse;
}
}
一旦利用者挪用所失掉毗连的close办法,因为用户的毗连对象是经由接受后的对象,因而JAVA假造时机起首挪用_Connection.invoke办法,在该办法中起首判别是不是为close办法,假如不是则将代码转给真实的没有被接受的毗连对象conn。不然的话只是复杂的将该毗连的形态设置为可用。到此您大概就分明了全部接受的历程,可是同时也有一个疑问:如许的话是否是这些已创建的毗连就一直没有举措真正封闭?谜底是能够的。我们来看看ConnectionFactory.unbind办法,该办法起首找到名字对应的毗连池对象,然后封闭该毗连池中的一切毗连并删撤除毗连池。在DataSourceImpl类中界说了一个close办法用来封闭一切的毗连,具体代码以下:
/**
*封闭该毗连池中的一切数据库毗连
*@returnint前往被封闭毗连的个数
*@throwsSQLException
*/
publicintclose()throwsSQLException
{
intcc=0;
SQLExceptionexcp=null;
Iteratoriter=conns.iterator();
while(iter.hasNext()){
try{
((_Connection)iter.next()).close();
cc++;
}catch(Exceptione){
if(einstanceofSQLException)
excp=(SQLException)e;
}
}
if(excp!=null)
throwexcp;
returncc;
}
该办法逐一挪用毗连池中每一个对象的close办法,这个close办法对应的是_Connection中对close的完成,在_Connection界说中封闭数据库毗连的时分是间接挪用没有经由接受的对象的封闭办法,因而该close办法真实的开释了数据库资本。
以上笔墨只是形貌了接口办法的接受,详细一个有用的毗连池模块还必要对余暇毗连的监控并实时开释毗连。
而学习JAVA我觉得最应该避免的就是:只学习,不思考,只记忆,不实践! |
|