用连接池提高Servlet访问数据库的效率.docx
《用连接池提高Servlet访问数据库的效率.docx》由会员分享,可在线阅读,更多相关《用连接池提高Servlet访问数据库的效率.docx(17页珍藏版)》请在冰豆网上搜索。
用连接池提高Servlet访问数据库的效率
∙用连接池提高Servlet访问数据库的效率-JSP教程、数据库相关
∙来源:
作者:
发布时间:
2007-12-2714:
03:
11
javaservlet作为最佳选择的服务器端数据处理技术,正在迅速取代cgi脚本。
servlet超越cgi的优势之一在于,不仅多个请求能共享公用资源,而且还能在不同用户请求之间保留持续数据。
本文介绍一种充分发挥该特色的实用技术,即数据库连接池。
一、实现连接池的意义
动态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使用连接池的一般过程。
二、具体实现
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||checkedout277con=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){
332