JAVA数据库连接池实现原理

参考博客:http://blog.csdn.net/tuke_tuke/article/details/51532510
首先感谢博主的分享,我在这里只是对博主的分享进行一个自我的学习总结和记录

为什么使用数据库连接池?
每一次web请求都要建立一次数据库连接。建立连接是一个费时的活动,每次都得花费0.05s~1s的时间,而且系统还要分配内存资源。这个时间对于一次或几次数据库操作,或许感觉不出系统有多大的开销。可是对于现在的web应用或者比较大型的网站,就能感觉到系统的开销还是很大的

解决办法呢?
查询完数据库后,不关闭连接,而是暂时存放起来,当别人使用时,把这个连接给他们使用。就避免了一次建立数据库连接和断开的操作时间消耗。

数据库连接池的基本思想?
为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。

代码的实现思路?
一、获得一个数据库连接池对象和创建数据库连接池对象
二、获得一个空闲的数据库连接对象
三、关闭数据库连接对象
四、程序入口ConnectionPoolTest的main方法
五、代码下载及阅读步骤说明(我没想要积分的,但是没有去掉积分的那个选项啊):
http://download.csdn.net/download/u012489091/10133734

package JDBCConnectionPool;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;

import JDBCConnectionPool.utils.ConnectionPoolUtils;
import JDBCConnectionPool.utils.JDBC;
/**
 * 数据连接池的测试
 * @author 张爽
 * @date 2017-7-27
 */
public class ConnectionPoolTest {
    public static void main(String[] args) throws Exception {
        ConnectionPoolTest connectionPoolTest = new ConnectionPoolTest();
        connectionPoolTest.testPool();
    }
    /**
     * 使用连接池方式调用100次
     * @throws Exception
     * @author 张爽
     * @date 2017-7-27
     */
    void testPool() throws Exception {
        ConnectionPool connPool = ConnectionPoolUtils.GetPoolInstance(); 
        // 循环测试100次数据库连接
        for (int i = 0; i < 100; i++) {
            Connection conn = connPool.getConnection(); // 从连接池中获得一个可用的连接
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery(JDBC.TESTSQL);
            while (rs.next()) {
            }
            rs.close();
            stmt.close();
            connPool.returnConnection(conn);
        }
    }
}
package JDBCConnectionPool.utils;

import JDBCConnectionPool.ConnectionPool;

/**
 * 连接池工具类,返回唯一的一个数据库连接池对象,单例模式
 * @author 张爽
 * @date 2017-7-26
 */
public class ConnectionPoolUtils {
    private static ConnectionPool poolInstance = null;

    private ConnectionPoolUtils() {
    }
    /**
     * 单例模式获得数据库连接池对象
     * @return
     * @author 张爽
     * @date 2017-7-27
     */
    public synchronized static ConnectionPool GetPoolInstance() {
        try {
            // 数据库连接池如果为空,我们就创建一个数据库连接池
            if (poolInstance == null) { 
                poolInstance = new ConnectionPool(
                        JDBC.DRIVER, 
                        JDBC.URL, 
                        JDBC.USERNAME, 
                        JDBC.PWD);
                poolInstance.createPool();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return poolInstance;
    }

}
package JDBCConnectionPool.utils;
/**
 * 数据库连接信息
 * @author 张爽
 * @date 2017-7-27
 */
public class JDBC {
    /** 数据库驱动 */
    public static final String DRIVER = "oracle.jdbc.driver.OracleDriver";
    /** 数据库连接url */
    public static final String URL = "";
    /** 数据库连接名 */
    public static final String USERNAME = "";
    /** 数据库连接密码 */
    public static final String PWD = "";
    /** 数据库连接设置的sql语句 */
    public static final String TESTSQL = "select " +
                                            "* " +
                                          "from " +
                                            "isc_user iu " +
                                          "where " +
                                            "iu.id = ''";
}
package JDBCConnectionPool;

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;

/**
 * 数据库连接池<br/>
 * JDBC应用程序访问数据库的过程:<br/>
 *  1.装载数据库驱动程序;<br/>
 *  2.通过jdbc建立数据库连接;<br/>
 *  3.访问数据库,执行sql语句;<br/>
 *  4.断开数据库连接。<br/>
 * 数据库连接池:<br/>
 *  为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,<br/>
 *  当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。<br/>
 *  我们可以通过设定连接池最大连接数来防止系统无尽的与数据库连接<br/>
 *  创建数据库连接池大概有3个步骤:<br/>
 *      1.创建ConnectionPool实例,并初始化创建10个连接,保存在Vector中(线程安全)<br/>
 *      2.实现getConnection()从连接库中获取一个可用的连接<br/>
 *      3.returnConnection(conn) 提供将连接放回连接池中方法<br/>
 *  数据库连接池:<br/>
 *  特性1:连接池中的对象可增长的<br/>
 *  特性2:先是初始化小量的Connection对象<br/>
 *  特性3:当连接池的长度超过了一部分长度,还可以为他增加一部分长度<br/>
 * @author 张爽
 * @date 2017-7-26
 */
public class ConnectionPool {
    /** 数据库驱动 */
    private String jdbcDriver = ""; 
    /** 数据库 URL */
    private String dbUrl = ""; 
    /** 数据库用户名 */
    private String dbUsername = "";
    /** 数据库用户密码 */
    private String dbPassword = "";  

    /** 测试连接是否可用的测试表名,默认没有测试表 */
    private String testTable = ""; 
    /** 连接池的初始大小,默认为10 */
    private int initialConnections = 10; 
    /** 连接池自动增加的大小,默认为5 */
    private int incrementalConnections = 5;
    /** 连接池最大的大小,默认为50。maxConnections为0或者为负数,表示连接数量没有限制 */
    private int maxConnections = 50;

    /** 存放连接池中数据库连接的向量 ,初始时为 null */
    private Vector<PooledConection> connectionsVector = null; 

    /**
     * 构造函数 -- 数据库连接池的创建
     * @param jdbcDriver JDBC 驱动类串
     * @param dbUrl 数据库URL
     * @param dbUsername 连接数据库用户名
     * @param dbPassword 连接数据库用户的密码
     * @author 张爽
     * @date 2017-7-26
     */
    public ConnectionPool(String jdbcDriver, String dbUrl, String dbUsername,  
            String dbPassword) {  
        this.jdbcDriver = jdbcDriver;  
        this.dbUrl = dbUrl;  
        this.dbUsername = dbUsername;  
        this.dbPassword = dbPassword;  
    } 

    /**
     * 创建一个数据库连接池,连接池中的可用连接数量要采用类成员:initialConnections中设置的值,默认为10<br/>
     * 步骤:<br/>
     *  1.连接池要有一个线程安全的集合(Vector)存储每一个JDBC的Connection连接对象<br/>
     *  2.Vector中存放所有的连接对象Connection<br/>
     *  3.但是步骤2并不能标识这个Connection是否是 占用 状态<br/>
     *  4.创建内部类PooledConection,存放Connection对象和busy,用busy标识每个<br/>
     *  Connection是否占用<br/>
     *  5.那么Vector的泛型也就得是PooledConection类型的对象,并且PooledConection要提<br/>
     *  供Connection和busy的get/set方法<br/>
     *  6.获得Connection并将Connection放到PooledConection封装类中<br/>
     * @throws Exception
     * @author 张爽
     * @date 2017-7-26
     */
    public synchronized void createPool() throws Exception {
        // 如果连接池已经创建了,则直接返回
        if (connectionsVector != null) {
            return; 
        }

        // 实例化 JDBC Driver 中指定的驱动类实例
        Driver driver = (Driver) Class.forName(this.jdbcDriver).newInstance();
        DriverManager.registerDriver(driver); // 注册JDBC驱动程序
        // 创建连接的向量,初始时有0个元素
        connectionsVector = new Vector<PooledConection>();
        // 根据 initialConnections中设置的值,创建连接
        createConnections(this.initialConnections);
        System.err.println("========================================数据库连接池创建成功!");
    }

    /**
     * 通过调用getFreeConnection()函数返回一个可用的数据库连接,
     * 如果当前没有可用的数据库连接,并且更多的数据库连接不能创建(如连
     * 接池大小的限制),此函数将等待一会再尝试获取
     * @return 返回一个可用的数据库连接对象
     * @author 张爽
     * @throws Exception 
     * @date 2017-7-26
     */
    public synchronized Connection getConnection() throws Exception {
        // 判断连接池是否已创建
        if (connectionsVector == null) {
            return null;
        }
        Connection conn = getFreeConnection(); // 获得一个可用的数据库连接
        while (conn == null) { // 如果目前没有可以使用的连接,即所有的连接都在使用中
            // 等一会再试
            System.err.println("Wait");
            wait(250);
            conn = getFreeConnection(); // 重新再试,直到获得可用的连接
        }
        return conn;
    }

    /**
     * 此函数返回一个数据库连接到连接池中,并把此连接置为空闲。
     * 所有使用连接池获得的数据库连接均应在不使用此连接时返回它。
     * @param conn 需要返回到连接池中的连接对象
     * @author 张爽
     * @date 2017-7-26
     */
    public void returnConnection(Connection conn) {
        // 确保连接池存在,如果连接没有创建(不存在),直接返回
        if (connectionsVector == null) {
            System.err.println(" 连接池不存在,无法返回此连接到连接池中 !");  
            return;
        }
        ConnectionPool.PooledConection pConn = null;
        Enumeration<PooledConection> enumerate = connectionsVector.elements();
        // 遍历连接池中的所有连接,找到这个要返回的连接对象
        while (enumerate.hasMoreElements()) {
            pConn = (ConnectionPool.PooledConection) enumerate.nextElement();
            // 先找到连接池中的要返回的连接对象
            if (conn == pConn.getConnection()) {
                // 找到了,设置此连接为空闲状态
                pConn.setBusy(false);
                break;
            }
        }
    }

    /**
     * 刷新连接池中所有的连接对象
     * @throws SQLException
     * @author 张爽
     * @date 2017-7-26
     */
    public synchronized void refreshConnection() throws SQLException {
        // 确保连接池已创建并存在
        if (connectionsVector == null) {
            System.err.println(" 连接池不存在,无法刷新 !");  
            return;
        }
        ConnectionPool.PooledConection pConn = null;
        Enumeration<PooledConection> enumerate = connectionsVector.elements();
        while (enumerate.hasMoreElements()) {
            pConn = (ConnectionPool.PooledConection) enumerate.nextElement();
            // 获得一个连接对象
            pConn = enumerate.nextElement();
            // 如果对象忙则等5秒,5秒后直接刷新
            if (pConn.isBusy()) {
                wait(5000); // 等5秒
            }
            // 关闭此连接,用一个新的连接代替它
            closeConnection(pConn.getConnection());
            pConn.setConnection(newConnection());
            pConn.setBusy(false);
        }
    }

    /**
     * 关闭连接池中所有的连接,并清空连接池
     * @throws SQLException
     * @author 张爽
     * @date 2017-7-26
     */
    public synchronized void closeConnectionPool() throws SQLException {
        if (connectionsVector == null) {
            System.err.println("连接池不存在,无法关闭 ");  
            return;  
        }
        ConnectionPool.PooledConection pConn = null;
        Enumeration<PooledConection> enumerate = connectionsVector.elements();
        while (enumerate.hasMoreElements()) {
            pConn = (ConnectionPool.PooledConection) enumerate
                    .nextElement();
            // 如果忙,等5秒
            if (pConn.isBusy()) {
                wait(5000); // 等5秒
            }
            // 5秒后直接关闭它
            closeConnection(pConn.getConnection());
            // 从连接池向量中删除它 
            connectionsVector.removeElement(pConn);
        }
        connectionsVector = null;
        System.err.println("======================数据连接池已关闭");
    }

    /**
     * 创建指定数目的数据库连接,并把这些连接放入connections向量中
     * @param numConnection 要创建的数据库连接池中有几个数据库连接对象
     * @throws Exception
     * @author 张爽
     * @date 2017-7-26
     */
    private void createConnections(int numConnections) throws Exception {
        // 循环创建指定数目的数据库连接
        for (int i = 0; i < numConnections; i++) {
            // 连接池的连接数已达到上限
            if (this.maxConnections > 0 && 
                    this.connectionsVector.size() >= this.maxConnections) {
                break;
            }
            try {
                connectionsVector.addElement(new PooledConection(newConnection()));
            } catch (Exception e) {
                System.err.println(" 创建数据库连接失败! " + e.getMessage());  
                throw new SQLException();
            }
        }
        System.err.println("=======================================数据库连接已创建");
    }

    /**
     * 创建一个新的数据库连接并返回它
     * @return
     * @throws SQLException
     * @author 张爽
     * @date 2017-7-26
     */
    private Connection newConnection() throws SQLException {
        // 创建一个数据库连接
        Connection conn = DriverManager.getConnection(dbUrl, dbUsername, 
                dbPassword);

        // 如果这是  第一次  创建数据库连接,就获得此数据库允许支持的最大客户连接数目
        if (connectionsVector.size() == 0) {
            DatabaseMetaData metaData = conn.getMetaData();
            int driverMaxConnections = metaData.getMaxConnections();
            // 1.数据库返回的driverMaxConnections若为0,表示此数据库没有最大连接限制
            // 或数据库的最大连接线至获取不到
            // 2.driverMaxConnections返回的是一个整数,表示此数据库允许客户连接的数目
            // 3.如果连接池中设置的最大连接数量大于数据库允许的连接数目,则置连接池的最大连接
            // 数目为数据库允许的最大数目
            if (driverMaxConnections > 0 && 
                    this.maxConnections > driverMaxConnections) {
                this.maxConnections = driverMaxConnections;
            }
        }
        return conn;
    }

    /**
     * 本函数从连接池向量connections中返回一个可用的数据库连接,如果,当
     * 前没有可用的数据库连接,本函数则根据incrementalConnections
     * 设置的值创建几个数据库连接,并放入连接池中。如果创建后,所有的连接仍都
     * 在使用中,则返回null
     * @return 返回一个可用的数据库连接
     * @throws Exception
     * @author 张爽
     * @date 2017-7-26
     */
    private Connection getFreeConnection() throws Exception {
        // 从连接池中寻找一个可用的数据库连接对象
        Connection conn = findFreeConnection();
        if (conn == null) {
            // 如果目前连接池中没有可用的连接
            // 则创建一些连接
            createConnections(incrementalConnections);
            // 重新从池中查找是否有可用的连接
            conn = findFreeConnection();
            if (conn == null) {
                // 如果创建连接后仍获得不到可用的连接,则返回null
                return null;
            }
        }
        return conn;
    }

    /**
     * 1.查找连接池中所有的连接
     * 2.查找一个可用的数据库连接,如果没有可用的连接返回null
     * @return 返回一个可用的数据库连接
     * @throws SQLException
     * @author 张爽
     * @date 2017-7-26
     */
    private Connection findFreeConnection() throws SQLException {
        Connection conn = null;
        ConnectionPool.PooledConection pConn = null;
        // 获得连接池向量中所有的对象
        Enumeration<PooledConection> enumerate = connectionsVector.elements();
        while (enumerate.hasMoreElements()) {
            // 获得一个连接对象
            pConn = (ConnectionPool.PooledConection) enumerate.nextElement();
            // 如果对象忙则等5秒,5秒后直接刷新
            if (!pConn.isBusy()) {
                // 如果此对象不忙,则获得它的数据库连接并把它设为忙
                conn = pConn.getConnection(); 
                pConn.setBusy(true);
                // 测试连接是否可用
                if (!testConnection(conn)) {
                    // 如果此连接不可再用,则创建一个新的连接
                    // 并替换此不可用的连接对象,如果创建失败,返回null
                    try {
                        conn = newConnection();
                    } catch (Exception e) {
                        System.err.println(" 创建数据库连接失败! " + e.getMessage());  
                        e.printStackTrace();
                        return null;  
                    }
                    pConn.setConnection(conn);
                }
                break; // 已经找到一个可用的连接,退出
            }
        }
        return conn; // 返回找到可用的连接
    }

    /**
     * 使程序等待给定的毫秒数
     * @param mSeconds 给定的毫秒数
     * @author 张爽
     * @date 2017-7-26
     */
    private void wait(int mSeconds) {
        try {
            Thread.sleep(mSeconds);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 测试一个连接是否可用,如果不可用,关掉它并返回false,否则返回true
     * @param conn 需要测试的数据库连接
     * @return
     * @author 张爽 true:表示此连接可用; false:表示不可用
     * @date 2017-7-26
     */
    private boolean testConnection(Connection conn) {
        try {
            if (testTable.equals("")) {
                // 如果测试表为空,试着使用此连接的setAutoCommit()方法
                // 来判断连接是否可用(此方法只在部分数据库可用,如果不可用,
                // 抛出异常)。
                // 注意:使用测试表的方法更可靠
                conn.setAutoCommit(true);
            } else {
                // 有测试表的时候,使用测试表测试
                Statement stmt = conn.createStatement();
                stmt.execute("select coutn(*) from " + testTable);
            }
        } catch (SQLException e) {
            // 上面抛出异常,此连接己不可用,关闭它,并返回 false 
            closeConnection(conn);
            e.printStackTrace();
            return false;
        }
        return true; // 连接可用,返回true
    }

    /**
     * 关闭一个数据库连接
     * @param conn 需要关闭的数据库连接
     * @author 张爽
     * @date 2017-7-26
     */
    private void closeConnection(Connection conn) {
        try {
            conn.close();
        } catch (SQLException e) {
            System.err.println(" 关闭数据库连接出错: " + e.getMessage());  
            e.printStackTrace();
        }
    }

    // =========================== start: 内部类:PooledConection =============================
    /**
     * 内部类,用于保存连接池中连接对象的类<br/>
     * 此类中有两个成员:<br/>
     *   一个是数据库的连接对象<br/>
     *   另一个是标识此数据库连接对象是否被占用<br/>
     * @author 张爽
     * @date 2017-7-26
     */
    class PooledConection {
        /** 数据库连接 */
        Connection connection = null; 
        /** 此连接是否正在使用的标志,默认没有正在使用 */
        boolean busy = false; 

        /**
         * 构造方法<br/>
         * 内部类,用于保存连接池中连接对象的类<br/>
         * 1.数据库的连接对象:Connection connection<br/>
         * 2.标识连接是否正在使用:boolean busy
         * 
         * @param connection 数据库连接对象
         * @author 张爽
         * @date 2017-7-26
         */
        public PooledConection(Connection connection) {
            this.connection = connection;
        }

        /**
         * 返回此对象中的连接 
         */
        public Connection getConnection() {
            return connection;
        }

        /**
         * 设置此对象的连接 
         * @param connection
         */
        public void setConnection(Connection connection) {
            this.connection = connection;
        }

        /**
         * 获得对象连接是否忙
         * @return
         */
        public boolean isBusy() {
            return busy;
        }

        /**
         * 设置对象的连接正在忙
         * @param busy
         */
        public void setBusy(boolean busy) {
            this.busy = busy;
        }
    }
    // =========================== end: 内部类:PooledConection =============================

    // ===========================start: get/set方法 =============================
    /**
     * 返回连接池的初始大小
     * @return 初始连接池中可获得的连接数量
     * @author 张爽
     * @date 2017-7-26
     */
    public int getInitialConnections() {  
        return this.initialConnections;  
    } 

    /**
     * 用于设置连接池的初始大小
     * @param initialConnections 用于设置初始连接池中连接的数量
     * @author 张爽
     * @date 2017-7-26
     */
    public void setInitialConnections(int initialConnections) {  
        this.initialConnections = initialConnections;  
    }

    /**
     * 返回连接池中自动增加的大小
     * @return 连接池中自动增加的大小
     * @author 张爽
     * @date 2017-7-26
     */
    public int getIncrementalConnections() {  
        return this.incrementalConnections;  
    } 

    /**
     * 设置连接池自动增加的大小
     * @param incrementalConnections 连接池自动增加大小
     * @author 张爽
     * @date 2017-7-26
     */
    public void setIncrementalConnections(int incrementalConnections) {  
        this.incrementalConnections = incrementalConnections;  
    } 

    /**
     * 连接池中最大可用的连接数量
     * @return 连接池中最大可用的连接数量
     * @author 张爽
     * @date 2017-7-26
     */
    public int getMaxConnections() {  
        return this.maxConnections;  
    }  

    /**
     * 设置连接池中最大可用的连接数量
     * @param maxConnections 设置连接池中最大可用的连接数量
     * @author 张爽
     * @date 2017-7-26
     */
    public void setMaxConnections(int maxConnections) {  
        this.maxConnections = maxConnections;  
    }

    /**
     * 获取测试数据库表的名字
     * @return 测试数据库表的名字
     * @author 张爽
     * @date 2017-7-26
     */
    public String getTestTable() {  
        return this.testTable;  
    }

    /**
     * 设置测试表的名字
     * @param testTable 测试表的名字
     * @author 张爽
     * @date 2017-7-26
     */
    public void setTestTable(String testTable) {  
        this.testTable = testTable;  
    }
    // ===========================end: get/set方法 =============================
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值