MySQL 数据库连接池

MySQL 数据库连接池

数据库连接池(connection pool)的工作原理


资源池(Resource Pool)。该模式正是为了解决资源的频繁分配﹑释放所造成的问题。为解决上述问题,可以采用数据库连接池技术。数据库连接池的基本思想就是为数据库连接 建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。我们可以通过设定 连接池最大连接数来防止系统无尽的与数据库连接。更为重要的是我们可以通过连接池的管理机制监视数据库的连接的数量﹑使用情况,为系统开发﹑测试及性能调 整提供依据。连接池的基本工作原理见下图2。


实例:

复制代码
  1 import java.sql.Connection;
  2 import java.sql.DatabaseMetaData;
  3 import java.sql.Driver;
  4 import java.sql.DriverManager;
  5 import java.sql.SQLException;
  6 import java.sql.Statement;
  7 import java.util.Enumeration;
  8 import java.util.Vector;
  9 
 10 public class ConnectionPool {
 11     private String jdbcDriver = ""; // 数据库驱动
 12     private String dbUrl = ""; // 数据 URL
 13     private String dbUsername = ""; // 数据库用户名
 14     private String dbPassword = ""; // 数据库用户密码
 15     private String testTable = ""; // 测试连接是否可用的测试表名,默认没有测试表
 16     private int initialConnections = 10; // 连接池的初始大小
 17     private int incrementalConnections = 5;// 连接池自动增加的大小
 18     private int maxConnections = 50; // 连接池最大的大小
 19     private Vector connections = null; // 存放连接池中数据库连接的向量 , 初始时为 null
 20     // 它中存放的对象为 PooledConnection 型
 21 
 22     /**
 23      * 构造函数
 24      * 
 25      * @param jdbcDriver
 26      *            String JDBC 驱动类串
 27      * @param dbUrl
 28      *            String 数据库 URL
 29      * @param dbUsername
 30      *            String 连接数据库用户名
 31      * @param dbPassword
 32      *            String 连接数据库用户的密码
 33      * 
 34      */
 35     public ConnectionPool(String jdbcDriver, String dbUrl, String dbUsername,
 36             String dbPassword) {
 37         this.jdbcDriver = jdbcDriver;
 38         this.dbUrl = dbUrl;
 39         this.dbUsername = dbUsername;
 40         this.dbPassword = dbPassword;
 41     }
 42 
 43     /**
 44      * 返回连接池的初始大小
 45      * 
 46      * @return 初始连接池中可获得的连接数量
 47      */
 48     public int getInitialConnections() {
 49         return this.initialConnections;
 50     }
 51 
 52     /**
 53      * 设置连接池的初始大小
 54      * 
 55      * @param 用于设置初始连接池中连接的数量
 56      */
 57 
 58     public void setInitialConnections(int initialConnections) {
 59         this.initialConnections = initialConnections;
 60     }
 61 
 62     /**
 63      * 返回连接池自动增加的大小 、
 64      * 
 65      * @return 连接池自动增加的大小
 66      */
 67     public int getIncrementalConnections() {
 68         return this.incrementalConnections;
 69     }
 70 
 71     /**
 72      * 设置连接池自动增加的大小
 73      * 
 74      * @param 连接池自动增加的大小
 75      */
 76 
 77     public void setIncrementalConnections(int incrementalConnections) {
 78         this.incrementalConnections = incrementalConnections;
 79     }
 80 
 81     /**
 82      * 返回连接池中最大的可用连接数量
 83      * 
 84      * @return 连接池中最大的可用连接数量
 85      */
 86 
 87     public int getMaxConnections() {
 88         return this.maxConnections;
 89     }
 90 
 91     /**
 92      * 设置连接池中最大可用的连接数量
 93      * 
 94      * @param 设置连接池中最大可用的连接数量值
 95      */
 96 
 97     public void setMaxConnections(int maxConnections) {
 98         this.maxConnections = maxConnections;
 99     }
100 
101     /**
102      * 获取测试数据库表的名字
103      * 
104      * @return 测试数据库表的名字
105      */
106 
107     public String getTestTable() {
108         return this.testTable;
109     }
110 
111     /**
112      * 设置测试表的名字
113      * 
114      * @param testTable
115      *            String 测试表的名字
116      */
117 
118     public void setTestTable(String testTable) {
119         this.testTable = testTable;
120     }
121 
122     /**
123      * 
124      * 创建一个数据库连接池,连接池中的可用连接的数量采用类成员 initialConnections 中设置的值
125      */
126 
127     public synchronized void createPool() throws Exception {
128         // 确保连接池没有创建
129         // 如果连接池己经创建了,保存连接的向量 connections 不会为空
130         if (connections != null) {
131             return; // 如果己经创建,则返回
132         }
133         // 实例化 JDBC Driver 中指定的驱动类实例
134         Driver driver = (Driver) (Class.forName(this.jdbcDriver).newInstance());
135         DriverManager.registerDriver(driver); // 注册 JDBC 驱动程序
136         // 创建保存连接的向量 , 初始时有 0 个元素
137         connections = new Vector();
138         // 根据 initialConnections 中设置的值,创建连接。
139         createConnections(this.initialConnections);
140         // System.out.println(" 数据库连接池创建成功! ");
141     }
142 
143     /**
144      * 创建由 numConnections 指定数目的数据库连接 , 并把这些连接 放入 connections 向量中
145      * 
146      * @param numConnections
147      *            要创建的数据库连接的数目
148      */
149 
150     @SuppressWarnings("unchecked")
151     private void createConnections(int numConnections) throws SQLException {
152         // 循环创建指定数目的数据库连接
153         for (int x = 0; x < numConnections; x++) {
154             // 是否连接池中的数据库连接的数量己经达到最大?最大值由类成员 maxConnections
155             // 指出,如果 maxConnections 为 0 或负数,表示连接数量没有限制。
156             // 如果连接数己经达到最大,即退出。
157             if (this.maxConnections > 0
158                     && this.connections.size() >= this.maxConnections) {
159                 break;
160             }
161             // add a new PooledConnection object to connections vector
162             // 增加一个连接到连接池中(向量 connections 中)
163             try {
164                 connections.addElement(new PooledConnection(newConnection()));
165             } catch (SQLException e) {
166                 System.out.println(" 创建数据库连接失败! " + e.getMessage());
167                 throw new SQLException();
168             }
169             // System.out.println(" 数据库连接己创建 ......");
170         }
171     }
172 
173     /**
174      * 创建一个新的数据库连接并返回它
175      * 
176      * @return 返回一个新创建的数据库连接
177      */
178 
179     private Connection newConnection() throws SQLException {
180         // 创建一个数据库连接
181         Connection conn = DriverManager.getConnection(dbUrl, dbUsername,
182                 dbPassword);
183         // 如果这是第一次创建数据库连接,即检查数据库,获得此数据库允许支持的
184         // 最大客户连接数目
185         // connections.size()==0 表示目前没有连接己被创建
186         if (connections.size() == 0) {
187             DatabaseMetaData metaData = conn.getMetaData();
188             int driverMaxConnections = metaData.getMaxConnections();
189             // 数据库返回的 driverMaxConnections 若为 0 ,表示此数据库没有最大
190             // 连接限制,或数据库的最大连接限制不知道
191             // driverMaxConnections 为返回的一个整数,表示此数 据库允许客户连接的数目
192             // 如果连接池中设置的最大连接数量大于数据库允许的连接数目 , 则置连接池的最大
193             // 连接数目为数据库允许的最大数目
194             if (driverMaxConnections > 0
195                     && this.maxConnections > driverMaxConnections) {
196                 this.maxConnections = driverMaxConnections;
197             }
198         }
199         return conn; // 返回创建的新的数据库连接
200     }
201 
202     /**
203      * 通过调用 getFreeConnection() 函数返回一个可用的数据库连接 , 如果当前没有可用的数据库连接,并且更多的数据库连接不能创
204      * 建(如连接池大小的限制),此函数等待一会再尝试获取。
205      * 
206      * @return 返回一个可用的数据库连接对象
207      */
208 
209     public synchronized Connection getConnection() throws SQLException {
210         // 确保连接池己被创建
211         if (connections == null) {
212             return null; // 连接池还没创建,则返回 null
213         }
214         Connection conn = getFreeConnection(); // 获得一个可用的数据库连接
215         // 如果目前没有可以使用的连接,即所有的连接都在使用中
216         while (conn == null) {
217             // 等一会再试
218             // System.out.println("Wait");
219             wait(250);
220             conn = getFreeConnection(); // 重新再试,直到获得可用的连接,如果
221             // getFreeConnection() 返回的为 null
222             // 则表明创建一批连接后也不可获得可用连接
223         }
224         return conn;// 返回获得的可用的连接
225     }
226 
227     /**
228      * 本函数从连接池向量 connections 中返回一个可用的的数据库连接,如果 当前没有可用的数据库连接,本函数则根据
229      * incrementalConnections 设置 的值创建几个数据库连接,并放入连接池中。 如果创建后,所有的连接仍都在使用中,则返回 null
230      * 
231      * @return 返回一个可用的数据库连接
232      */
233     private Connection getFreeConnection() throws SQLException {
234         // 从连接池中获得一个可用的数据库连接
235         Connection conn = findFreeConnection();
236         if (conn == null) {
237             // 如果目前连接池中没有可用的连接
238             // 创建一些连接
239             createConnections(incrementalConnections);
240             // 重新从池中查找是否有可用连接
241             conn = findFreeConnection();
242             if (conn == null) {
243                 // 如果创建连接后仍获得不到可用的连接,则返回 null
244                 return null;
245             }
246         }
247         return conn;
248     }
249 
250     /**
251      * 查找连接池中所有的连接,查找一个可用的数据库连接, 如果没有可用的连接,返回 null
252      * 
253      * @return 返回一个可用的数据库连接
254      */
255 
256     private Connection findFreeConnection() throws SQLException {
257         Connection conn = null;
258         PooledConnection pConn = null;
259         // 获得连接池向量中所有的对象
260         Enumeration enumerate = connections.elements();
261         // 遍历所有的对象,看是否有可用的连接
262         while (enumerate.hasMoreElements()) {
263             pConn = (PooledConnection) enumerate.nextElement();
264             if (!pConn.isBusy()) {
265                 // 如果此对象不忙,则获得它的数据库连接并把它设为忙
266                 conn = pConn.getConnection();
267                 pConn.setBusy(true);
268                 // 测试此连接是否可用
269                 if (!testConnection(conn)) {
270                     // 如果此连接不可再用了,则创建一个新的连接,
271                     // 并替换此不可用的连接对象,如果创建失败,返回 null
272                     try {
273                         conn = newConnection();
274                     } catch (SQLException e) {
275                         System.out.println(" 创建数据库连接失败! " + e.getMessage());
276                         return null;
277                     }
278                     pConn.setConnection(conn);
279                 }
280                 break; // 己经找到一个可用的连接,退出
281             }
282         }
283         return conn;// 返回找到到的可用连接
284     }
285 
286     /**
287      * 测试一个连接是否可用,如果不可用,关掉它并返回 false 否则可用返回 true
288      * 
289      * @param conn
290      *            需要测试的数据库连接
291      * @return 返回 true 表示此连接可用, false 表示不可用
292      */
293 
294     private boolean testConnection(Connection conn) {
295         try {
296             // 判断测试表是否存在
297             if (testTable.equals("")) {
298                 // 如果测试表为空,试着使用此连接的 setAutoCommit() 方法
299                 // 来判断连接否可用(此方法只在部分数据库可用,如果不可用 ,
300                 // 抛出异常)。注意:使用测试表的方法更可靠
301                 conn.setAutoCommit(true);
302             } else {// 有测试表的时候使用测试表测试
303                 // check if this connection is valid
304                 Statement stmt = conn.createStatement();
305                 stmt.execute("select count(*) from " + testTable);
306             }
307         } catch (SQLException e) {
308             // 上面抛出异常,此连接己不可用,关闭它,并返回 false;
309             closeConnection(conn);
310             return false;
311         }
312         // 连接可用,返回 true
313         return true;
314     }
315 
316     /**
317      * 此函数返回一个数据库连接到连接池中,并把此连接置为空闲。 所有使用连接池获得的数据库连接均应在不使用此连接时返回它。
318      * 
319      * @param 需返回到连接池中的连接对象
320      */
321 
322     public void returnConnection(Connection conn) {
323         // 确保连接池存在,如果连接没有创建(不存在),直接返回
324         if (connections == null) {
325             System.out.println(" 连接池不存在,无法返回此连接到连接池中 !");
326             return;
327         }
328         PooledConnection pConn = null;
329         Enumeration enumerate = connections.elements();
330         // 遍历连接池中的所有连接,找到这个要返回的连接对象
331         while (enumerate.hasMoreElements()) {
332             pConn = (PooledConnection) enumerate.nextElement();
333             // 先找到连接池中的要返回的连接对象
334             if (conn == pConn.getConnection()) {
335                 // 找到了 , 设置此连接为空闲状态
336                 pConn.setBusy(false);
337                 break;
338             }
339         }
340     }
341 
342     /**
343      * 刷新连接池中所有的连接对象
344      * 
345      */
346 
347     public synchronized void refreshConnections() throws SQLException {
348         // 确保连接池己创新存在
349         if (connections == null) {
350             System.out.println(" 连接池不存在,无法刷新 !");
351             return;
352         }
353         PooledConnection pConn = null;
354         Enumeration enumerate = connections.elements();
355         while (enumerate.hasMoreElements()) {
356             // 获得一个连接对象
357             pConn = (PooledConnection) enumerate.nextElement();
358             // 如果对象忙则等 5 秒 ,5 秒后直接刷新
359             if (pConn.isBusy()) {
360                 wait(5000); // 等 5 秒
361             }
362             // 关闭此连接,用一个新的连接代替它。
363             closeConnection(pConn.getConnection());
364             pConn.setConnection(newConnection());
365             pConn.setBusy(false);
366         }
367     }
368 
369     /**
370      * 关闭连接池中所有的连接,并清空连接池。
371      */
372 
373     public synchronized void closeConnectionPool() throws SQLException {
374         // 确保连接池存在,如果不存在,返回
375         if (connections == null) {
376             System.out.println(" 连接池不存在,无法关闭 !");
377             return;
378         }
379         PooledConnection pConn = null;
380         Enumeration enumerate = connections.elements();
381         while (enumerate.hasMoreElements()) {
382             pConn = (PooledConnection) enumerate.nextElement();
383             // 如果忙,等 5 秒
384             if (pConn.isBusy()) {
385                 wait(5000); // 等 5 秒
386             }
387             // 5 秒后直接关闭它
388             closeConnection(pConn.getConnection());
389             // 从连接池向量中删除它
390             connections.removeElement(pConn);
391         }
392         // 置连接池为空
393         connections = null;
394     }
395 
396     /**
397      * 关闭一个数据库连接
398      * 
399      * @param 需要关闭的数据库连接
400      */
401 
402     private void closeConnection(Connection conn) {
403         try {
404             conn.close();
405         } catch (SQLException e) {
406             System.out.println(" 关闭数据库连接出错: " + e.getMessage());
407         }
408     }
409 
410     /**
411      * 使程序等待给定的毫秒数
412      * 
413      * @param 给定的毫秒数
414      */
415 
416     private void wait(int mSeconds) {
417         try {
418             Thread.sleep(mSeconds);
419         } catch (InterruptedException e) {
420         }
421     }
422 
423     /**
424      * 
425      * 内部使用的用于保存连接池中连接对象的类 此类中有两个成员,一个是数据库的连接,另一个是指示此连接是否 正在使用的标志。
426      */
427 
428     class PooledConnection {
429         Connection connection = null;// 数据库连接
430         boolean busy = false; // 此连接是否正在使用的标志,默认没有正在使用
431 
432         // 构造函数,根据一个 Connection 构告一个 PooledConnection 对象
433         public PooledConnection(Connection connection) {
434             this.connection = connection;
435         }
436 
437         // 返回此对象中的连接
438         public Connection getConnection() {
439             return connection;
440         }
441 
442         // 设置此对象的,连接
443         public void setConnection(Connection connection) {
444             this.connection = connection;
445         }
446 
447         // 获得对象连接是否忙
448         public boolean isBusy() {
449             return busy;
450         }
451 
452         // 设置对象的连接正在忙
453         public void setBusy(boolean busy) {
454             this.busy = busy;
455         }
456     }
457 
458 }
复制代码

测试类:

复制代码
 1 import java.sql.Connection;
 2 import java.sql.DriverManager;
 3 import java.sql.ResultSet;
 4 import java.sql.SQLException;
 5 import java.sql.Statement;
 6 
 7 public class ConnecttionPoolTest {
 8     /**
 9      * @param args
10      * @throws Exception
11      */
12     public static void main(String[] args) throws Exception {
13         // TODO Auto-generated method stub
14         try {
15             // 创建数据库连接库对象
16             ConnectionPool connPool = new ConnectionPool(
17                     "com.mysql.jdbc.Driver",
18                     "jdbc:mysql://localhost:3306/test", "root", "123456");
19             // 新建数据库连接库
20             connPool.createPool();
21             // SQL测试语句
22             String sql = "Select * from pet";
23             // 设定程序运行起始时间
24             long start = System.currentTimeMillis();
25             // 循环测试100次数据库连接
26             for (int i = 0; i < 100; i++) {
27                 Connection conn = connPool.getConnection(); // 从连接库中获取一个可用的连接
28                 Statement stmt = conn.createStatement();
29                 ResultSet rs = stmt.executeQuery(sql);
30                 while (rs.next()) {
31                     String name = rs.getString("name");
32                   //  System.out.println("查询结果" + name);
33                 }
34                 rs.close();
35                 stmt.close();
36                 connPool.returnConnection(conn);// 连接使用完后释放连接到连接池
37             }
38             System.out.println("经过100次的循环调用,使用连接池花费的时间:"
39                     + (System.currentTimeMillis() - start) + "ms");
40             // connPool.refreshConnections();//刷新数据库连接池中所有连接,即不管连接是否正在运行,都把所有连接都释放并放回到连接池。注意:这个耗时比较大。
41             connPool.closeConnectionPool();// 关闭数据库连接池。注意:这个耗时比较大。
42             // 设定程序运行起始时间
43             start = System.currentTimeMillis();
44             // 导入驱动
45             Class.forName("com.mysql.jdbc.Driver");
46             for (int i = 0; i < 100; i++) {
47                 // 创建连接
48                 Connection conn = DriverManager.getConnection(
49                         "jdbc:mysql://localhost:3306/test", "root", "123456");
50                 Statement stmt = conn.createStatement();
51                 ResultSet rs = stmt.executeQuery(sql);
52                 while (rs.next()) {
53                 }
54                 rs.close();
55                 stmt.close();
56                 conn.close();// 关闭连接
57             }
58             System.out.println("经过100次的循环调用,不使用连接池花费的时间:"
59                     + (System.currentTimeMillis() - start) + "ms");
60         } catch (SQLException e) {
61             e.printStackTrace();
62         } catch (ClassNotFoundException e) {
63             e.printStackTrace();
64         }
65     }
66 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值