DataSource连接池
1 // 2 // 一个效果非常不错的JAVA数据库连接池. 3 // from:http://www.jxer.com/home/?uid-195-action-viewspace-itemid-332 4 // 虽然现在用APACHE COMMONS DBCP可以非常方便的建立数据库连接池, 5 // 但是像这篇文章把数据库连接池的内部原理写的这么透彻,注视这么完整, 6 // 真是非常难得,让开发人员可以更深层次的理解数据库连接池,真是非常感 7 // 谢这篇文章的作者。 8 // 9 import java.sql.Connection; 10 import java.sql.DatabaseMetaData; 11 import java.sql.Driver; 12 import java.sql.DriverManager; 13 import java.sql.PreparedStatement; 14 import java.sql.ResultSet; 15 import java.sql.SQLException; 16 import java.sql.Statement; 17 import java.util.Enumeration; 18 import java.util.Vector; 19 @SuppressWarnings("rawtypes") 20 public class ConnectionPool { 21 22 private String jdbcDriver = ""; // 数据库驱动 23 private String dbUrl = ""; // 数据 URL 24 private String dbUsername = ""; // 数据库用户名 25 private String dbPassword = ""; // 数据库用户密码 26 private String testTable = ""; // 测试连接是否可用的测试表名,默认没有测试表 27 private int initialConnections = 10; // 连接池的初始大小 28 private int incrementalConnections = 5; // 连接池自动增加的大小 29 private int maxConnections = 50; // 连接池最大的大小 30 31 private Vector connections = null; // 存放连接池中数据库连接的向量 , 初始时为 null 32 // 它中存放的对象为 PooledConnection 型 33 34 35 public ConnectionPool(String jdbcDriver, String dbUrl, String dbUsername, 36 String dbPassword) { 37 38 this.jdbcDriver = jdbcDriver; 39 this.dbUrl = dbUrl; 40 this.dbUsername = dbUsername; 41 this.dbPassword = dbPassword; 42 } 43 44 45 public int getInitialConnections() { 46 return this.initialConnections; 47 } 48 49 50 public void setInitialConnections(int initialConnections) { 51 this.initialConnections = initialConnections; 52 } 53 54 55 public int getIncrementalConnections() { 56 return this.incrementalConnections; 57 } 58 59 60 public void setIncrementalConnections(int incrementalConnections) { 61 this.incrementalConnections = incrementalConnections; 62 } 63 64 65 public int getMaxConnections() { 66 return this.maxConnections; 67 } 68 69 70 public void setMaxConnections(int maxConnections) { 71 this.maxConnections = maxConnections; 72 } 73 74 75 public String getTestTable() { 76 return this.testTable; 77 } 78 79 80 public void setTestTable(String testTable) { 81 this.testTable = testTable; 82 } 83 84 85 public synchronized void createPool() throws Exception { 86 // 确保连接池没有创建 87 // 假如连接池己经创建了,保存连接的向量 connections 不会为空 88 if (connections != null) { 89 return; // 假如己经创建,则返回 90 } 91 // 实例化 JDBC Driver 中指定的驱动类实例 92 Driver driver = (Driver) (Class.forName(this.jdbcDriver).newInstance()); 93 DriverManager.registerDriver(driver); // 注册 JDBC 驱动程序 94 // 创建保存连接的向量 , 初始时有 0 个元素 95 connections = new Vector(); 96 // 根据 initialConnections 中设置的值,创建连接。 97 createConnections(this.initialConnections); 98 System.out.println(" 数据库连接池创建成功! "); 99 } 100 101 102 @SuppressWarnings("unchecked") 103 private void createConnections(int numConnections) throws SQLException { 104 // 循环创建指定数目的数据库连接 105 for (int x = 0; x < numConnections; x++) { 106 // 是否连接池中的数据库连接的数量己经达到最大?最大值由类成员 maxConnections 107 // 指出,假如 maxConnections 为 0 或负数,表示连接数量没有限制。 108 // 假如连接数己经达到最大,即退出。 109 if (this.maxConnections > 0 && 110 this.connections.size() >= this.maxConnections) { 111 break; 112 } 113 //add a new PooledConnection object to connections vector 114 // 增加一个连接到连接池中(向量 connections 中) 115 try { 116 connections.addElement(new PooledConnection(newConnection())); 117 } catch (SQLException e) { 118 System.out.println(" 创建数据库连接失败! " + e.getMessage()); 119 throw new SQLException(); 120 } 121 System.out.println(" 数据库连接己创建 ......"); 122 } 123 } 124 125 126 private Connection newConnection() throws SQLException { 127 // 创建一个数据库连接 128 Connection conn = DriverManager.getConnection(dbUrl, dbUsername, 129 dbPassword); 130 // 假如这是第一次创建数据库连接,即检查数据库,获得此数据库答应支持的 131 // 最大客户连接数目 132 //connections.size()==0 表示目前没有连接己被创建 133 if (connections.size() == 0) { 134 DatabaseMetaData metaData = conn.getMetaData(); 135 int driverMaxConnections = metaData.getMaxConnections(); 136 // 数据库返回的 driverMaxConnections 若为 0 ,表示此数据库没有最大 137 // 连接限制,或数据库的最大连接限制不知道 138 //driverMaxConnections 为返回的一个整数,表示此数据库答应客户连接的数目 139 // 假如连接池中设置的最大连接数量大于数据库答应的连接数目 , 则置连接池的最大 140 // 连接数目为数据库答应的最大数目 141 if (driverMaxConnections > 0 && 142 this.maxConnections > driverMaxConnections) { 143 this.maxConnections = driverMaxConnections; 144 } 145 } 146 return conn; // 返回创建的新的数据库连接 147 } 148 149 150 151 public synchronized Connection getConnection() throws SQLException { 152 // 确保连接池己被创建 153 if (connections == null) { 154 return null; // 连接池还没创建,则返回 null 155 } 156 Connection conn = getFreeConnection(); // 获得一个可用的数据库连接 157 // 假如目前没有可以使用的连接,即所有的连接都在使用中 158 while (conn == null) { 159 // 等一会再试 160 wait(250); 161 conn = getFreeConnection(); // 重新再试,直到获得可用的连接,假如 162 //getFreeConnection() 返回的为 null 163 // 则表明创建一批连接后也不可获得可用连接 164 } 165 return conn; // 返回获得的可用的连接 166 } 167 168 169 private Connection getFreeConnection() throws SQLException { 170 // 从连接池中获得一个可用的数据库连接 171 Connection conn = findFreeConnection(); 172 if (conn == null) { 173 // 假如目前连接池中没有可用的连接 174 // 创建一些连接 175 createConnections(incrementalConnections); 176 // 重新从池中查找是否有可用连接 177 conn = findFreeConnection(); 178 if (conn == null) { 179 // 假如创建连接后仍获得不到可用的连接,则返回 null 180 return null; 181 } 182 } 183 return conn; 184 } 185 186 187 @SuppressWarnings("resource") 188 private Connection findFreeConnection() throws SQLException { 189 Connection conn = null; 190 PooledConnection pConn = null; 191 // 获得连接池向量中所有的对象 192 Enumeration enumerate = connections.elements(); 193 // 遍历所有的对象,看是否有可用的连接 194 while (enumerate.hasMoreElements()) { 195 pConn = (PooledConnection) enumerate.nextElement(); 196 if (!pConn.isBusy()) { 197 // 假如此对象不忙,则获得它的数据库连接并把它设为忙 198 conn = pConn.getConnection(); 199 pConn.setBusy(true); 200 // 测试此连接是否可用 201 if (!testConnection(conn)) { 202 // 假如此连接不可再用了,则创建一个新的连接, 203 // 并替换此不可用的连接对象,假如创建失败,返回 null 204 try { 205 conn = newConnection(); 206 } catch (SQLException e) { 207 System.out.println(" 创建数据库连接失败! " + e.getMessage()); 208 return null; 209 } 210 pConn.setConnection(conn); 211 } 212 break; // 己经找到一个可用的连接,退出 213 } 214 } 215 return conn; // 返回找到到的可用连接 216 } 217 218 219 private boolean testConnection(Connection conn) { 220 try { 221 // 判定测试表是否存在 222 if (testTable.equals("")) { 223 // 假如测试表为空,试着使用此连接的 setAutoCommit() 方法 224 // 来判定连接否可用(此方法只在部分数据库可用,假如不可用 , 225 // 抛出异常)。注重:使用测试表的方法更可靠 226 conn.setAutoCommit(true); 227 } else { // 有测试表的时候使用测试表测试 228 //check if this connection is valid 229 Statement stmt = conn.createStatement(); 230 stmt.execute("select count(*) from " + testTable); 231 } 232 } catch (SQLException e) { 233 // 上面抛出异常,此连接己不可用,关闭它,并返回 false; 234 closeConnection(conn); 235 return false; 236 } 237 // 连接可用,返回 true 238 return true; 239 } 240 241 242 public void returnConnection(Connection conn) { 243 // 确保连接池存在,假如连接没有创建(不存在),直接返回 244 if (connections == null) { 245 System.out.println(" 连接池不存在,无法返回此连接到连接池中 !"); 246 return; 247 } 248 PooledConnection pConn = null; 249 Enumeration enumerate = connections.elements(); 250 // 遍历连接池中的所有连接,找到这个要返回的连接对象 251 while (enumerate.hasMoreElements()) { 252 pConn = (PooledConnection) enumerate.nextElement(); 253 // 先找到连接池中的要返回的连接对象 254 if (conn == pConn.getConnection()) { 255 // 找到了 , 设置此连接为空闲状态 256 pConn.setBusy(false); 257 break; 258 } 259 } 260 } 261 262 263 264 public synchronized void refreshConnections() throws SQLException { 265 // 确保连接池己创新存在 266 if (connections == null) { 267 System.out.println(" 连接池不存在,无法刷新 !"); 268 return; 269 } 270 PooledConnection pConn = null; 271 Enumeration enumerate = connections.elements(); 272 while (enumerate.hasMoreElements()) { 273 // 获得一个连接对象 274 pConn = (PooledConnection) enumerate.nextElement(); 275 // 假如对象忙则等 5 秒 ,5 秒后直接刷新 276 if (pConn.isBusy()) { 277 wait(5000); // 等 5 秒 278 } 279 // 关闭此连接,用一个新的连接代替它。 280 closeConnection(pConn.getConnection()); 281 pConn.setConnection(newConnection()); 282 pConn.setBusy(false); 283 } 284 } 285 286 287 public synchronized void closeConnectionPool() throws SQLException { 288 // 确保连接池存在,假如不存在,返回 289 if (connections == null) { 290 System.out.println(" 连接池不存在,无法关闭 !"); 291 return; 292 } 293 PooledConnection pConn = null; 294 Enumeration enumerate = connections.elements(); 295 while (enumerate.hasMoreElements()) { 296 pConn = (PooledConnection) enumerate.nextElement(); 297 // 假如忙,等 5 秒 298 if (pConn.isBusy()) { 299 wait(5000); // 等 5 秒 300 } 301 //5 秒后直接关闭它 302 closeConnection(pConn.getConnection()); 303 // 从连接池向量中删除它 304 connections.removeElement(pConn); 305 } 306 // 置连接池为空 307 connections = null; 308 } 309 310 311 private void closeConnection(Connection conn) { 312 try { 313 conn.close(); 314 } catch (SQLException e) { 315 System.out.println(" 关闭数据库连接出错: " + e.getMessage()); 316 } 317 } 318 319 320 private void wait(int mSeconds) { 321 try { 322 Thread.sleep(mSeconds); 323 } catch (InterruptedException e) { 324 } 325 } 326 327 328 329 class PooledConnection { 330 Connection connection = null; // 数据库连接 331 boolean busy = false; // 此连接是否正在使用的标志,默认没有正在使用 332 // 构造函数,根据一个 Connection 构造一个 PooledConnection 对象 333 public PooledConnection(Connection connection) { 334 this.connection = connection; 335 } 336 337 // 返回此对象中的连接 338 public Connection getConnection() { 339 return connection; 340 } 341 342 // 设置此对象的,连接 343 public void setConnection(Connection connection) { 344 this.connection = connection; 345 } 346 347 // 获得对象连接是否忙 348 public boolean isBusy() { 349 return busy; 350 } 351 352 // 设置对象的连接正在忙 353 public void setBusy(boolean busy) { 354 this.busy = busy; 355 } 356 } 357 358 359 public static void main(String[] args) { 360 361 ConnectionPool connPool= new ConnectionPool("oracle.jdbc.OracleDriver", 362 "jdbc:oracle:thin:@*:1521:orcl" 363 , "*", "*"); 364 try { 365 connPool.createPool(); 366 } catch (Exception ex) { 367 ex.printStackTrace(); 368 } 369 try { 370 Connection conn = connPool.getConnection(); 371 PreparedStatement preparedStatement=conn.prepareStatement("select * from user_files_flow"); 372 ResultSet result=preparedStatement.executeQuery(); 373 while (result.next()) { 374 System.out.println(result.getString("id")); 375 } 376 System.out.println(conn); 377 } catch (SQLException ex1) { 378 ex1.printStackTrace(); 379 } 380 381 } 382 383 }
作者:唐 冰(Tobey) (http://www.cnblogs.com/tobey/)
版权声明:本文的版权归作者与博客园共同所有。转载时请在明显地方注明本文的详细链接,未经作者同意请不要删除此段声明,感谢您为保护知识产权做出的贡献。