国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發 > 綜合 > 正文

關于連接池

2024-07-21 02:39:27
字體:
來源:轉載
供稿:網友

到目前為目,JDBC2的連結池只是一個接口,沒有真正的實現,JDBC3正在開發中,據報已經支持連結池,但JDBC3用了JNDI技術,連結池的配置可以讓一個高手都煩死. 

目前第三方已經實現的連結池當然是poolman,1.0版對一般用戶來說已經足夠用了.配置也簡單,2.0版雖然增加了一些功能,但配置也是采用JNDI,對RMI和EJB不懂的朋友可能很煩.建議用1.0的了. 

假如有愛好,自己也可以實現連結池,最要害的技術也就是把連結作為參數傳給一個BEAN,用完后返回這個參數連結而不是關閉. 
下面是一個簡單的實現: 
DBConnectionManager.java程序清單如下: 

001 import java.io.*; 
002 import java.sql.*; 
003 import java.util.*; 
004 import java.util.Date; 
005 
006 /** 
007 * 治理類DBConnectionManager支持對一個或多個由屬性文件定義的數據庫連接 
008 * 池的訪問.客戶程序可以調用getInstance()方法訪問本類的唯一實例. 
009 */ 
010 public class DBConnectionManager { 
011 static PRivate DBConnectionManager instance; // 唯一實例 
012 static private int clients; 
013 
014 private Vector drivers = new Vector(); 
015 private PrintWriter log; 
016 private Hashtable pools = new Hashtable(); 
017 
018 /** 
019 * 返回唯一實例.假如是第一次調用此方法,則創建實例 
020 * 
021 * @return DBConnectionManager 唯一實例 
022 */ 
023 static synchronized public DBConnectionManager getInstance() { 
024 if (instance == null) { 
025 instance = new DBConnectionManager(); 
026 } 
027 clients++; 
028 return instance; 
029 } 
030 
031 /** 
032 * 建構函數私有以防止其它對象創建本類實例 
033 */ 
034 private DBConnectionManager() { 
035 init(); 
036 } 
037 
038 /** 
039 * 將連接對象返回給由名字指定的連接池 
040 * 
041 * @param name 在屬性文件中定義的連接池名字 
042 * @param con 連接對象//r 

043 */ 
044 public void freeConnection(String name, Connection con) { 
045 DBConnectionPool pool = (DBConnectionPool) pools.get(name); 
046 if (pool != null) { 
047 pool.freeConnection(con); 
048 } 
049 } 
050 
051 /** 
052 * 獲得一個可用的(空閑的)連接.假如沒有可用連接,且已有連接數小于最大連接數 

053 * 限制,則創建并返回新連接 
054 * 
055 * @param name 在屬性文件中定義的連接池名字 
056 * @return Connection 可用連接或null 
057 */ 
058 public Connection getConnection(String name) { 
059 DBConnectionPool pool = (DBConnectionPool) pools.get(name); 
060 if (pool != null) { 
061 return pool.getConnection(); 
062 } 
063 return null; 
064 } 
065 
066 /** 
067 * 獲得一個可用連接.若沒有可用連接,且已有連接數小于最大連接數限制, 
068 * 則創建并返回新連接.否則,在指定的時間內等待其它線程釋放連接. 
069 * 
070 * @param name 連接池名字 
071 * @param time 以毫秒計的等待時間//r 

072 * @return Connection 可用連接或null 
073 */ 
074 public Connection getConnection(String name, long time) { 
075 DBConnectionPool pool = (DBConnectionPool) pools.get(name); 
076 if (pool != null) { 
077 return pool.getConnection(time); 
078 } 
079 return null; 
080 } 
081 
082 /** 
083 * 關閉所有連接,撤銷驅動程序的注冊//r 

084 */ 
085 public synchronized void release() { 
086 // 等待直到最后一個客戶程序調用 
087 if (--clients != 0) { 
088 return; 
089 } 
090 
091 Enumeration allPools = pools.elements(); 
092 while (allPools.hasMoreElements()) { 
093 DBConnectionPool pool = (DBConnectionPool) allPools.nextElement(); 
094 pool.release(); 
095 } 
096 Enumeration allDrivers = drivers.elements(); 
097 while (allDrivers.hasMoreElements()) { 
098 Driver driver = (Driver) allDrivers.nextElement(); 
099 try { 
100 DriverManager.deregisterDriver(driver); 
101 log("撤銷JDBC驅動程序 " + driver.getClass().getName()+"的注冊///"); 
102 } 
103 catch (SQLException e) { 
104 log(e, "無法撤銷下列JDBC驅動程序的注冊: " + driver.getClass().getName()); 
105 } 
106 } 
107 } 
108 
109 /** 
110 * 根據指定屬性創建連接池實例. 
111 * 

112 * @param props 連接池屬性 
113 */ 
114 private void createPools(Properties props) { 
115 Enumeration propNames = props.propertyNames(); 
116 while (propNames.hasMoreElements()) { 
117 String name = (String) propNames.nextElement(); 
118 if (name.endsWith(".url")) { 
119 String poolName = name.substring(0, name.lastIndexOf(".")); 
120 String url = props.getProperty(poolName + ".url"); 
121 if (url == null) { 
122 log("沒有為連接池" + poolName + "指定URL"); 
123 continue; 
124 } 
125 String user = props.getProperty(poolName + ".user"); 
126 String passWord = props.getProperty(poolName + ".password"); 
127 String maxconn = props.getProperty(poolName + ".maxconn", "0"); 
128 int max; 
129 try { 
130 max = Integer.valueOf(maxconn).intValue(); 
131 } 
132 catch (NumberFormatException e) { 
133 log("錯誤的最大連接數限制: " + maxconn + " .連接池: " + poolName); 
134 max = 0; 
135 } 
136 DBConnectionPool pool = 
137 new DBConnectionPool(poolName, url, user, password, max); 
138 pools.put(poolName, pool); 
139 log("成功創建連接池" + poolName); 
140 } 
141 } 
142 } 
143 
144 /** 
145 * 讀取屬性完成初始化 
146 */ 
147 private void init() { 
148 InputStream is = getClass().getResourceAsStream("/db.properties"); 
149 Properties dbProps = new Properties(); 
150 try { 
151 dbProps.load(is); 
152 } 
153 catch (Exception e) { 
154 System.err.println("不能讀取屬性文件. " + 
155 "請確保db.properties在CLASSPATH指定的路徑中"); 
156 return; 
157 } 
158 String logFile = dbProps.getProperty("logfile", "DBConnectionManager.log"); 
159 try { 
160 log = new PrintWriter(new FileWriter(logFile, true), true); 

161 } 
162 catch (IOException e) { 
163 System.err.println("無法打開日志文件: " + logFile); 
164 log = new PrintWriter(System.err); 
165 } 
166 loadDrivers(dbProps); 
167 createPools(dbProps); 
168 } 
169 
170 /** 
171 * 裝載和注冊所有JDBC驅動程序//r 

172 * 
173 * @param props 屬性 
174 */ 
175 private void loadDrivers(Properties props) { 
176 String driverClasses = props.getProperty("drivers"); 
177 StringTokenizer st = new StringTokenizer(driverClasses); 
178 while (st.hasMoreElements()) { 
179 String driverClassName = st.nextToken().trim(); 
180 try { 
181 Driver driver = (Driver) 
182 Class.forName(driverClassName).newInstance(); 
183 DriverManager.registerDriver(driver); 
184 drivers.addElement(driver); 
185 log("成功注冊JDBC驅動程序///" + driverClassName); 
186 } 
187 catch (Exception e) { 
188 log("無法注冊JDBC驅動程序: " + 
189 driverClassName + ", 錯誤: " + e); 
190 } 
191 } 
192 } 
193 
194 /** 
195 * 將文本信息寫入日志文件 
196 */ 
197 private void log(String msg) { 
198 log.println(new Date() + ": " + msg); 
199 } 
200 
201 /** 
202 * 將文本信息與異常寫入日志文件 
203 */ 
204 private void log(Throwable e, String msg) { 
205 log.println(new Date() + ": " + msg); 
206 e.printStackTrace(log); 
207 } 
208 
209 /** 
210 * 此內部類定義了一個連接池.它能夠根據要求創建新連接,直到預定的最//r 

211 * 大連接數為止.在返回連接給客戶程序之前,它能夠驗證連接的有效性. 
212 */ 
213 class DBConnectionPool { 
214 private int checkedOut; 
215 private Vector freeConnections = new Vector(); 
216 private int maxConn; 
217 private String name; 
218 private String password; 
219 private String URL; 
220 private String user; 
221 
222 /** 
223 * 創建新的連接池 
224 * 

225 * @param name 連接池名字 
226 * @param URL 數據庫的JDBC URL 
227 * @param user 數據庫帳號,或 null 
228 * @param password 密碼,或 null 
229 * @param maxConn 此連接池答應建立的最大連接數 
230 */ 
231 public DBConnectionPool(String name, String URL, String user, String password, 
232 int maxConn) { 
233 this.name = name; 
234 this.URL = URL; 
235 this.user = user; 
236 this.password = password; 
237 this.maxConn = maxConn; 
238 } 
239 
240 /** 
241 * 將不再使用的連接返回給連接池 
242 * 
243 * @param con 客戶程序釋放的連接 
244 */ 
245 public synchronized void freeConnection(Connection con) { 
246 // 將指定連接加入到向量末尾 
247 freeConnections.addElement(con); 
248 checkedOut--; 
249 notifyAll(); 
250 } 
251 
252 /** 
253 * 從連接池獲得一個可用連接.如沒有空閑的連接且當前連接數小于最大連接 
254 * 數限制,則創建新連接.如原來登記為可用的連接不再有效,則從向量刪除之, 
255 * 然后遞歸調用自己以嘗試新的可用連接. 
256 */ 
257 public synchronized Connection getConnection() { 
258 Connection con = null; 
259 if (freeConnections.size() > 0) { 
260 // 獲取向量中第一個可用連接 
261 con = (Connection) freeConnections.firstElement(); 
262 freeConnections.removeElementAt(0); 
263 try { 
264 if (con.isClosed()) { 
265 log("從連接池" + name+"刪除一個無效連接"); 
266 // 遞歸調用自己,嘗試再次獲取可用連接 
267 con = getConnection(); 
268 } 
269 } 
270 catch (SQLException e) { 
271 log("從連接池" + name+"刪除一個無效連接"); 
272 // 遞歸調用自己,嘗試再次獲取可用連接 
273 con = getConnection(); 
274 } 
275 } 
276 else if (maxConn == 0  checkedOut < maxConn) { 
277 con = newConnection(); 
278 } 
279 if (con != null) { 
280 checkedOut++; 
281 } 
282 return con; 
283 } 
284 
285 /** 
286 * 從連接池獲取可用連接.可以指定客戶程序能夠等待的最長時間//r 

287 * 參見前一個getConnection()方法. 

288 * 
289 * @param timeout 以毫秒計的等待時間限制 
290 */ 
291 public synchronized Connection getConnection(long timeout) { 
292 long startTime = new Date().getTime(); 
293 Connection con; 
294 while ((con = getConnection()) == null) { 
295 try { 
296 wait(timeout); 
297 } 
298 catch (InterruptedException e) {} 
299 if ((new Date().getTime() - startTime) >= timeout) { 
300 // wait()返回的原因是超時 
301 return null; 
302 } 
303 } 
304 return con; 
305 } 
306 
307 /** 
308 * 關閉所有連接 
309 */ 
310 public synchronized void release() { 
311 Enumeration allConnections = freeConnections.elements(); 
312 while (allConnections.hasMoreElements()) { 
313 Connection con = (Connection) allConnections.nextElement(); 
314 try { 
315 con.close(); 
316 log("關閉連接池" + name+"中的一個連接"); 
317 } 
318 catch (SQLException e) { 
319 log(e, "無法關閉連接池" + name+"中的連接"); 
320 } 
321 } 
322 freeConnections.removeAllElements(); 
323 } 
324 
325 /** 
326 * 創建新的連接 
327 */ 
328 private Connection newConnection() { 
329 Connection con = null; 
330 try { 
331 if (user == null) { 
332 con = DriverManager.getConnection(URL); 
333 } 
334 else { 
335 con = DriverManager.getConnection(URL, user, password); 
336 } 
337 log("連接池" + name+"創建一個新的連接"); 
338 } 
339 catch (SQLException e) { 
340 log(e, "無法創建下列URL的連接: " + URL); 
341 return null; 
342 } 
343 return con; 
344 } 
345 } 
346 } 

三、類DBConnectionPool說明/ 

該類在209至345行實現,它表示指向某個數據庫的連接池。數據庫由JDBC URL標識。一個JDBC URL由三部分組成:協議標識(總是jdbc),驅動程序標識(如 odbc、idb、Oracle等),數據庫標識(其格式依靠于驅動程序)。例如,jdbc:odbc:demo,即是一個指向demo數據庫的JDBC URL,而且訪問該數據庫要使用JDBC-ODBC驅動程序。每個連接池都有一個供客戶程序使用的名字以及可選的用戶帳號、密碼、最大連接數限制。假如Web應用程序所支持的某些數據庫操作可以被所有用戶執行,而其它一些操作應由非凡許可的用戶執行,則可以為兩類操作分別定義連接池,兩個連接池使用相同的JDBC URL,但使用不同的帳號和密碼。 

類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()方法,這些方法要用到JDBC URL與其它一些參數,如用戶帳號和密碼等。DriverManager將使用指定的JDBC URL確定適合于目標數據庫的驅動程序及建立連接。 
在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向量并關閉所有連接,然后從向量中刪除這些連接。 


四、類DBConnectionManager 說明/ 

該類只能創建一個實例,其它對象能夠調用其靜態方法(也稱為類方法)獲得該唯一實例的引用。如031至036行所示,DBConnectionManager類的建構函數是私有的,這是為了避免其它對象創建該類的實例。 
DBConnectionManager類的客戶程序可以調用getInstance()方法獲得對該類唯一實例的引用。如018至029行所示,類的唯一實例在getInstance()方法第一次被調用期間創建,此后其引用就一直保存在靜態變量instance中。每次調用getInstance()都增加一個DBConnectionManager的客戶程序計數。即,該計數代表引用DBConnectionManager唯一實例的客戶程序總數,它將被用于控制連接池的關閉操作。 
該類實例的初始化工作由146至168行之間的私有方法init()完成。其中 getResourceAsStream()方法用于定位并打開外部文件。外部文件的定位方法依靠于類裝載器的實現。標準的本地類裝載器查找操作總是開始于類文件所在路徑,也能夠搜索CLASSPATH中聲明的路徑。db.properties是一個屬性文件,它包含定義連接池的鍵-值對??晒┒x的公用屬性如下: 

drivers 以空格分隔的JDBC驅動程序類列表/ 
logfile 日志文件的絕對路徑 

其它的屬性和特定連接池相關,其屬性名字前應加上連接池名字: 

< poolname>.url 數據庫的 JDBC URL 
< poolname>.maxconn 答應建立的最大連接數,0表示沒有限制 
< poolname>.user 用于該連接池的數據庫帳號 
< poolname>.password 相應的密碼/ 

其中url屬性是必需的,而其它屬性則是可選的。數據庫帳號和密碼必須合法。用于Windows平臺的db.properties文件示例如下: 

drivers=sun.jdbc.odbc.JdbcOdbcDriver jdbc.idbDriver 
logfile=D://user//src//java//DBConnectionManager//log.txt 


idb.url=jdbc:idb:c://local//javawebserver1.1//db//db.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驅動程序的注冊。 


五、Servlet使用連接池示例 

Servlet API所定義的Servlet生命周期類如: 

1) 創建并初始化Servlet(init()方法)。 
2) 響應客戶程序的服務請求(service()方法)。 
3) Servlet終止運行,釋放所有資源(destroy()方法)。 

本例演示連接池應用,上述要害步驟中的相關操作為: 

1) 在init(),用實例變量connMgr 保存調用DBConnectionManager.getInstance()所返回的引用。 
2) 在service(),調用getConnection(),執行數據庫操作,用freeConnection()將連接返回給連接池。 
3) 在destroy(),調用release()關閉所有連接,釋放所有資源。 

示例程序清單如下: 


import java.io.*;import java.sql.*;import javax.servlet.*;import javax.servlet.http.*;public class TestServlet extends HttpServlet { private DBConnectionManager connMgr; public void init(ServletConfig conf) throws ServletException { super.init(conf); connMgr = DBConnectionManager.getInstance(); } public void service(HttpServletRequest req, HttpServletResponse res) throws IOException { res.setContentType("text/Html"); PrintWriter out = res.getWriter(); Connection con = connMgr.getConnection("idb");
 if (con == null) { out.println("不能獲取數據庫連接."); return; } ResultSet rs = null; ResultSetMetaData md = null; Statement stmt = null; try { stmt = con.createStatement(); rs = stmt.executeQuery("SELECT * FROM EMPLOYEE"); md = rs.getMetaData(); out.println("< H1>職工數據< /H1>"); while (rs.next()) { out.println("< BR>"); for (int i = 1; i < md.getColumnCount(); i++) { out.print(rs.getString(i) + ", "); } } stmt.close(); rs.close(); } catch (SQLException e) { e.printStackTrace(out); } connMgr.freeConnection("idb", con); } public void destroy() { connMgr.release(); super.destroy(); }}

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 镇平县| 阜平县| 怀柔区| 陆良县| 蓬溪县| 平陆县| 克什克腾旗| 阿克苏市| 历史| 阿城市| 中方县| 什邡市| 中江县| 枞阳县| 砀山县| 唐山市| 柞水县| 淄博市| 齐河县| 嘉义市| 上林县| 德州市| 澄迈县| 叶城县| 新巴尔虎右旗| 榕江县| 贵州省| 金山区| 莱州市| 海南省| 衡阳县| 洛宁县| 大连市| 德令哈市| 松江区| 平顶山市| 吉水县| 合山市| 阿坝县| 梅州市| 雷州市|