MySql数据库链接池专题

1、什么是数据库链接池?

官方:数据库链接池(Connection pooling)是程序启动时创建足够的数据库链接,并将这些链接组成一个链接池,由程序动态地对池中的链接进行申请,使用,释放。
我的理解:建立数据库链接是一个很耗时的操做,也容易对数据库形成安全隐患。因此,在程序初始化的时候,集中建立多个数据库链接,并把他们集中管理,供程序使用,能够保证较快的数据库读写速度,还更加安全可靠。html

2、传统的链接机制与数据库链接池的运行机制区别

 传通通连接:     通常来讲,Java应用程序访问数据库的过程是:java

  ①装载数据库驱动程序;mysql

  ②经过JDBC创建数据库链接;web

  ③访问数据库,执行SQL语句;面试

  ④断开数据库链接。sql

使用了数据库链接池的机制:
(1)  程序初始化时建立链接池
(2) 使用时向链接池申请可用链接
(3) 使用完毕,将链接返还给链接池
(4) 程序退出时,断开全部链接,并释放资源

 

一. 为什么要使用数据库链接池
假设网站一天有很大的访问量,数据库服务器就须要为每次链接建立一次数据库链接,极大的浪费数据库的资源,而且极易形成数据库服务器内存溢出、拓机。
数据库链接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现的尤其突出.对数据库链接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标.数据库链接池正式针对这个问题提出来的.数据库链接池负责分配,管理和释放数据库链接,它容许应用程序重复使用一个现有的数据库链接,而不是从新创建一个

数据库

数据库链接池在初始化时将建立必定数量的数据库链接放到链接池中, 这些数据库链接的数量是由最小数据库链接数来设定的.不管这些数据库链接是否被使用,链接池都将一直保证至少拥有这么多的链接数量.链接池的最大数据库链接数量限定了这个链接池能占有的最大链接数,当应用程序向链接池请求的链接数超过最大链接数量时,这些请求将被加入到等待队列中.apache

      数据库链接池的最小链接数和最大链接数的设置要考虑到如下几个因素:编程

  1, 最小链接数:是链接池一直保持的数据库链接,因此若是应用程序对数据库链接的使用量不大,将会有大量的数据库链接资源被浪费.
  2, 最大链接数:是链接池能申请的最大链接数,若是数据库链接请求超过次数,后面的数据库链接请求将被加入到等待队列中,这会影响之后的数据库操做
  3, 若是最小链接数与最大链接数相差很大:那么最早链接请求将会获利,以后超过最小链接数量的链接请求等价于创建一个新的数据库链接.不过,这些大于最小链接数的数据库链接在使用完不会立刻被释放,他将被           放到链接池中等待重复使用或是空间超时后被释放.c#

2、使用数据库链接池的关键点

一、并发问题

  为了使链接管理服务具备最大的通用性,必须考虑多线程环境,即并发问题。这个问题相对比较好解决,由于各个语言自身提供了对并发管理的支持像java,c#等等,使用synchronized(java)lock(C#)关键字便可确保线程是同步的。使用方法能够参考,相关文献。

2、事务处理

DB链接池必需要确保某一时间内一个 conn 只能分配给一个线程。不一样 conn 的事务是相互独立的。 

  咱们知道,事务具备原子性,此时要求对数据库的操做符合“ALL-ALL-NOTHING”原则,即对于一组SQL语句要么全作,要么全不作。 
  咱们知道当2个线程共用一个链接Connection对象,并且各自都有本身的事务要处理时候,对于链接池是一个很头疼的问题,由于即便Connection类提供了相应的事务支持,但是咱们仍然不能肯定那个数据库操做是对应那个事务的,这是因为咱们有2个线程都在进行事务操做而引发的。为此咱们可使用每个事务独占一个链接来实现,虽然这种方法有点浪费链接池资源可是能够大大下降事务管理的复杂性。 

 

3、链接池的分配与释放

  链接池的分配与释放,对系统的性能有很大的影响。合理的分配与释放,能够提升链接的复用度,从而下降创建新链接的开销,同时还能够加快用户的访问速度。 
  对于链接的管理可以使用一个List。即把已经建立的链接都放入List中去统一管理。每当用户请求一个链接时,系统检查这个List中有没有能够分配的链接。若是有就把那个最合适的链接分配给他(如何能找到最合适的链接文章将在关键议题中指出);若是没有就抛出一个异常给用户,List中链接是否能够被分配由一个线程来专门管理捎后我会介绍这个线程的具体实现。

4、链接池的配置与维护

  链接池中到底应该放置多少链接,才能使系统的性能最佳?系统可采起设置最小链接数(minConnection)和最大链接数(maxConnection)等参数来控制链接池中的链接。比方说,最小链接数是系统启动时链接池所建立的链接数。若是建立过多,则系统启动就慢,但建立后系统的响应速度会很快;若是建立过少,则系统启动的很快,响应起来却慢。这样,能够在开发时,设置较小的最小链接数,开发起来会快,而在系统实际使用时设置较大的,由于这样对访问客户来讲速度会快些。最大链接数是链接池中容许链接的最大数目,具体设置多少,要看系统的访问量,可经过软件需求上获得。 
  如何确保链接池中的最小链接数呢?有动态和静态两种策略。动态即每隔必定时间就对链接池进行检测,若是发现链接数量小于最小链接数,则补充相应数量的新链接,以保证链接池的正常运转。静态是发现空闲链接不够时再去检查。

 

3、使用数据库链接池的优点和其工做原理

一、链接池的优点

链接池用于建立和管理数据库链接的缓冲池技术,缓冲池中的链接能够被任何须要他们的线程使用。当一个线程须要用JDBC对一个数据库操做时,将从池中请求一个链接。当这个链接使用完毕后,将返回到链接池中,等待为其余的线程服务。

链接池的主要优势有如下三个方面。

第1、减小链接建立时间。链接池中的链接是已准备好的、可重复使用的,获取后能够直接访问数据库,所以减小了链接建立的次数和时间。

第2、简化的编程模式。当使用链接池时,每个单独的线程可以像建立一个本身的JDBC链接同样操做,容许用户直接使用JDBC编程技术。

第3、控制资源的使用。若是不使用链接池,每次访问数据库都须要建立一个链接,这样系统的稳定性受系统链接需求影响很大,很容易产生资源浪费和高负载异常。链接池可以使性能最大化,将资源利用控制在必定的水平之下。链接池能控制池中的链接数量,加强了系统在大量用户应用时的稳定性。

二、链接池的工做原理

下面,简单的阐述下链接池的工做原理。

链接池技术的核心思想是链接复用,经过创建一个数据库链接池以及一套链接使用、分配和管理策略,使得该链接池中的链接能够获得高效、安全的复用,避免了数据库链接频繁创建、关闭的开销。

链接池的工做原理主要由三部分组成,分别为链接池的创建、链接池中链接的使用管理、链接池的关闭。

第1、链接池的创建。通常在系统初始化时,链接池会根据系统配置创建,并在池中建立了几个链接对象,以便使用时能从链接池中获取。链接池中的链接不能随意建立和关闭,这样避免了链接随意创建和关闭形成的系统开销。Java中提供了不少容器类能够方便的构建链接池,例如Vector、Stack等。

第2、链接池的管理。链接池管理策略是链接池机制的核心,链接池内链接的分配和释放对系统的性能有很大的影响。其管理策略是:

  • 当客户请求数据库链接时,首先查看链接池中是否有空闲链接,若是存在空闲链接,则将链接分配给客户使用;若是没有空闲链接,则查看当前所开的链接数是否已经达到最大链接数,若是没达到就从新建立一个链接给请求的客户;若是达到就按设定的最大等待时间进行等待,若是超出最大等待时间,则抛出异常给客户。
  • 当客户释放数据库链接时,先判断该链接的引用次数是否超过了规定值,若是超过就从链接池中删除该链接,不然保留为其余客户服务。

    该策略保证了数据库链接的有效复用,避免频繁的创建、释放链接所带来的系统资源开销。

第3、链接池的关闭。当应用程序退出时,关闭链接池中全部的链接,释放链接池相关的资源,该过程正好与建立相反。

 三、经常使用的链接池:

     (1) dbcp
dbcp多是使用最多的开源链接池,缘由大概是由于配置方便,并且不少开源和tomcat应用例子都是使用的这个链接池吧。
这个链接池能够设置最大和最小链接,链接等待时间等,基本功能都有。这个链接池的配置参见附件压缩包中的:dbcp.xml
使用评价:在具体项目应用中,发现此链接池的持续运行的稳定性仍是能够,不过速度稍慢,在大并发量的压力下稳定性
有所降低,此外不提供链接池监控

经常使用的参数(阿里面试问经常使用的参数):  


咱们来看DBCP 的例子, 而后根据例子来分析:

#链接设置
driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/day14 username=root password=abc #<!-- 初始化链接 --> initialSize=10 #最大链接数量 maxActive=50 #<!-- 最大空闲链接 --> maxIdle=20 #<!-- 最小空闲链接 --> minIdle=5 #<!-- 超时等待时间以毫秒为单位 60000毫秒/1000等于60秒 --> maxWait=60000 #JDBC驱动创建链接时附带的链接属性属性的格式必须为这样:[属性名=property;] #注意:"user" 与 "password" 两个属性会被明确地传递,所以这里不须要包含他们。 connectionProperties=useUnicode=true;characterEncoding=utf8 #指定由链接池所建立的链接的自动提交(auto-commit)状态。 defaultAutoCommit=true #driver default 指定由链接池所建立的链接的只读(read-only)状态。 #若是没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix) defaultReadOnly= #driver default 指定由链接池所建立的链接的事务级别(TransactionIsolation)。 #可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE defaultTransactionIsolation=REPEATABLE_READ DBCP配置文件

配置参数详解:

MaxActive,链接池的最大数据库链接数。设为0表示无限制。maxActive是最大激活链接数,这里取值为20,表示同时最多有20个数据库连 
maxIdle 链接池中最多可空闲maxIdle个链接,maxIdle是最大的空闲链接数,这里取值为20,表示即便没有数据库链接时依然能够保持20空闲的链接,而不被清除,随时处于待命状态
minIdle 链接池中最少空闲maxIdle个链接 
initialSize 初始化链接数目 
maxWait 链接池中链接用完时,新的请求等待时间,毫秒 MaxWait是最大等待秒钟数,这里取值-1,表示无限等待,直到超时为止,也可取值9000,表示9秒后超时。
maxIdle,最大空闲数,数据库链接的最大空闲时间。超过空闲时间,数据库连
接将被标记为不可用,而后被释放。设为0表示无限制。

   (2) c3p0
c3p0是另一个开源的链接池,在业界也是比较有名的,这个链接池能够设置最大和最小链接,链接等待时间等,基本功能都有。
这个链接池的配置参见附件压缩包中的:c3p0.xml。
使用评价:在具体项目应用中,发现此链接池的持续运行的稳定性至关不错,在大并发量的压力下稳定性也有必定保证,
          此外不提供链接池监控。          

   1.Apache commons-dbcp 链接池

        下载:http://commons.apache.org/proper/commons-dbcp/ 

       2.c3p0 数据库链接池

        下载:http://sourceforge.net/projects/c3p0/

程序开发过程当中,存在不少问题:

首先,每一次web请求都要创建一次数据库链接。创建链接是一个费时的活动,每次都得花费0.05s~1s的时间,并且系统还要分配内存资源。这个时间对于一次或几回数据库操做,或许感受不出系统有多大的开销。

但是对于如今的web应用,尤为是大型电子商务网站,同时有几百人甚至几千人在线是很正常的事。在这种状况下,频繁的进行数据库链接操做势必占用不少的系统资源,网站的响应速度一定降低,严重的甚至会形成服务器的崩溃。不是危言耸听,这就是制约某些电子商务网站发展的技术瓶颈问题。其次,对于每一次数据库链接,使用完后都得断开。不然,若是程序出现异常而未能关闭,将会致使数据库系统中的内存泄漏,最终将不得不重启数据库

     经过上面的分析,咱们能够看出来,“数据库链接”是一种稀缺的资源,为了保障网站的正常使用,应该对其进行妥善管理。其实咱们查询完数据库后,若是不关闭链接,而是暂时存放起来,当别人使用时,把这个链接给他们使用。就避免了一次创建数据库链接和断开的操做时间消耗。

数据库链接池的基本思想就是为数据库链接创建一个“缓冲池”。预先在缓冲池中放入必定数量的链接,当须要创建数据库链接时,只需从“缓冲池”中取出一个,使用完毕以后再放回去。咱们能够经过设定链接池最大链接数来防止系统无尽的与数据库链接

建立数据库链接池大概有3个步骤:

① 建立ConnectionPool实例,并初始化建立10个链接,保存在Vector中(线程安全)单例模式实现
② 实现getConnection()从链接库中获取一个可用的链接
③ returnConnection(conn) 提供将链接放回链接池中方法

  链接池的实现

  一、链接池模型

  本文讨论的链接池包括一个链接池类(DBConnectionPool)和一个链接池管理类(DBConnetionPoolManager)。链接池类是对某一数据库全部链接的“缓冲池”,主要实现如下功能:①从链接池获取或建立可用链接;②使用完毕以后,把链接返还给链接池;③在系统关闭前,断开全部链接并释放链接占用的系统资源;④还可以处理无效链接(原来登记为可用的链接,因为某种缘由再也不可用,如超时,通信问题),并可以限制链接池中的链接总数不低于某个预约值和不超过某个预约值。

  链接池管理类是链接池类的外覆类(wrapper),符合单例模式,即系统中只能有一个链接池管理类的实例。其主要用于对多个链接池对象的管理,具备如下功能:①装载并注册特定数据库的JDBC驱动程序;②根据属性文件给定的信息,建立链接池对象;③为方便管理多个链接池对象,为每个链接池对象取一个名字,实现链接池名字与其实例之间的映射;④跟踪客户使用链接状况,以便须要是关闭链接释放资源。链接池管理类的引入主要是为了方便对多个链接池的使用和管理,如系统须要链接不一样的数据库,或链接相同的数据库但因为安全性问题,须要不一样的用户使用不一样的名称和密码。

链接池源码:

ConnectionPool.Java 

[java] view plain copy print?
//////////////////////////////// 数据库链接池类 ConnectionPool.java ////////////////////////////////////////  
  
/* 
 这个例子是根据POSTGRESQL数据库写的, 
 请用的时候根据实际的数据库调整。 
 调用方法以下: 
 ① ConnectionPool connPool  
 = new ConnectionPool("com.microsoft.jdbc.sqlserver.SQLServerDriver" 
 ,"jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=MyDataForTest" 
 ,"Username" 
 ,"Password"); 
 ② connPool .createPool(); 
 Connection conn = connPool .getConnection(); 
 connPool.returnConnection(conn);  
 connPool.refreshConnections(); 
 connPool.closeConnectionPool(); 
 */  
import java.sql.Connection;  
import java.sql.DatabaseMetaData;  
import java.sql.Driver;  
import java.sql.DriverManager;  
import java.sql.SQLException;  
import java.sql.Statement;  
import java.util.Enumeration;  
import java.util.Vector;  
  
public class ConnectionPool {  
    private String jdbcDriver = ""; // 数据库驱动  
    private String dbUrl = ""; // 数据 URL  
    private String dbUsername = ""; // 数据库用户名  
    private String dbPassword = ""; // 数据库用户密码  
    private String testTable = ""; // 测试链接是否可用的测试表名,默认没有测试表  
      
    private int initialConnections = 10; // 链接池的初始大小  
    private int incrementalConnections = 5;// 链接池自动增长的大小  
    private int maxConnections = 50; // 链接池最大的大小  
    private Vector connections = null; // 存放链接池中数据库链接的向量 , 初始时为 null  
    // 它中存放的对象为 PooledConnection 型  
  
    /** 
     * 构造函数 
     *  
     * @param jdbcDriver 
     *            String JDBC 驱动类串 
     * @param dbUrl 
     *            String 数据库 URL 
     * @param dbUsername 
     *            String 链接数据库用户名 
     * @param dbPassword 
     *            String 链接数据库用户的密码 
     *  
     */  
    public ConnectionPool(String jdbcDriver, String dbUrl, String dbUsername,  
            String dbPassword) {  
        this.jdbcDriver = jdbcDriver;  
        this.dbUrl = dbUrl;  
        this.dbUsername = dbUsername;  
        this.dbPassword = dbPassword;  
    }  
  
    /** 
     * 返回链接池的初始大小 
     *  
     * @return 初始链接池中可得到的链接数量 
     */  
    public int getInitialConnections() {  
        return this.initialConnections;  
    }  
    /** 
     * 设置链接池的初始大小 
     *  
     * @param 用于设置初始链接池中链接的数量 
     */  
    public void setInitialConnections(int initialConnections) {  
        this.initialConnections = initialConnections;  
    }  
    /** 
     * 返回链接池自动增长的大小 、 
     *  
     * @return 链接池自动增长的大小 
     */  
    public int getIncrementalConnections() {  
        return this.incrementalConnections;  
    }  
    /** 
     * 设置链接池自动增长的大小 
     *  
     * @param 链接池自动增长的大小 
     */  
  
    public void setIncrementalConnections(int incrementalConnections) {  
        this.incrementalConnections = incrementalConnections;  
    }  
    /** 
     * 返回链接池中最大的可用链接数量 
     *  
     * @return 链接池中最大的可用链接数量 
     */  
    public int getMaxConnections() {  
        return this.maxConnections;  
    }  
    /** 
     * 设置链接池中最大可用的链接数量 
     *  
     * @param 设置链接池中最大可用的链接数量值 
     */  
    public void setMaxConnections(int maxConnections) {  
        this.maxConnections = maxConnections;  
    }  
  
    /** 
     * 获取测试数据库表的名字 
     *  
     * @return 测试数据库表的名字 
     */  
  
    public String getTestTable() {  
        return this.testTable;  
    }  
  
    /** 
     * 设置测试表的名字 
     *  
     * @param testTable 
     *            String 测试表的名字 
     */  
  
    public void setTestTable(String testTable) {  
        this.testTable = testTable;  
    }  
  
    /** 
     *  
     * 建立一个数据库链接池,链接池中的可用链接的数量采用类成员 initialConnections 中设置的值 
     */  
  
    public synchronized void createPool() throws Exception {  
        // 确保链接池没有建立  
        // 若是链接池己经建立了,保存链接的向量 connections 不会为空  
        if (connections != null) {  
            return; // 若是己经建立,则返回  
        }  
        // 实例化 JDBC Driver 中指定的驱动类实例  
        Driver driver = (Driver) (Class.forName(this.jdbcDriver).newInstance());  
        DriverManager.registerDriver(driver); // 注册 JDBC 驱动程序  
        // 建立保存链接的向量 , 初始时有 0 个元素  
        connections = new Vector();  
        // 根据 initialConnections 中设置的值,建立链接。  
        createConnections(this.initialConnections);  
        // System.out.println(" 数据库链接池建立成功! ");  
    }  
  
    /** 
     * 建立由 numConnections 指定数目的数据库链接 , 并把这些链接 放入 connections 向量中 
     *  
     * @param numConnections 
     *            要建立的数据库链接的数目 
     */  
  
    private void createConnections(int numConnections) throws SQLException {  
        // 循环建立指定数目的数据库链接  
        for (int x = 0; x < numConnections; x++) {  
            // 是否链接池中的数据库链接的数量己经达到最大?最大值由类成员 maxConnections  
            // 指出,若是 maxConnections 为 0 或负数,表示链接数量没有限制。  
            // 若是链接数己经达到最大,即退出。  
            if (this.maxConnections > 0  
                    && this.connections.size() >= this.maxConnections) {  
                break;  
            }  
            // add a new PooledConnection object to connections vector  
            // 增长一个链接到链接池中(向量 connections 中)  
            try {  
                connections.addElement(new PooledConnection(newConnection()));  
            } catch (SQLException e) {  
                System.out.println(" 建立数据库链接失败! " + e.getMessage());  
                throw new SQLException();  
            }  
            // System.out.println(" 数据库链接己建立 ......");  
        }  
    }  
    /** 
     * 建立一个新的数据库链接并返回它 
     *  
     * @return 返回一个新建立的数据库链接 
     */  
    private Connection newConnection() throws SQLException {  
        // 建立一个数据库链接  
        Connection conn = DriverManager.getConnection(dbUrl, dbUsername,  
                dbPassword);  
        // 若是这是第一次建立数据库链接,即检查数据库,得到此数据库容许支持的  
        // 最大客户链接数目  
        // connections.size()==0 表示目前没有链接己被建立  
        if (connections.size() == 0) {  
            DatabaseMetaData metaData = conn.getMetaData();  
            int driverMaxConnections = metaData.getMaxConnections();  
            // 数据库返回的 driverMaxConnections 若为 0 ,表示此数据库没有最大  
            // 链接限制,或数据库的最大链接限制不知道  
            // driverMaxConnections 为返回的一个整数,表示此数据库容许客户链接的数目  
            // 若是链接池中设置的最大链接数量大于数据库容许的链接数目 , 则置链接池的最大  
            // 链接数目为数据库容许的最大数目  
            if (driverMaxConnections > 0  
                    && this.maxConnections > driverMaxConnections) {  
                this.maxConnections = driverMaxConnections;  
            }  
        }  
        return conn; // 返回建立的新的数据库链接  
    }  
  
    /** 
     * 经过调用 getFreeConnection() 函数返回一个可用的数据库链接 , 若是当前没有可用的数据库链接,而且更多的数据库链接不能创 
     * 建(如链接池大小的限制),此函数等待一会再尝试获取。 
     *  
     * @return 返回一个可用的数据库链接对象 
     */  
  
    public synchronized Connection getConnection() throws SQLException {  
        // 确保链接池己被建立  
        if (connections == null) {  
            return null; // 链接池还没建立,则返回 null  
        }  
        Connection conn = getFreeConnection(); // 得到一个可用的数据库链接  
        // 若是目前没有可使用的链接,即全部的链接都在使用中  
        while (conn == null) {  
            // 等一会再试  
            // System.out.println("Wait");  
            wait(250);  
            conn = getFreeConnection(); // 从新再试,直到得到可用的链接,若是  
            // getFreeConnection() 返回的为 null  
            // 则代表建立一批链接后也不可得到可用链接  
        }  
        return conn;// 返回得到的可用的链接  
    }  
  
    /** 
     * 本函数从链接池向量 connections 中返回一个可用的的数据库链接,若是 当前没有可用的数据库链接,本函数则根据 
     * incrementalConnections 设置 的值建立几个数据库链接,并放入链接池中。 若是建立后,全部的链接仍都在使用中,则返回 null 
     *  
     * @return 返回一个可用的数据库链接 
     */  
    private Connection getFreeConnection() throws SQLException {  
        // 从链接池中得到一个可用的数据库链接  
        Connection conn = findFreeConnection();  
        if (conn == null) {  
            // 若是目前链接池中没有可用的链接  
            // 建立一些链接  
            createConnections(incrementalConnections);  
            // 从新从池中查找是否有可用链接  
            conn = findFreeConnection();  
            if (conn == null) {  
                // 若是建立链接后仍得到不到可用的链接,则返回 null  
                return null;  
            }  
        }  
        return conn;  
    }  
  
    /** 
     * 查找链接池中全部的链接,查找一个可用的数据库链接, 若是没有可用的链接,返回 null 
     *  
     * @return 返回一个可用的数据库链接 
     */  
  
    private Connection findFreeConnection() throws SQLException {  
        Connection conn = null;  
        PooledConnection pConn = null;  
        // 得到链接池向量中全部的对象  
        Enumeration enumerate = connections.elements();  
        // 遍历全部的对象,看是否有可用的链接  
        while (enumerate.hasMoreElements()) {  
            pConn = (PooledConnection) enumerate.nextElement();  
            if (!pConn.isBusy()) {  
                // 若是此对象不忙,则得到它的数据库链接并把它设为忙  
                conn = pConn.getConnection();  
                pConn.setBusy(true);  
                // 测试此链接是否可用  
                if (!testConnection(conn)) {  
                    // 若是此链接不可再用了,则建立一个新的链接,  
                    // 并替换此不可用的链接对象,若是建立失败,返回 null  
                    try {  
                        conn = newConnection();  
                    } catch (SQLException e) {  
                        System.out.println(" 建立数据库链接失败! " + e.getMessage());  
                        return null;  
                    }  
                    pConn.setConnection(conn);  
                }  
                break; // 己经找到一个可用的链接,退出  
            }  
        }  
        return conn;// 返回找到到的可用链接  
    }  
  
    /** 
     * 测试一个链接是否可用,若是不可用,关掉它并返回 false 不然可用返回 true 
     *  
     * @param conn 
     *            须要测试的数据库链接 
     * @return 返回 true 表示此链接可用, false 表示不可用 
     */  
  
    private boolean testConnection(Connection conn) {  
        try {  
            // 判断测试表是否存在  
            if (testTable.equals("")) {  
                // 若是测试表为空,试着使用此链接的 setAutoCommit() 方法  
                // 来判断链接否可用(此方法只在部分数据库可用,若是不可用 ,  
                // 抛出异常)。注意:使用测试表的方法更可靠  
                conn.setAutoCommit(true);  
            } else {// 有测试表的时候使用测试表测试  
                // check if this connection is valid  
                Statement stmt = conn.createStatement();  
                stmt.execute("select count(*) from " + testTable);  
            }  
        } catch (SQLException e) {  
            // 上面抛出异常,此链接己不可用,关闭它,并返回 false;  
            closeConnection(conn);  
            return false;  
        }  
        // 链接可用,返回 true  
        return true;  
    }  
  
    /** 
     * 此函数返回一个数据库链接到链接池中,并把此链接置为空闲。 全部使用链接池得到的数据库链接均应在不使用此链接时返回它。 
     *  
     * @param 需返回到链接池中的链接对象 
     */  
  
    public void returnConnection(Connection conn) {  
        // 确保链接池存在,若是链接没有建立(不存在),直接返回  
        if (connections == null) {  
            System.out.println(" 链接池不存在,没法返回此链接到链接池中 !");  
            return;  
        }  
        PooledConnection pConn = null;  
        Enumeration enumerate = connections.elements();  
        // 遍历链接池中的全部链接,找到这个要返回的链接对象  
        while (enumerate.hasMoreElements()) {  
            pConn = (PooledConnection) enumerate.nextElement();  
            // 先找到链接池中的要返回的链接对象  
            if (conn == pConn.getConnection()) {  
                // 找到了 , 设置此链接为空闲状态  
                pConn.setBusy(false);  
                break;  
            }  
        }  
    }  
  
    /** 
     * 刷新链接池中全部的链接对象 
     *  
     */  
  
    public synchronized void refreshConnections() throws SQLException {  
        // 确保链接池己创新存在  
        if (connections == null) {  
            System.out.println(" 链接池不存在,没法刷新 !");  
            return;  
        }  
        PooledConnection pConn = null;  
        Enumeration enumerate = connections.elements();  
        while (enumerate.hasMoreElements()) {  
            // 得到一个链接对象  
            pConn = (PooledConnection) enumerate.nextElement();  
            // 若是对象忙则等 5 秒 ,5 秒后直接刷新  
            if (pConn.isBusy()) {  
                wait(5000); // 等 5 秒  
            }  
            // 关闭此链接,用一个新的链接代替它。  
            closeConnection(pConn.getConnection());  
            pConn.setConnection(newConnection());  
            pConn.setBusy(false);  
        }  
    }  
  
    /** 
     * 关闭链接池中全部的链接,并清空链接池。 
     */  
  
    public synchronized void closeConnectionPool() throws SQLException {  
        // 确保链接池存在,若是不存在,返回  
        if (connections == null) {  
            System.out.println(" 链接池不存在,没法关闭 !");  
            return;  
        }  
        PooledConnection pConn = null;  
        Enumeration enumerate = connections.elements();  
        while (enumerate.hasMoreElements()) {  
            pConn = (PooledConnection) enumerate.nextElement();  
            // 若是忙,等 5 秒  
            if (pConn.isBusy()) {  
                wait(5000); // 等 5 秒  
            }  
            // 5 秒后直接关闭它  
            closeConnection(pConn.getConnection());  
            // 从链接池向量中删除它  
            connections.removeElement(pConn);  
        }  
        // 置链接池为空  
        connections = null;  
    }  
  
    /** 
     * 关闭一个数据库链接 
     *  
     * @param 须要关闭的数据库链接 
     */  
  
    private void closeConnection(Connection conn) {  
        try {  
            conn.close();  
        } catch (SQLException e) {  
            System.out.println(" 关闭数据库链接出错: " + e.getMessage());  
        }  
    }  
    /** 
     * 使程序等待给定的毫秒数 
     *  
     * @param 给定的毫秒数 
     */  
  
    private void wait(int mSeconds) {  
        try {  
            Thread.sleep(mSeconds);  
        } catch (InterruptedException e) {  
        }  
    }  
    /** 
     *  
     * 内部使用的用于保存链接池中链接对象的类 此类中有两个成员,一个是数据库的链接,另外一个是指示此链接是否 正在使用的标志。 
     */  
  
    class PooledConnection {  
        Connection connection = null;// 数据库链接  
        boolean busy = false; // 此链接是否正在使用的标志,默认没有正在使用  
  
        // 构造函数,根据一个 Connection 构告一个 PooledConnection 对象  
        public PooledConnection(Connection connection) {  
            this.connection = connection;  
        }  
  
        // 返回此对象中的链接  
        public Connection getConnection() {  
            return connection;  
        }  
  
        // 设置此对象的,链接  
        public void setConnection(Connection connection) {  
            this.connection = connection;  
        }  
  
        // 得到对象链接是否忙  
        public boolean isBusy() {  
            return busy;  
        }  
  
        // 设置对象的链接正在忙  
        public void setBusy(boolean busy) {  
            this.busy = busy;  
        }  
    }  
  
}  
View Code

 

ConnectionPoolUtils.java

/*链接池工具类,返回惟一的一个数据库链接池对象,单例模式*/  
public class ConnectionPoolUtils {  
    private ConnectionPoolUtils(){};//私有静态方法  
    private static ConnectionPool poolInstance = null;  
    public static ConnectionPool GetPoolInstance(){  
        if(poolInstance == null) {  
            poolInstance = new ConnectionPool(                     
                    "com.mysql.jdbc.Driver",                   
                    "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8",                
                    "root", "123456");  
            try {  
                poolInstance.createPool();  
            } catch (Exception e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
        }  
        return poolInstance;  
    }  
}  

ConnectionPoolTest.java 

import java.sql.Connection;  
import java.sql.DriverManager;  
import java.sql.ResultSet;  
import java.sql.SQLException;  
import java.sql.Statement;  
  
  
public class ConnectionTest {  
  
    /** 
     * @param args 
     * @throws Exception  
     */  
    public static void main(String[] args) throws Exception {  
         try {  
                  /*使用链接池建立100个链接的时间*/   
                   /*// 建立数据库链接库对象 
                   ConnectionPool connPool = new ConnectionPool("com.mysql.jdbc.Driver","jdbc:mysql://localhost:3306/test", "root", "123456"); 
                   // 新建数据库链接库 
                   connPool.createPool();*/  
               
                  ConnectionPool  connPool=ConnectionPoolUtils.GetPoolInstance();//单例模式建立链接池对象  
                    // SQL测试语句  
                   String sql = "Select * from pet";  
                   // 设定程序运行起始时间  
                   long start = System.currentTimeMillis();  
                         // 循环测试100次数据库链接  
                          for (int i = 0; i < 100; i++) {  
                              Connection conn = connPool.getConnection(); // 从链接库中获取一个可用的链接  
                              Statement stmt = conn.createStatement();  
                              ResultSet rs = stmt.executeQuery(sql);  
                              while (rs.next()) {  
                                  String name = rs.getString("name");  
                               //  System.out.println("查询结果" + name);  
                              }  
                              rs.close();  
                              stmt.close();  
                              connPool.returnConnection(conn);// 链接使用完后释放链接到链接池  
                          }  
                          System.out.println("通过100次的循环调用,使用链接池花费的时间:"+ (System.currentTimeMillis() - start) + "ms");  
                          // connPool.refreshConnections();//刷新数据库链接池中全部链接,即无论链接是否正在运行,都把全部链接都释放并放回到链接池。注意:这个耗时比较大。  
                         connPool.closeConnectionPool();// 关闭数据库链接池。注意:这个耗时比较大。  
                          // 设定程序运行起始时间  
                          start = System.currentTimeMillis();  
                            
                          /*不使用链接池建立100个链接的时间*/  
                         // 导入驱动  
                          Class.forName("com.mysql.jdbc.Driver");  
                          for (int i = 0; i < 100; i++) {  
                              // 建立链接  
                             Connection conn = DriverManager.getConnection(  
                                      "jdbc:mysql://localhost:3306/test", "root", "123456");  
                              Statement stmt = conn.createStatement();  
                              ResultSet rs = stmt.executeQuery(sql);  
                             while (rs.next()) {  
                              }  
                             rs.close();  
                             stmt.close();  
                             conn.close();// 关闭链接  
                         }  
                         System.out.println("通过100次的循环调用,不使用链接池花费的时间:"  
                                 + (System.currentTimeMillis() - start) + "ms");  
                     } catch (SQLException e) {  
                        e.printStackTrace();  
                     } catch (ClassNotFoundException e) {  
                         e.printStackTrace();  
                    }  
    }  
View Code

 

DBCPUtils:

 

public class DBCPUtils {
    private static DataSource ds;//定义一个链接池对象
    static{
        try {
            Properties pro = new Properties();
            pro.load(DBCPUtils.class.getClassLoader().getResourceAsStream("dbcpconfig.properties"));
            ds = BasicDataSourceFactory.createDataSource(pro);//获得一个链接池对象
        } catch (Exception e) {
            throw new ExceptionInInitializerError("初始化链接错误,请检查配置文件!");
        }
    }
    //从池中获取一个链接
    public static Connection getConnection() throws SQLException{
        return ds.getConnection();
    }
    
    public static void closeAll(ResultSet rs,Statement stmt,Connection conn){
        if(rs!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        
        if(stmt!=null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        
        if(conn!=null){
            try {
                conn.close();//关闭
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

 

6.导入的jar包

commons-dbcp.jar:DBCP实现要导入的jar

commons-pool.jar: 链接池实现的依赖类

commons-collections.jar :链接池实现的集合类

 

参考:MySql数据库链接池

参考:数据库链接池的理解

参考:数据库链接池原理

参考:Java数据库链接池实现原理

参考:Java数据库链接池--DBCP浅析.

相关文章
相关标签/搜索