随笔 - 45, 文章 - 6, 评论 - 4, 引用 - 0
数据加载中……

一个效果非常不错的JAVA数据库连接池

虽然现在用APACHE COMMONS DBCP可以非常方便的建立数据库连接池,
但是像这篇文章把数据库连接池的内部原理写的这么透彻,注视这么完整,
真是非常难得,让开发人员可以更深层次的理解数据库连接池,真是非常感
谢这篇文章的作者。

  1import java.sql.Connection;
  2import java.sql.DatabaseMetaData;
  3import java.sql.Driver;
  4import java.sql.DriverManager;
  5import java.sql.SQLException;
  6import java.sql.Statement;
  7import java.util.Enumeration;
  8import java.util.Vector;
  9
 10public class ConnectionPool {
 11
 12    private String _jdbcDriver = ""// 数据库驱动
 13
 14    private String _dbUrl = ""// 数据 URL
 15
 16    private String _dbUsername = ""// 数据库用户名
 17
 18    private String _dbPassword = ""// 数据库用户密码
 19
 20    private String _testTable = ""// 测试连接是否可用的测试表名,默认没有测试表
 21
 22    private int _initialConnections = 20// 连接池的初始大小
 23
 24    private int _incrementalConnections = 5;// 连接池自动增加的大小
 25
 26    private int _maxConnections = 50// 连接池最大的大小
 27
 28    private Vector _connectionVector = null// 存放连接池中数据库连接的向量 , 初始时为 null
 29
 30    // 它中存放的对象为 PooledConnection 型
 31
 32    /**
 33     * 
 34     * 构造函数
 35     * 
 36     * 
 37     * 
 38     * @param jdbcDriver
 39     *            String JDBC 驱动类串
 40     * 
 41     * @param dbUrl
 42     *            String 数据库 URL
 43     * 
 44     * @param dbUsername
 45     *            String 连接数据库用户名
 46     * 
 47     * @param dbPassword
 48     *            String 连接数据库用户的密码
 49     * 
 50     * 
 51     * 
 52     */

 53
 54    public ConnectionPool(String jdbcDriver, String dbUrl, String dbUsername,
 55            String dbPassword) {
 56        
 57        this._jdbcDriver = jdbcDriver;
 58
 59        this._dbUrl = dbUrl;
 60
 61        this._dbUsername = dbUsername;
 62
 63        this._dbPassword = dbPassword;
 64
 65    }

 66
 67    /**
 68     * 
 69     * 返回连接池的初始大小
 70     * 
 71     * 
 72     * 
 73     * @return 初始连接池中可获得的连接数量
 74     * 
 75     */

 76
 77    public int getInitialConnections() {
 78
 79        return this._initialConnections;
 80
 81    }

 82
 83    /**
 84     * 
 85     * 设置连接池的初始大小
 86     * 
 87     * 
 88     * 
 89     * @param 用于设置初始连接池中连接的数量
 90     * 
 91     */

 92
 93    public void setInitialConnections(int initialConnections) {
 94
 95        this._initialConnections = initialConnections;
 96
 97    }

 98
 99    /**
100     * 
101     * 返回连接池自动增加的大小
102     * 
103     * 
104     * 
105     * @return 连接池自动增加的大小
106     * 
107     */

108
109    public int getIncrementalConnections() {
110
111        return this._incrementalConnections;
112
113    }

114
115    /**
116     * 
117     * 设置连接池自动增加的大小
118     * 
119     * @param 连接池自动增加的大小
120     * 
121     */

122
123    public void setIncrementalConnections(int incrementalConnections) {
124
125        this._incrementalConnections = incrementalConnections;
126
127    }

128
129    /**
130     * 
131     * 返回连接池中最大的可用连接数量
132     * 
133     * @return 连接池中最大的可用连接数量
134     * 
135     */

136
137    public int getMaxConnections() {
138
139        return this._maxConnections;
140
141    }

142
143    /**
144     * 
145     * 设置连接池中最大可用的连接数量
146     * 
147     * 
148     * 
149     * @param 设置连接池中最大可用的连接数量值
150     * 
151     */

152
153    public void setMaxConnections(int maxConnections) {
154
155        this._maxConnections = maxConnections;
156
157    }

158
159    /**
160     * 
161     * 获取测试数据库表的名字
162     * 
163     * 
164     * 
165     * @return 测试数据库表的名字
166     * 
167     */

168
169    public String getTestTable() {
170
171        return this._testTable;
172
173    }

174
175    /**
176     * 
177     * 设置测试表的名字
178     * 
179     * @param testTable
180     *            String 测试表的名字
181     * 
182     */

183
184    public void setTestTable(String testTable) {
185
186        this._testTable = testTable;
187
188    }

189
190    /**
191     * 
192     * 
193     * 
194     * 创建一个数据库连接池,连接池中的可用连接的数量采用类成员
195     * 
196     * initialConnections 中设置的值
197     * 
198     */

199
200    public synchronized void createPool() throws Exception {
201
202        // 确保连接池没有创建
203
204        // 如果连接池己经创建了,保存连接的向量 connections 不会为空
205
206        if (_connectionVector != null{
207
208            return// 如果己经创建,则返回
209
210        }

211
212        // 实例化 JDBC Driver 中指定的驱动类实例
213
214        Driver driver = (Driver) (Class.forName(this._jdbcDriver).newInstance());
215
216        DriverManager.registerDriver(driver); // 注册 JDBC 驱动程序
217
218        // 创建保存连接的向量 , 初始时有 0 个元素
219
220        _connectionVector = new Vector();
221
222        // 根据 initialConnections 中设置的值,创建连接。
223
224        createConnections(this._initialConnections);
225
226        System.out.println(" 数据库连接池创建成功! ");
227
228    }

229
230    /**
231     * 
232     * 创建由 numConnections 指定数目的数据库连接 , 并把这些连接
233     * 
234     * 放入 connections 向量中
235     * 
236     * 
237     * 
238     * @param numConnections
239     *            要创建的数据库连接的数目
240     * 
241     */

242
243    @SuppressWarnings("unchecked")
244    private void createConnections(int numConnections) throws SQLException {
245
246        // 循环创建指定数目的数据库连接
247
248        for (int x = 0; x < numConnections; x++{
249
250            // 是否连接池中的数据库连接的数量己经达到最大?最大值由类成员 maxConnections
251
252            // 指出,如果 maxConnections 为 0 或负数,表示连接数量没有限制。
253
254            // 如果连接数己经达到最大,即退出。
255
256            if (this._maxConnections > 0
257                    && this._connectionVector.size() >= this._maxConnections) {
258
259                break;
260
261            }

262
263            // add a new PooledConnection object to connections vector
264
265            // 增加一个连接到连接池中(向量 connections 中)
266
267            try {
268
269                _connectionVector.addElement(new PooledConnection(newConnection()));
270
271            }
 catch (SQLException e) {
272
273                System.out.println(" 创建数据库连接失败! " + e.getMessage());
274
275                throw new SQLException();
276
277            }

278
279            System.out.println(" 数据库连接己创建 ");
280
281        }

282
283    }

284
285    /**
286     * 
287     * 创建一个新的数据库连接并返回它
288     * 
289     * 
290     * 
291     * @return 返回一个新创建的数据库连接
292     * 
293     */

294
295    private Connection newConnection() throws SQLException {
296
297        // 创建一个数据库连接
298
299        Connection conn = DriverManager.getConnection(_dbUrl, _dbUsername,
300                _dbPassword);
301
302        // 如果这是第一次创建数据库连接,即检查数据库,获得此数据库允许支持的
303
304        // 最大客户连接数目
305
306        // connections.size()==0 表示目前没有连接己被创建
307
308        if (_connectionVector.size() == 0{
309
310            DatabaseMetaData metaData = conn.getMetaData();
311
312            int driverMaxConnections = metaData.getMaxConnections();
313
314            // 数据库返回的 driverMaxConnections 若为 0 ,表示此数据库没有最大
315
316            // 连接限制,或数据库的最大连接限制不知道
317
318            // driverMaxConnections 为返回的一个整数,表示此数据库允许客户连接的数目
319
320            // 如果连接池中设置的最大连接数量大于数据库允许的连接数目 , 则置连接池的最大
321
322            // 连接数目为数据库允许的最大数目
323
324            if (driverMaxConnections > 0
325                    && this._maxConnections > driverMaxConnections) {
326
327                this._maxConnections = driverMaxConnections;
328
329            }

330
331        }

332
333        return conn; // 返回创建的新的数据库连接
334
335    }

336
337    /**
338     * 
339     * 通过调用 getFreeConnection() 函数返回一个可用的数据库连接 ,
340     * 
341     * 如果当前没有可用的数据库连接,并且更多的数据库连接不能创
342     * 
343     * 建(如连接池大小的限制),此函数等待一会再尝试获取。
344     * 
345     * 
346     * 
347     * @return 返回一个可用的数据库连接对象
348     * 
349     */

350
351    public synchronized Connection getConnection() throws SQLException {
352
353        // 确保连接池己被创建
354
355        if (_connectionVector == null{
356
357            return null// 连接池还没创建,则返回 null
358
359        }

360
361        Connection conn = getFreeConnection(); // 获得一个可用的数据库连接
362
363        // 如果目前没有可以使用的连接,即所有的连接都在使用中
364
365        while (conn == null{
366
367            // 等一会再试
368
369            wait(250);
370
371            conn = getFreeConnection(); // 重新再试,直到获得可用的连接,如果
372
373            // getFreeConnection() 返回的为 null
374
375            // 则表明创建一批连接后也不可获得可用连接
376
377        }

378
379        return conn;// 返回获得的可用的连接
380
381    }

382
383    /**
384     * 
385     * 本函数从连接池向量 connections 中返回一个可用的的数据库连接,如果
386     * 
387     * 当前没有可用的数据库连接,本函数则根据 incrementalConnections 设置
388     * 
389     * 的值创建几个数据库连接,并放入连接池中。
390     * 
391     * 如果创建后,所有的连接仍都在使用中,则返回 null
392     * 
393     * @return 返回一个可用的数据库连接
394     * 
395     */

396
397    private Connection getFreeConnection() throws SQLException {
398
399        // 从连接池中获得一个可用的数据库连接
400
401        Connection conn = findFreeConnection();
402
403        if (conn == null{
404
405            // 如果目前连接池中没有可用的连接
406
407            // 创建一些连接
408
409            createConnections(_incrementalConnections);
410
411            // 重新从池中查找是否有可用连接
412
413            conn = findFreeConnection();
414
415            if (conn == null{
416
417                // 如果创建连接后仍获得不到可用的连接,则返回 null
418
419                return null;
420
421            }

422
423        }

424
425        return conn;
426
427    }

428
429    /**
430     * 
431     * 查找连接池中所有的连接,查找一个可用的数据库连接,
432     * 
433     * 如果没有可用的连接,返回 null
434     * 
435     * 
436     * 
437     * @return 返回一个可用的数据库连接
438     * 
439     */

440
441    private Connection findFreeConnection() throws SQLException {
442
443        Connection conn = null;
444
445        PooledConnection pConn = null;
446
447        // 获得连接池向量中所有的对象
448
449        Enumeration enumerate = _connectionVector.elements();
450
451        // 遍历所有的对象,看是否有可用的连接
452
453        while (enumerate.hasMoreElements()) {
454
455            pConn = (PooledConnection) enumerate.nextElement();
456
457            if (!pConn.isBusy()) {
458
459                // 如果此对象不忙,则获得它的数据库连接并把它设为忙
460
461                conn = pConn.getConnection();
462
463                pConn.setBusy(true);
464
465                // 测试此连接是否可用
466
467                if (!testConnection(conn)) {
468
469                    // 如果此连接不可再用了,则创建一个新的连接,
470
471                    // 并替换此不可用的连接对象,如果创建失败,返回 null
472
473                    try {
474
475                        conn = newConnection();
476
477                    }
 catch (SQLException e) {
478
479                        System.out.println(" 创建数据库连接失败! " + e.getMessage());
480
481                        return null;
482
483                    }

484
485                    pConn.setConnection(conn);
486
487                }

488
489                break// 己经找到一个可用的连接,退出
490
491            }

492
493        }

494
495        return conn;// 返回找到到的可用连接
496
497    }

498
499    /**
500     * 
501     * 测试一个连接是否可用,如果不可用,关掉它并返回 false
502     * 
503     * 否则可用返回 true
504     * 
505     * 
506     * 
507     * @param conn
508     *            需要测试的数据库连接
509     * 
510     * @return 返回 true 表示此连接可用, false 表示不可用
511     * 
512     */

513
514    private boolean testConnection(Connection conn) {
515
516        try {
517
518            // 判断测试表是否存在
519
520            if (_testTable.equals("")) {
521
522                // 如果测试表为空,试着使用此连接的 setAutoCommit() 方法
523
524                // 来判断连接否可用(此方法只在部分数据库可用,如果不可用 ,
525
526                // 抛出异常)。注意:使用测试表的方法更可靠
527
528                conn.setAutoCommit(true);
529
530            }
 else {// 有测试表的时候使用测试表测试
531
532                // check if this connection is valid
533
534                Statement stmt = conn.createStatement();
535
536                stmt.execute("select count(*) from " + _testTable);
537
538            }

539
540        }
 catch (SQLException e) {
541
542            // 上面抛出异常,此连接己不可用,关闭它,并返回 false;
543
544            closeConnection(conn);
545
546            return false;
547
548        }

549
550        // 连接可用,返回 true
551
552        return true;
553
554    }

555
556    /**
557     * 
558     * 此函数返回一个数据库连接到连接池中,并把此连接置为空闲。
559     * 
560     * 所有使用连接池获得的数据库连接均应在不使用此连接时返回它。
561     * 
562     * 
563     * 
564     * @param 需返回到连接池中的连接对象
565     * 
566     */

567
568    public void returnConnection(Connection conn) {
569
570        // 确保连接池存在,如果连接没有创建(不存在),直接返回
571
572        if (_connectionVector == null{
573
574            System.out.println(" 连接池不存在,无法返回此连接到连接池中 !");
575
576            return;
577
578        }

579
580        PooledConnection pConn = null;
581
582        Enumeration enumerate = _connectionVector.elements();
583
584        // 遍历连接池中的所有连接,找到这个要返回的连接对象
585
586        while (enumerate.hasMoreElements()) {
587
588            pConn = (PooledConnection) enumerate.nextElement();
589
590            // 先找到连接池中的要返回的连接对象
591
592            if (conn == pConn.getConnection()) {
593
594                // 找到了 , 设置此连接为空闲状态
595
596                pConn.setBusy(false);
597
598                break;
599
600            }

601
602        }

603
604    }

605
606    /**
607     * 
608     * 刷新连接池中所有的连接对象
609     * 
610     * 
611     * 
612     */

613
614    public synchronized void refreshConnections() throws SQLException {
615
616        // 确保连接池己创新存在
617
618        if (_connectionVector == null{
619
620            System.out.println(" 连接池不存在,无法刷新 !");
621
622            return;
623
624        }

625
626        PooledConnection pConn = null;
627
628        Enumeration enumerate = _connectionVector.elements();
629
630        while (enumerate.hasMoreElements()) {
631
632            // 获得一个连接对象
633
634            pConn = (PooledConnection) enumerate.nextElement();
635
636            // 如果对象忙则等 5 秒 ,5 秒后直接刷新
637
638            if (pConn.isBusy()) {
639
640                wait(5000); // 等 5 秒
641
642            }

643
644            // 关闭此连接,用一个新的连接代替它。
645
646            closeConnection(pConn.getConnection());
647
648            pConn.setConnection(newConnection());
649
650            pConn.setBusy(false);
651
652        }

653
654    }

655
656    /**
657     * 
658     * 关闭连接池中所有的连接,并清空连接池。
659     * 
660     */

661
662    public synchronized void closeConnectionPool() throws SQLException {
663
664        // 确保连接池存在,如果不存在,返回
665
666        if (_connectionVector == null{
667
668            System.out.println(" 连接池不存在,无法关闭 !");
669
670            return;
671
672        }

673
674        PooledConnection pConn = null;
675
676        Enumeration enumerate = _connectionVector.elements();
677
678        while (enumerate.hasMoreElements()) {
679
680            pConn = (PooledConnection) enumerate.nextElement();
681
682            // 如果忙,等 5 秒
683
684            if (pConn.isBusy()) {
685
686                wait(5000); // 等 5 秒
687
688            }

689
690            // 5 秒后直接关闭它
691
692            closeConnection(pConn.getConnection());
693
694            // 从连接池向量中删除它
695
696            _connectionVector.removeElement(pConn);
697
698        }

699
700        // 置连接池为空
701
702        _connectionVector = null;
703
704    }

705
706    /**
707     * 
708     * 关闭一个数据库连接
709     * 
710     * 
711     * 
712     * @param 需要关闭的数据库连接
713     * 
714     */

715
716    private void closeConnection(Connection conn) {
717
718        try {
719
720            conn.close();
721
722        }
 catch (SQLException e) {
723
724            System.out.println(" 关闭数据库连接出错: " + e.getMessage());
725
726        }

727
728    }

729
730    /**
731     * 
732     * 使程序等待给定的毫秒数
733     * 
734     * 
735     * 
736     * @param 给定的毫秒数
737     * 
738     */

739
740    private void wait(int mSeconds) {
741
742        try {
743
744            Thread.sleep(mSeconds);
745
746        }
 catch (InterruptedException e) {
747
748        }

749
750    }

751
752    /**
753     * 
754     * 
755     * 
756     * 内部使用的用于保存连接池中连接对象的类
757     * 
758     * 此类中有两个成员,一个是数据库的连接,另一个是指示此连接是否
759     * 
760     * 正在使用的标志。
761     * 
762     */

763
764    class PooledConnection {
765
766        Connection connection = null;// 数据库连接
767
768        boolean busy = false// 此连接是否正在使用的标志,默认没有正在使用
769
770        // 构造函数,根据一个 Connection 构告一个 PooledConnection 对象
771
772        public PooledConnection(Connection connection) {
773
774            this.connection = connection;
775
776        }

777
778        // 返回此对象中的连接
779
780        public Connection getConnection() {
781
782            return connection;
783
784        }

785
786        // 设置此对象的,连接
787
788        public void setConnection(Connection connection) {
789
790            this.connection = connection;
791
792        }

793
794        // 获得对象连接是否忙
795
796        public boolean isBusy() {
797
798            return busy;
799
800        }

801
802        // 设置对象的连接正在忙
803
804        public void setBusy(boolean busy) {
805
806            this.busy = busy;
807
808        }

809
810    }

811
812}

posted on 2008-12-29 17:00 liyang 阅读(1669) 评论(0)  编辑  收藏