|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
你希望java的IDE整合。这个是没有必要的,重要的是你理解java有多深以及怎么组织你的代码,即使没有IDE,代码照样能够编译运行的。servlet|会见|数据|数据库
JavaServlet作为首选的服务器端数据处置手艺,正在敏捷代替CGI剧本。Servlet超出CGI的上风之一在于,不但多个哀求能够共享公用资本,并且还能够在分歧用户哀求之间保存延续数据。本文先容一种充实发扬该特征的有用手艺,即数据库毗连池。
1、完成毗连池的意义
静态Web站点常常用数据库存储的信息天生Web页面,每个页面哀求招致一次数据库会见。毗连数据库不但要开支必定的通信和内存资本,还必需完成用户考证、平安高低文设置这类义务,因此常常成为最为耗时的操纵。固然,实践的毗连工夫开支一成不变,但1到2秒提早并不是不罕见。假如某个基于数据库的Web使用只需创建一次初始连
接,分歧页面哀求可以共享统一毗连,就可以取得明显的功能改良。
Servlet是一个Java类。Servlet引擎(它多是Web服务软件的一部分,也多是一个自力的附加模块)在体系启动或Servlet第一次被哀求时将该类装进Java假造机并创立它的一个实例。分歧用户哀求由统一Servlet实例的多个自力线程处置。那些要
求在分歧哀求之间延续无效的数据既能够用Servlet的实例变量来保留,也能够保留在自力的帮助对象中。
用JDBC会见数据库起首要创立与数据库之间的毗连,取得一个毗连对象(Connection),由毗连对象供应实行SQL语句的办法。
本文先容的数据库毗连池包含一个办理类DBConnectionManager,卖力供应与多个毗连池对象(DBConnectionPool类)之间的接口。每个毗连池对象办理一组JDBC毗连对象,每个毗连对象能够被恣意数目的Servlet共享。
类DBConnectionPool供应以下功效:
1)从毗连池猎取(或创立)可用毗连。
2)把毗连前往给毗连池。
3)在体系封闭时开释一切资本,封闭一切毗连。
别的,DBConnectionPool类还可以处置有效毗连(本来挂号为可用的毗连,因为某种缘故原由不再可用,如超时,通信成绩)
,并可以限定毗连池中的毗连总数不凌驾某个预定值。
办理类DBConnectionManager用于办理多个毗连池对象,它供应以下功效:
1)装载和注册JDBC驱动程序。
2)依据在属性文件中界说的属性创立毗连池对象。
3)完成毗连池名字与实在例之间的映照。
4)跟踪客户程序对毗连池的援用,包管在最初一个客户程序停止时平安地封闭一切毗连池。
本文余下部分将具体申明这两个类,最初给出一个示例演示Servlet利用毗连池的一样平常历程。
2、详细完成
DBConnectionManager.java程序清单以下:
001importjava.io.*;
002importjava.sql.*;
003importjava.util.*;
004importjava.util.Date;
005
006/**
007*办理类DBConnectionManager撑持对一个或多个由属性文件界说的数据库毗连
008*池的会见.客户程序能够挪用getInstance()办法会见本类的独一实例.
009*/
010publicclassDBConnectionManager{
011staticprivateDBConnectionManagerinstance;//独一实例
012staticprivateintclients;
013
014privateVectordrivers=newVector();
015privatePrintWriterlog;
016privateHashtablepools=newHashtable();
017
018/**
019*前往独一实例.假如是第一次挪用此办法,则创立实例
020*
021*@returnDBConnectionManager独一实例
022*/
023staticsynchronizedpublicDBConnectionManagergetInstance(){
024if(instance==null){
025instance=newDBConnectionManager();
026}
027clients++;
028returninstance;
029}
030
031/**
032*建构函数公有以避免别的对象创立本类实例
033*/
034privateDBConnectionManager(){
035init();
036}
037
038/**
039*将毗连对象前往给由名字指定的毗连池
040*
041*@paramname在属性文件中界说的毗连池名字
042*@paramcon毗连对象
043*/
044publicvoidfreeConnection(Stringname,Connectioncon){
045DBConnectionPoolpool=(DBConnectionPool)pools.get(name);
046if(pool!=null){
047pool.freeConnection(con);
048}
049}
050
051/**
052*取得一个可用的(余暇的)毗连.假如没有可用毗连,且已有毗连数小于最年夜毗连数
053*限定,则创立并前往新毗连
054*
055*@paramname在属性文件中界说的毗连池名字
056*@returnConnection可用毗连或null
057*/
058publicConnectiongetConnection(Stringname){
059DBConnectionPoolpool=(DBConnectionPool)pools.get(name);
060if(pool!=null){
061returnpool.getConnection();
062}
063returnnull;
064}
065
066/**
067*取得一个可用毗连.若没有可用毗连,且已有毗连数小于最年夜毗连数限定,
068*则创立并前往新毗连.不然,在指定的工夫内守候别的线程开释毗连.
069*
070*@paramname毗连池名字
071*@paramtime以毫秒计的守候工夫
072*@returnConnection可用毗连或null
073*/
074publicConnectiongetConnection(Stringname,longtime){
075DBConnectionPoolpool=(DBConnectionPool)pools.get(name);
076if(pool!=null){
077returnpool.getConnection(time);
078}
079returnnull;
080}
081
082/**
083*封闭一切毗连,打消驱动程序的注册
084*/
085publicsynchronizedvoidrelease(){
086//守候直到最初一个客户程序挪用
087if(--clients!=0){
088return;
089}
090
091EnumerationallPools=pools.elements();
092while(allPools.hasMoreElements()){
093DBConnectionPoolpool=(DBConnectionPool)allPools.nextElement();
094pool.release();
095}
096EnumerationallDrivers=drivers.elements();
097while(allDrivers.hasMoreElements()){
098Driverdriver=(Driver)allDrivers.nextElement();
099try{
100DriverManager.deregisterDriver(driver);
101log("打消JDBC驱动程序"+driver.getClass().getName()+"的注册");
102}
103catch(SQLExceptione){
104log(e,"没法打消以下JDBC驱动程序的注册:"+driver.getClass().getName());
105}
106}
107}
108
109/**
110*依据指定属性创立毗连池实例.
111*
112*@paramprops毗连池属性
113*/
114privatevoidcreatePools(Propertiesprops){
115EnumerationpropNames=props.propertyNames();
116while(propNames.hasMoreElements()){
117Stringname=(String)propNames.nextElement();
118if(name.endsWith(".url")){
119StringpoolName=name.substring(0,name.lastIndexOf("."));
120Stringurl=props.getProperty(poolName+".url");
121if(url==null){
122log("没无为毗连池"+poolName+"指定URL");
123continue;
124}
125Stringuser=props.getProperty(poolName+".user");
126Stringpassword=props.getProperty(poolName+".password");
127Stringmaxconn=props.getProperty(poolName+".maxconn","0");
128intmax;
129try{
130max=Integer.valueOf(maxconn).intValue();
131}
132catch(NumberFormatExceptione){
133log("毛病的最年夜毗连数限定:"+maxconn+".毗连池:"+poolName);
134max=0;
135}
136DBConnectionPoolpool=
137newDBConnectionPool(poolName,url,user,password,max);
138pools.put(poolName,pool);
139log("乐成创立毗连池"+poolName);
140}
141}
142}
143
144/**
145*读取属性完成初始化
146*/
147privatevoidinit(){
148InputStreamis=getClass().getResourceAsStream("/db.properties");
149PropertiesdbProps=newProperties();
150try{
151dbProps.load(is);
152}
153catch(Exceptione){
154System.err.println("不克不及读取属性文件."+
155"请确保db.properties在CLASSPATH指定的路径中");
156return;
157}
158StringlogFile=dbProps.getProperty("logfile","DBConnectionManager.log");
159try{
160log=newPrintWriter(newFileWriter(logFile,true),true);
161}
162catch(IOExceptione){
163System.err.println("没法翻开日记文件:"+logFile);
164log=newPrintWriter(System.err);
165}
166loadDrivers(dbProps);
167createPools(dbProps);
168}
169
170/**
171*装载和注册一切JDBC驱动程序
172*
173*@paramprops属性
174*/
175privatevoidloadDrivers(Propertiesprops){
176StringdriverClasses=props.getProperty("drivers");
177StringTokenizerst=newStringTokenizer(driverClasses);
178while(st.hasMoreElements()){
179StringdriverClassName=st.nextToken().trim();
180try{
181Driverdriver=(Driver)
182Class.forName(driverClassName).newInstance();
183DriverManager.registerDriver(driver);
184drivers.addElement(driver);
185log("乐成注册JDBC驱动程序"+driverClassName);
186}
187catch(Exceptione){
188log("没法注册JDBC驱动程序:"+
189driverClassName+",毛病:"+e);
190}
191}
192}
193
194/**
195*将文本信息写进日记文件
196*/
197privatevoidlog(Stringmsg){
198log.println(newDate()+":"+msg);
199}
200
201/**
202*将文本信息与非常写进日记文件
203*/
204privatevoidlog(Throwablee,Stringmsg){
205log.println(newDate()+":"+msg);
206e.printStackTrace(log);
207}
208
209/**
210*此外部类界说了一个毗连池.它可以依据请求创立新毗连,直到预定的最
211*年夜毗连数为止.在前往毗连给客户程序之前,它可以考证毗连的无效性.
212*/
213classDBConnectionPool{
214privateintcheckedOut;
215privateVectorfreeConnections=newVector();
216privateintmaxConn;
217privateStringname;
218privateStringpassword;
219privateStringURL;
220privateStringuser;
221
222/**
223*创立新的毗连池
224*
225*@paramname毗连池名字
226*@paramURL数据库的JDBCURL
227*@paramuser数据库帐号,或null
228*@parampassword暗码,或null
229*@parammaxConn此毗连池同意创建的最年夜毗连数
230*/
231publicDBConnectionPool(Stringname,StringURL,Stringuser,Stringpassword,
232intmaxConn){
233this.name=name;
234this.URL=URL;
235this.user=user;
236this.password=password;
237this.maxConn=maxConn;
238}
239
240/**
241*将不再利用的毗连前往给毗连池
242*
243*@paramcon客户程序开释的毗连
244*/
245publicsynchronizedvoidfreeConnection(Connectioncon){
246//将指定毗连到场到向量开端
247freeConnections.addElement(con);
248checkedOut--;
249notifyAll();
250}
251
252/**
253*从毗连池取得一个可用毗连.如没有余暇的毗连且以后毗连数小于最年夜毗连
254*数限定,则创立新毗连.如本来挂号为可用的毗连不再无效,则从向量删除之,
255*然后递回挪用本人以实验新的可用毗连.
256*/
257publicsynchronizedConnectiongetConnection(){
258Connectioncon=null;
259if(freeConnections.size()>0){
260//猎取向量中第一个可用毗连
261con=(Connection)freeConnections.firstElement();
262freeConnections.removeElementAt(0);
263try{
264if(con.isClosed()){
265log("从毗连池"+name+"删除一个有效毗连");
266//递回挪用本人,实验再次猎取可用毗连
267con=getConnection();
268}
269}
270catch(SQLExceptione){
271log("从毗连池"+name+"删除一个有效毗连");
272//递回挪用本人,实验再次猎取可用毗连
273con=getConnection();
274}
275}
276elseif(maxConn==0||checkedOut<maxConn){
277con=newConnection();
278}
279if(con!=null){
280checkedOut++;
281}
282returncon;
283}
284
285/**
286*从毗连池猎取可用毗连.能够指定客户程序可以守候的最长工夫
287*拜见前一个getConnection()办法.
288*
289*@paramtimeout以毫秒计的守候工夫限定
290*/
291publicsynchronizedConnectiongetConnection(longtimeout){
292longstartTime=newDate().getTime();
293Connectioncon;
294while((con=getConnection())==null){
295try{
296wait(timeout);
297}
298catch(InterruptedExceptione){}
299if((newDate().getTime()-startTime)>=timeout){
300//wait()前往的缘故原由是超时
301returnnull;
302}
303}
304returncon;
305}
306
307/**
308*封闭一切毗连
309*/
310publicsynchronizedvoidrelease(){
311EnumerationallConnections=freeConnections.elements();
312while(allConnections.hasMoreElements()){
313Connectioncon=(Connection)allConnections.nextElement();
314try{
315con.close();
316log("封闭毗连池"+name+"中的一个毗连");
317}
318catch(SQLExceptione){
319log(e,"没法封闭毗连池"+name+"中的毗连");
320}
321}
322freeConnections.removeAllElements();
323}
324
325/**
326*创立新的毗连
327*/
328privateConnectionnewConnection(){
329Connectioncon=null;
330try{
331if(user==null){
332con=DriverManager.getConnection(URL);
333}
334else{
335con=DriverManager.getConnection(URL,user,password);
336}
337log("毗连池"+name+"创立一个新的毗连");
338}
339catch(SQLExceptione){
340log(e,"没法创立以下URL的毗连:"+URL);
341returnnull;
342}
343returncon;
344}
345}
346}
3、类DBConnectionPool申明该类在209至345行完成,它暗示指向某个数据库的毗连池。数据库由JDBCURL标识。一个JDBCURL由三部分构成:协定标识(老是jdbc),驱动程序标识(如odbc、idb、oracle等),数据库标识(其格局依附于驱动程序)。比方,jdbc:odbc:de
mo,便是一个指向demo数据库的JDBCURL,并且会见该数据库要利用JDBC-ODBC驱动程序。每一个毗连池都有一个供客户程序利用的名字和可选的用户帐号、暗码、最年夜毗连数限定。假如Web使用程序所撑持的某些数据库操纵能够被一切用户实行,而别的一些操纵应由出格允许的用户实行,则能够为两类操纵分离界说毗连池,两个毗连池利用不异的JDBCURL,但利用分歧的帐号和暗码。
类DBConnectionPool的建构函数必要上述一切数据作为其参数。如222至238行所示,这些数据被保留为它的实例变量:
如252至283行、285至305行所示,客户程序可使用DBConnectionPool类供应的两个办法猎取可用毗连。二者的配合的地方在于:如毗连池中存在可用毗连,则间接前往,不然创立新的毗连并前往。假如没有可用毗连且已有毗连总数即是最年夜限定
数,第一个办法将间接前往null,而第二个办法将守候直到有可用毗连为止。
一切的可用毗连对象均挂号在名为freeConnections的向量(Vector)中。假如向量中有多于一个的毗连,getConnection()老是拔取第一个。同时,因为新的可用毗连老是从尾部到场向量,从而使得数据库毗连因为长工夫闲置而被封闭的风险减低到最小水平。
第一个getConnection()在前往可用毗连给客户程序之前,挪用了isClosed()办法考证毗连仍然无效。假如该毗连被封闭或触发非常,getConnection()递回地挪用本人以实验猎取别的的可用毗连。假如在向量freeConnections中不存在任何可用连
接,getConnection()办法反省是不是已指定最年夜毗连数限定。如已指定,则反省以后毗连数是不是已抵达极限。此处maxConn为0暗示没无限制。假如没有指定最年夜毗连数限定或以后毗连数小于该值,该办法实验创立新的毗连。如创立乐成,则增添已利用毗连的计数并前往,不然前往空值。
如325至345行所示,创立新毗连由newConnection()办法完成。创立历程与是不是已指定命据库帐号、暗码有关。
JDBC的DriverManager类供应多个getConnection()办法,这些办法要用到JDBCURL与别的一些参数,如用户帐号和暗码等。
DriverManager将利用指定的JDBCURL断定合适于方针数据库的驱动程序及创建毗连。
在285至305行完成的第二个getConnection()办法必要一个以毫秒为单元的工夫参数,该参数暗示客户程序可以守候的最长工夫。创建毗连的详细操纵仍然由第一个getConnection()办法完成。
该办法实行时先将startTime初始化为以后工夫。在while轮回中实验取得一个毗连。假如失利,则以给定的工夫值为参数挪用wait()。wait()的前往多是因为别的线程挪用notify()或notifyAll(),也多是因为预准时间已到。为找出wait()前往的真正缘故原由,程序用以后工夫减入手下手工夫(startTime),如差值年夜于预准时间则前往空值,不然再次挪用getConnection()。
把余暇的毗连挂号到毗连池由240至250行的freeConnection()办法完成,它的参数为前往给毗连池的毗连对象。该对象被到场到freeConnections向量的开端,然后削减已利用毗连计数。挪用notifyAll()是为了关照别的正在守候可用毗连的线程。
很多Servlet引擎为完成平安封闭供应多种办法。数据库毗连池必要晓得该事务以包管一切毗连可以一般封闭。DBConnectionManager类负和谐全部封闭历程,但封闭毗连池中一切毗连的义务则由DBConnectionPool类卖力。在307至323行完成的release()办法供DBConnectionManager挪用。该办法遍历freeConnections向量并封闭一切毗连,然后从向量中删除这些毗连。
4、类DBConnectionManager申明
该类只能创立一个实例,别的对象可以挪用其静态办法(也称为类办法)取得该独一实例的援用。如031至036行所示,DBConnectionManager类的建构函数是公有的,这是为了不别的对象创立该类的实例。
DBConnectionManager类的客户程序能够挪用getInstance()办法取得对该类独一实例的援用。如018至029行所示,类的独一实例在getInstance()办法第一次被挪用时代创立,今后其援用就一向保留在静态变量instance中。每次挪用getInstance()
都增添一个DBConnectionManager的客户程序计数。即,该计数代表援用DBConnectionManager独一实例的客户程序总数,它将被用于把持毗连池的封闭操纵。
该类实例的初始化事情由146至168行之间的公有办法init()完成。个中getResourceAsStream()办法用于定位并翻开内部文件。内部文件的定位办法依附于类装载器的完成。尺度的当地类装载器查找操纵老是入手下手于类文件地点路径,也可以搜刮CLASSPATH中声明的路径。db.properties是一个属性文件,它包括界说毗连池的键-值对。可供界说的公用属性以下:
drivers以空格分开的JDBC驱动程序类列表
logfile日记文件的相对路径
别的的属性和特定毗连池相干,其属性名字前应加上毗连池名字:
<poolname>.url数据库的JDBCURL
<poolname>.maxconn同意创建的最年夜毗连数,0暗示没无限制
<poolname>.user用于该毗连池的数据库帐号
<poolname>.password响应的暗码
个中url属性是必须的,而别的属性则是可选的。数据库帐号和暗码必需正当。用于Windows平台的db.properties文件示例
以下:
drivers=sun.jdbc.odbc.JdbcOdbcDriverjdbc.idbDriver
logfile=D:usersrcjavaDBConnectionManagerlog.txt
idb.url=jdbc:idb:c:localjavawebserver1.1dbdb.prp
idb.maxconn=2
access.url=jdbc:odbc:demo
access.user=demo
access.password=demopw
注重在Windows路径中的反斜杠必需输出2个,这是因为属性文件中的反斜杠同时也是一个本义字符。
init()办法在创立属性对象并读取db.properties文件以后,就入手下手反省logfile属性。假如属性文件中没有指定日记文件,则默许为以后目次下的DBConnectionManager.log文件。如日记文件没法利用,则向System.err输入日记纪录。
装载和注册一切在drivers属性中指定的JDBC驱动程序由170至192行之间的loadDrivers()办法完成。该办法先用StringTokenizer将drivers属性值支解为对应于驱动程序称号的字符串,然后顺次装载这些类并创立实在例,最初在DriverManager中注册
该实例并把它到场到一个公有的向量drivers。向量drivers将用于封闭服务时从DriverManager作废一切JDBC驱动程序的注册。
init()办法的最初一个义务是挪用公有办法createPools()创立毗连池对象。如109至142行所示,createPools()办法先创立一切属性名字的列举对象(即Enumeration对象,该对象能够设想为一个元素系列,逐次挪用其nextElement()办法将按次返
回各元素),然后在个中搜刮名字以“.url”开头的属性。关于每个切合前提的属性,先提取其毗连池名字部分,进而读取一切属于该毗连池的属性,最初创立毗连池对象并把它保留在实例变量pools中。散列表(Hashtable类)pools完成毗连
池名字到毗连池对象之间的映照,此处以毗连池名字为键,毗连池对象为值。
为便于客户程序从指定毗连池取得可用毗连或将毗连前往给毗连池,类DBConnectionManager供应了办法getConnection()和freeConnection()。一切这些办法都请求在参数中指定毗连池名字,详细的毗连猎取或前往操纵则挪用对应的毗连池对象完成。它们的完成分离在051至064行、066至080行、038至049行。
如082至107行所示,为完成毗连池的平安封闭,DBConnectionManager供应了办法release()。在下面我们已提到,一切DBConnectionManager的客户程序都应当挪用静态办法getInstance()以取得该办理器的援用,此挪用将增添客户程序计数。
客户程序在封闭时挪用release()能够递加该计数。当最初一个客户程序挪用release(),递加后的援用计数为0,就能够挪用各个毗连池的release()办法封闭一切毗连了。办理类release()办法最初的义务是打消一切JDBC驱动程序的注册。
5、Servlet利用毗连池示例
ServletAPI所界说的Servlet性命周期类如:
1)创立并初始化Servlet(init()办法)。
2)呼应客户程序的服务哀求(service()办法)。
3)Servlet停止运转,开释一切资本(destroy()办法)。
本例演示毗连池使用,上述关头步骤中的相干操纵为:
1)在init(),用实例变量connMgr保留挪用DBConnectionManager.getInstance()所前往的援用。
2)在service(),挪用getConnection(),实行数据库操纵,用freeConnection()将毗连前往给毗连池。
3)在destroy(),挪用release()封闭一切毗连,开释一切资本。
示例程序清单以下:
importjava.io.*;
importjava.sql.*;
importjavax.servlet.*;
importjavax.servlet.http.*;
publicclassTestServletextendsHttpServlet{
privateDBConnectionManagerconnMgr;
publicvoidinit(ServletConfigconf)throwsServletException{
super.init(conf);
connMgr=DBConnectionManager.getInstance();
}
publicvoidservice(HttpServletRequestreq,HttpServletResponseres)
throwsIOException{
res.setContentType("text/html");
PrintWriterout=res.getWriter();
Connectioncon=connMgr.getConnection("idb");
if(con==null){
out.println("不克不及猎取数据库毗连.");
return;
}
ResultSetrs=null;
ResultSetMetaDatamd=null;
Statementstmt=null;
try{
stmt=con.createStatement();
rs=stmt.executeQuery("SELECT*FROMEMPLOYEE");
md=rs.getMetaData();
out.println("<H1>职工数据</H1>");
while(rs.next()){
out.println("<BR>");
for(inti=1;i<md.getColumnCount();i++){
out.print(rs.getString(i)+",");
}
}
stmt.close();
rs.close();
}
catch(SQLExceptione){
e.printStackTrace(out);
}
connMgr.freeConnection("idb",con);
}
publicvoiddestroy(){
connMgr.release();
super.destroy();
}
}
但是对于JAVA技术类的学习,我觉得大课堂反而会影响自身独立思考的过程,因为上课的时候,老师讲课的速度很快为了不遗漏要点,通常会仔细的听, |
|