连接池是非常好的想法,应用很普遍。自己写一个数据库连接池,并不像想象中那样困难。一般系统对连接池的功能不会有太多要求,使用自己的连接池未必是个坏主意。下面以Oracle为例,但是对Teradata和Greenplum也是可行的。另外我还实现了连接有效性检查(checkConn)和恢复连接(resetConn)的方法。本例编程采用的是JRE1.4.2环境(别忘了准备访问数据库的jar包)。有任何问题请随时留言,欢迎探讨。

在Oracle内创建测试数据:



1. drop table
2. create table
3.        field_id varchar2(3),  
4.        field_content varchar2(60),  
5. date default
6. );  
7.  
8. insert into my_table(field_id,field_content) values('001','this is first record');  
9. insert into my_table(field_id,field_content) values('002','this is second record');  
10. insert into my_table(field_id,field_content) values('003','this is third record');  
11. commit;

DBPool.java:




1. package
2.  
3. import
4. import
5. import
6. import
7. import
8.  
9. public class
10. private
11. private
12. private
13. private
14. private int connCount = 3;//连接数 
15. private Connection[] connections;//保存数据库连接 
16. private String[] connStatus;// 已连可用Y   已连不可用N   未连接X 
17. private Date[] lastQueryTime;//时间戳 
18.       
19. public
20. this.connCount=poolConfiguration.getConnCount();  
21. this.cls=poolConfiguration.getCls();  
22. this.url=poolConfiguration.getUrl();  
23. this.usr=poolConfiguration.getUsr();  
24. this.pss=poolConfiguration.getPss();  
25. this.connections=new Connection[this.connCount];  
26. this.connStatus=new String[this.connCount];  
27. for(int i=0;i<this.connCount;i++){  
28. this.connStatus[i]="X";//初始化全部未连接 
29.         }  
30. this.lastQueryTime = new Date[this.connCount];        
31.     }  
32.       
33. public
34. this.cls=cls;  
35. this.url=url;  
36. this.usr=usr;  
37. this.pss=pss;  
38. this.connections=new Connection[this.connCount];  
39. this.connStatus=new String[this.connCount];  
40. for(int i=0;i<this.connCount;i++){  
41. this.connStatus[i]="X";//初始化全部未连接 
42.         }  
43. this.lastQueryTime = new Date[this.connCount];  
44.     }  
45.  
46. public void
47. if(connCount<1){  
48. "请正确设置连接池窗口个数");  
49. else{  
50. try{  
51. this.cls);//register class 
52. catch(ClassNotFoundException e){  
53.                 System.out.println(e.getMessage());  
54. catch(Exception e){  
55. //other exceptions 
56.             }  
57.  
58. for(int i=0;i<this.connCount;i++){  
59. try{  
60. this.connections[i]=DriverManager.getConnection(this.url, this.usr, this.pss);  
61. this.connStatus[i]="Y";  
62. catch(SQLException e){  
63.                     System.out.println(e.getMessage());  
64. catch(Exception e){  
65. //other exceptions 
66.                 }  
67.             }     
68. "initPool is ready...");  
69. //end if 
70.     }  
71.  
72. public void
73. for(int i=0;i<this.connCount;i++){  
74. try{  
75. this.connections[i].commit();  
76. this.connections[i].close();  
77. this.connStatus[i]="X";  
78. this.lastQueryTime[i]=null;  
79. catch(Exception e){  
80. try{  
81. this.connections[i].close();  
82. this.connStatus[i]="X";  
83. this.lastQueryTime[i]=null;                   
84. catch(Exception e1){  
85. //just for catch 
86.                 }  
87.             }             
88.         }  
89. "freePool is over ...");  
90.     }  
91.  
92. public DBPoolConnection getPoolConn() throws
93. new
94.         poolConnection.connNbr=getConnNbr();  
95. if(poolConnection.connNbr==-1){  
96. throw new DBPoolIsFullException("连接池已满");  
97. else{  
98.             poolConnection.conn=getConn(poolConnection.connNbr);  
99.         }  
100. return
101.     }  
102.  
103. public void
104. if(poolConnection==null){  
105. "poolConnection==null,不需要释放");  
106. else{  
107.             freeConn(poolConnection.connNbr);  
108.         }  
109.     }  
110.       
111. public void
112. for(int i=0;i<this.connStatus.length;i++){  
113. "");  
114. this.connStatus[i].toString());  
115. if(this.lastQueryTime[i]==null){  
116. "-[null] ");  
117. else{  
118. "-["+this.lastQueryTime[i].toString()+"] ");  
119.             }  
120.         }  
121. "");  
122.     }  
123.       
124. public
125. return this.cls;  
126.     }  
127.  
128. public
129. return this.url;  
130.     }  
131.  
132. public
133. return this.usr;  
134.     }  
135.  
136. int
137. int iConn=-1;  
138. for(int i=0;i<this.connCount;i++){  
139. if(this.connStatus[i].equals("Y")){  
140. this.lastQueryTime[i]=new
141. this.connStatus[i]="N";  
142.                 iConn=i;  
143. break;  
144.             }  
145.         }  
146. return
147.     }  
148.       
149. int
150. return this.connections[i];  
151.     }  
152.           
153. void
154. try{  
155. this.connections[poolConnection.connNbr].close();  
156. catch(SQLException e){  
157.             System.out.println(e.getMessage());   
158.         }  
159.     }  
160.           
161. boolean
162. null;  
163. "";  
164. boolean checkResult=true;  
165.           
166. //检查连接是否有效 
167. try{  
168. "select * from dual";  
169. this.connections[poolConnection.connNbr].createStatement();  
170. //execute sql 
171.             stmt.close();  
172. "checkConn:checkMessage:execute sql success";  
173.             System.out.println(checkMessage);  
174. catch(Exception e){  
175.             checkMessage = e.getMessage();  
176. //other exceptions 
177. if(checkMessage==null){  
178. "e.getMessage() is null";  
179.                 System.out.println(checkMessage);  
180.             }  
181. //采取激进重连的策略,尽量避免业务中断 
182. if (checkMessage.indexOf("ORA-00942")>=0){  
183. true;//不需要重连 
184. else if(checkMessage.indexOf("does not exist")>=0){  
185. true;//不需要重连 
186. else if(checkMessage.indexOf("Syntax error")>=0){  
187. true;//不需要重连 
188. else{            
189. false;//需要重连 
190.             }  
191.         }  
192. return
193.     }  
194.       
195. boolean
196. boolean result=false;//默认不需要重建连接  
197.           
198. if(poolConnection==null){  
199. "poolConnection==null,不知道您想重设哪个连接");  
200. else if(poolConnection.connNbr==-1){  
201. "poolConnection.connNbr==-1,不知道您想重设哪个连接");  
202. else{  
203. if(checkConn(poolConnection)==true){  
204. "连接有效,不需要重设");  
205. else{  
206. //重设连接 
207. try{  
208. this.cls);//register class 
209. catch(ClassNotFoundException e){  
210.                     System.out.println(e.getMessage());  
211. catch(Exception e){  
212. //other exceptions 
213.                 }  
214. try{  
215. this.connections[poolConnection.connNbr]=DriverManager.getConnection(this.url, this.usr, this.pss);  
216. this.connStatus[poolConnection.connNbr]="Y";  
217. "连接已重建");  
218. true;//告知调用者连接已重建 
219. catch(SQLException e){  
220.                     System.out.println(e.getMessage());  
221. catch(Exception e){  
222. //other exceptions 
223.                 }     
224.             }  
225.         }  
226. return
227.     }  
228.           
229. void freeConn(int
230. try{  
231. if(i==-1){  
232. "i=-1,不需要释放");  
233. else{  
234. this.connections[i].commit();  
235.             }  
236. catch(SQLException e){  
237.             System.out.println(e.getMessage());  
238. catch(Exception e){  
239. //other exceptions 
240.         }  
241. this.connStatus[i]="Y";  
242.     }  
243. }

DBPoolConfiguration.java


1. package
2.  
3. public class
4. private
5. private
6. private
7. private
8. private int connCount;//连接数  
9.       
10. public
11. return
12.     }  
13. public void
14. this.cls = cls;  
15.     }  
16. public
17. return
18.     }  
19. public void
20. this.url = url;  
21.     }  
22. public
23. return
24.     }  
25. public void
26. this.usr = usr;  
27.     }  
28. public
29. return
30.     }  
31. public void
32. this.pss = pss;  
33.     }  
34. public int
35. return
36.     }  
37. public void setConnCount(int
38. this.connCount = connCount;  
39.     }  
40. }

DBPoolConnection.java:


1. package
2.  
3. import
4.  
5. public class
6. public int connNbr=-1;  
7. public Connection conn=null;  
8.     DBPoolConnection(){  
9. this.connNbr=-1;  
10. this.conn = null;  
11.     }  
12. }

DBPoolIsFullException.java



1. package
2.  
3. public class DBPoolIsFullException extends
4. static final long
5.     DBPoolIsFullException(String message){  
6. super(message);  
7.     }  
8. }

Test.java


1. package
2.  
3. import
4. import
5. import
6. import
7. import
8. import
9.  
10. public class
11. static String rpad(String str,int
12.         String s = str;  
13. if(s==null){  
14. "";  
15.         }  
16. while(s.getBytes().length<len){  
17. " ";  
18.         }  
19. return
20.     }  
21.       
22. public static void
23. //初始化 
24. "",url="",usr="",pss="",sql="";  
25. null;  
26. null;  
27. "|";//分隔符 
28. int connCount=2;//最大连接数 
29.  
30. //连接Oracle  配置 
31. "oracle.jdbc.driver.OracleDriver";  
32. "jdbc:oracle:thin:@localhost:1521:myoradb";  
33. "abc";  
34. "123";  
35.           
36. "select t.field_id,t.field_content,to_char(t.record_create_date,'YYYYMMDD') day from my_table t";  
37.  
38. new
39.         poolConfiguration.setCls(cls);  
40.         poolConfiguration.setUrl(url);  
41.         poolConfiguration.setUsr(usr);  
42.         poolConfiguration.setPss(pss);  
43.         poolConfiguration.setConnCount(connCount);  
44.           
45. new
46.         myPool.initPool();  
47.  
48.           
49. "");  
50. "after init Pool");  
51.         myPool.printPoolStatus();  
52. "");  
53.           
54. null;  
55.  
56. try{  
57.             c1 = myPool.getPoolConn();  
58.  
59. "");  
60. "after getPoolConn");  
61.             myPool.printPoolStatus();  
62. "");           
63.               
64.             stmt = c1.conn.createStatement();  
65.             rs = stmt.executeQuery(sql);  
66.  
67.             ResultSetMetaData md = rs.getMetaData();  
68.               
69. "";  
70. int
71. for(int i=1;i<=colCount;i++){  
72. if(recordHead.equals("")){  
73.                     recordHead += rpad(md.getColumnName(i),md.getColumnDisplaySize(i));  
74. else{  
75.                     recordHead+= gapStr + rpad(md.getColumnName(i),md.getColumnDisplaySize(i));  
76.                 }  
77.             }  
78. //打印表头 
79.               
80. while(rs.next()){  
81. "";  
82. for(int i=1;i<=colCount;i++){  
83. int
84. if(tmp.equals("")){  
85.                         tmp += rpad(rs.getString(i),colSize);  
86. else{  
87.                         tmp += gapStr + rpad(rs.getString(i),colSize);  
88.                     }  
89.                 }  
90. //打印数据 
91.             }  
92.               
93. //释放资源但是不关闭连接                       
94.             myPool.freePoolConn(c1);  
95.               
96. "");  
97. "after freePoolConn");  
98.             myPool.printPoolStatus();  
99. "");           
100. catch(Exception e){  
101.             System.out.println(e.getMessage());  
102.         }  
103.  
104.         myPool.freePool();  
105.  
106. //end of main 
107. }

下面是Test.java的执行结果:



1. initPool is
2.  
3. after
4. Y-[null]   
5. Y-[null]   
6.  
7.  
8. after
9. N-[Wed Mar 20 14:46:31 GMT 2013]   
10. Y-[null]   
11.  
12. FIELD_ID|FIELD_CONTENT                                               |DAY
13. 001     |this is first
14. 002     |this is second
15. 003     |this is
16.  
17. after
18. Y-[Wed Mar 20 14:46:31 GMT 2013]   
19. Y-[null]   
20.  
21. freePool is

 感谢您的耐心读到此处,我相信接下去的文字会更有价值。

保持连接池简单性的几个设计思想(不一定正确):

1)在系统中连接池不应作为一个独立的模块,最好是作为某模块的底层实现。这样可以将超时管理、请求队列、确保资源释放、数据分页(方言不可避免了)等功能剥离出去。

2)固定连接数比动态连接数容易实现,简单的代码更易于维护。

本例有待完善之处(供参考):

1)查询空闲连接要遍历数组,这样当池中的连接数很大时可能会有问题(具体没测过)

2)可以添加连接池阻塞的功能,有时候后台数据库需要重启,或者想重建连接池以提供更多的并发连接数?