`

很不错的数据库连接池入门代码(详细注释版)

阅读更多

首先,真的很感谢这篇文章的作者,写了如此详细的注释,让初学者们更好的理解连接池。

package my;
import java.sql.*;
import java.util.*;
public class ConnectionPool {
    private String jdbcDriver = "";//数据库驱动
    private String dbUrl = "";//Database URL
    private String username = "";
    private String password = "";
    private String testTable = "";//测试连接是否可用的测试表名,默认没有测试表
    private int initConnections = 10;//连接池初始大小
    private int incrementalConnections = 5;//连接池自动增加的大小
    private int maxConnections = 50;//连接池连接数上限
    @SuppressWarnings("unchecked")
    private Vector connections = null;//存放连接池中数据库连接的Vector , 初始时为 null
    // 它中存放的对象为 PooledConnection 型
    
    /** *//**
     * 默认构造函数
     */
    
    public ConnectionPool () {}
    
    /** *//**
     * 自定义构造函数
     * 
     * @param jdbcDriver 驱动
     * @param dbUrl 数据库URL
     * @param username 用户名
     * @param password 密码
     */
    public ConnectionPool (String jdbcDriver, String dbUrl, String username, String password) {
        this.jdbcDriver = jdbcDriver;
        this.dbUrl = dbUrl;
        this.username = username;
        this.password = password;
    }
    
    /** *//**
     * 获取测试数据库表的名字
     * @return
     */
    public String getTestTable() {
        return testTable;
    }
    /** *//**
     * 设置测试数据库表的名字
     * @param testTable
     */
    public void setTestTable(String testTable) {
        this.testTable = testTable;
    }
    /** *//**
     * 获取连接池的初始大小
     * @return 初始连接池中可获得的连接数量
     */
    public int getInitConnections() {
        return initConnections;
    }
    /** *//**
     * 设置连接池的初始大小
     * @param initConnections 用于设置初始连接池中连接的数量
     */
    public void setInitConnections(int initConnections) {
        this.initConnections = initConnections;
    }
    /** *//**
     * 获取连接池自动增加的大小
     * @return 连接池自动增加的大小
     */
    public int getIncrementalConnections() {
        return incrementalConnections;
    }
    /** *//**
     * 设置连接池自动增加的大小
     * @param incrementalConnections 连接池自动增加的大小
     */
    public void setIncrementalConnections(int incrementalConnections) {
        this.incrementalConnections = incrementalConnections;
    }
    /** *//**
     * 获取连接池中最大的可用连接数量
     * @return 连接池中最大的可用连接数量
     */
    public int getMaxConnections() {
        return maxConnections;
    }
    /** *//**
     * 设置连接池中最大可用的连接数量
     * @param maxConnections 设置连接池中最大可用的连接数量值
     */
    public void setMaxConnections(int maxConnections) {
        this.maxConnections = maxConnections;
    }
    
    /** *//**
     * 使程序等待给定的毫秒数
     * @param mSeconds 给定的毫秒数
     */
    private void wait(int mSeconds) {
        try {
         Thread.sleep(mSeconds); 
        } catch (InterruptedException e) {
        }
    }
    /** *//**
     * 刷新连接池中所有的连接对象
     * @throws SQLException
     */
    @SuppressWarnings("unchecked")
    public synchronized void refreshConnections() throws SQLException {
        // 确保连接池己创新存在
        if (connections == null) {
            System.out.println(" 连接池不存在,无法刷新 !");
            return;
        }
        
        PooledConnection pooledConn = null;
        Enumeration enu = connections.elements();
        while (enu.hasMoreElements()) {
            pooledConn = (PooledConnection)enu.nextElement();// 获得一个连接对象
            
            // 如果对象忙则等 5 秒 ,5 秒后直接刷新
            if (pooledConn.isBusy()) {
                wait(5000);
            }
            
             // 关闭此连接,用一个新的连接代替它。
            closeConnection(pooledConn.getConn());
            pooledConn.setConn(newConnection());
            pooledConn.setBusy(false);
        }
    }
    
    /** *//**
     * 关闭连接池中所有的连接,并清空连接池。
     * @throws SQLException
     */
    @SuppressWarnings("unchecked")
    public synchronized void closeConnectionPool() throws SQLException {
         // 确保连接池存在,如果不存在,返回
        if (connections == null) {
            System.out.println(" 连接池不存在,无法关闭 !");
            return;
        }
        
        PooledConnection pooledConn = null;
        Enumeration enu = connections.elements();
        while (enu.hasMoreElements()) {
            pooledConn = (PooledConnection)enu.nextElement();// 获得一个连接对象
            
            // 如果对象忙则等 5 秒 ,5 秒后直接刷新
            if (pooledConn.isBusy()) {
                wait(5000);
            }
            
            //5 秒后直接关闭它
            closeConnection(pooledConn.getConn());
            
            // 从连接池向量中删除它
            connections.removeElement(pooledConn);
            //设置连接池为空
            connections = null;
        }
    }
    
    /** *//**
     * 关闭一个数据库连接
     * @param conn 需要关闭的数据库连接
     */
    private void closeConnection(Connection conn) {
        try {
         conn.close();
        }catch (SQLException e) {
         System.out.println(" 关闭数据库连接出错: "+e.getMessage());
        }
    }
    
    /** *//**
     * 创建一个新的数据库连接并返回它
     * @return 返回一个新创建的数据库连接
     * @throws SQLException
     */
    private Connection newConnection () throws SQLException {
        Connection conn = DriverManager.getConnection(dbUrl,username,password);//创建一个数据库连接
        
        // 如果这是第一次创建数据库连接,即检查数据库,获得此数据库允许支持的最大客户连接数目
        //connections.size()==0 表示目前没有连接己被创建
        
        if (connections.size() == 0) {
            DatabaseMetaData metadata = conn.getMetaData();
            
            //DatabaseMetaData接口是描述数据库元数据的。我们可以从中取得数据库的表、视图等信息。
            
            int driverMaxConnections = metadata.getMaxConnections();
            
            // 数据库返回的 driverMaxConnections 若为 0 ,表示此数据库没有最大 
            // 连接限制,或数据库的最大连接限制不知道
            //driverMaxConnections 为返回的一个整数,表示此数据库允许客户连接的数目
            // 如果连接池中设置的最大连接数量大于数据库允许的连接数目 , 则置连接池的最大连接数目为数据库允许的最大数目
            
            if (driverMaxConnections > 0 && this.maxConnections > driverMaxConnections) {
                this.maxConnections = driverMaxConnections;
            }
        }
        return conn;        
    }
    
    /** *//**
     *  创建由 numConnections 指定数目的数据库连接 , 并把这些连接
     *  放入 connections 向量中
     * @param numConnections 要创建的数据库连接的数目
     * @throws SQLException
     */
    @SuppressWarnings("unchecked")
    private void createConnections (int numConnections) throws SQLException {
        for (int i = 0; i < numConnections; i++) { //循环创建指定数目的数据库连接
            // 是否连接池中的数据库连接的数量己经达到最大?最大值由类成员 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(" 数据库连接己创建 ");
        }
    }
    /** *//**
     * 创建一个数据库连接池,连接池中的可用连接的数量采用类成员initConnections 中设置的值
     * @throws Exception
     * 
     * Java  synchronized 解析
        一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,
            一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。 
        二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,
            另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。 
        三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,
            其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。 
        四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,
            它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。 
        五、以上规则对其它对象锁同样适用
     */
    @SuppressWarnings("unchecked")
    public synchronized void createPool () throws Exception {
        // 确保连接池没有创建
        // 如果连接池己经创建了,保存连接的Vector中 connections 不会为空
        
        if (connections != null) {
            return; //如果已经创建,则返回
        }
        
        Driver driver = (Driver)(Class.forName(this.jdbcDriver)).newInstance();
        //实例化 JDBC Driver 中指定的驱动类实例
        
        DriverManager.registerDriver(driver);//注册jdbc驱动
        
        connections = new Vector();// 创建保存连接的向量 , 初始时有 0 个元素
        // 根据 initialConnections 中设置的值,创建连接。
        createConnections(this.initConnections);
        System.out.println("数据库连接池创建成功!");
    }
    
    /** *//**
     * 此函数返回一个数据库连接到连接池中,并把此连接置为空闲
     * 所有使用连接池获得的数据库连接均应在不使用此连接时返回它
     * @param conn 需返回到连接池中的连接对象
     */
    @SuppressWarnings("unchecked")
    public void returnConnection (Connection conn) {
        // 确保连接池存在,如果连接没有创建(不存在),直接返回
        if (connections == null) {
            System.out.println("连接池不存在,无法返回此连接到连接池中 !");
            return;
        }
        PooledConnection pooledConn = null;
        Enumeration enmu = connections.elements();// 遍历连接池中的所有连接,找到这个要返回的连接对象
        while (enmu.hasMoreElements()) {
            pooledConn = (PooledConnection) enmu.nextElement();
             // 先找到连接池中的要返回的连接对象
            
            if (conn == pooledConn.getConn()) {// 找到了 , 设置此连接为空闲状态
                pooledConn.setBusy(false);
                break;
            }
        }
    }
    
    /** *//**
     * 测试一个连接是否可用,如果不可用,关掉它并返回 false
     * 否则可用返回 true
     * @param conn 需要测试的数据库连接
     * @return 返回 true 表示此连接可用, false 表示不可用
     */
    private boolean testConnection(Connection conn) {
        try {
            //判断测试表是否存在
            if (testTable.equals("")) {
                 // 如果测试表为空,试着使用此连接的 setAutoCommit() 方法
                 // 来判断连接否可用(此方法只在部分数据库可用,如果不可用 ,
                 // 抛出异常)。注意:使用测试表的方法更可靠
                conn.setAutoCommit(true);
            } else {// 有测试表的时候使用测试表测试
                Statement sta = conn.createStatement();
                sta.execute("select count(*) from " + testTable);
            }
        } catch (SQLException e) {
             // 上面抛出异常,此连接己不可用,关闭它,并返回 false;
              closeConnection(conn);
              return false;
        }
        //连接可用,返回 true
        return true;
    }
    
    /** *//**
     * 查找连接池中所有的连接,查找一个可用的数据库连接
     * 如果没有可用的连接,返回 null
     * @return 返回一个可用的数据库连接
     * @throws SQLException
     */
    @SuppressWarnings("unchecked")
    private Connection findFreeConnection() throws SQLException {
        Connection conn = null;
        PooledConnection pooledConn = null;
        
        // 获得连接池向量中所有的对象
        Enumeration enu = connections.elements();
        // 遍历所有的对象,看是否有可用的连接
        while (enu.hasMoreElements()) {
            pooledConn = (PooledConnection)enu.nextElement();
            if (!pooledConn.isBusy()) {
                // 如果此对象不忙,则获得它的数据库连接并把它设为忙
                conn = pooledConn.getConn();
                pooledConn.setBusy(true);
                
                // 测试此连接是否可用
                if (!testConnection(conn)) {
                    // 如果此连接不可再用了,则创建一个新的连接, 
                    // 并替换此不可用的连接对象,如果创建失败,返回 null
                    try {
                        conn = newConnection();
                    } catch (SQLException e) {
                        System.out.println(" 创建数据库连接失败! "+e.getMessage());
                        return null;
                    }
                    pooledConn.setConn(conn);
                }
                break;// 己经找到一个可用的连接,退出
            }
        }
        return conn;// 返回找到到的可用连接
    }
    
    /** *//**
     * 本函数从连接池向量 connections 中返回一个可用的的数据库连接,如果
     * 当前没有可用的数据库连接,本函数则根据 incrementalConnections 设置
     * 的值创建几个数据库连接,并放入连接池中。
     * 如果创建后,所有的连接仍都在使用中,则返回 null
     * @return 返回一个可用的数据库连接
     * @throws SQLException
     */
    private Connection getFreeConnection() throws SQLException {
        
        //从连接池中获得一个可用的数据库连接
        Connection conn = findFreeConnection();
        
        if (conn == null) {
            // 如果目前连接池中没有可用的连接
            // 创建一些连接
            createConnections(incrementalConnections);
            
            // 重新从池中查找是否有可用连接
            conn = findFreeConnection();
            if (conn == null) {
                // 如果创建连接后仍获得不到可用的连接,则返回 null
                return null;
            }
        }
        
        return conn;
    }
    
    /** *//**
     * 通过调用 getFreeConnection() 函数返回一个可用的数据库连接
     * 如果当前没有可用的数据库连接,并且更多的数据库连接不能创建
     *(如连接池大小的限制),此函数等待一会再尝试获取。
     * @return 返回一个可用的数据库连接对象
     * @throws SQLException
     */
    public synchronized Connection getConnection() throws SQLException {
        
        //确保连接池己被创建
        if (connections == null) {
            return null; // 连接池还没创建,则返回 null
        }
        
        Connection conn = getFreeConnection();// 获得一个可用的数据库连接
        
        while (conn == null) {// 如果目前没有可以使用的连接,即所有的连接都在使用中
            wait(250);// 等一会再试
            
            conn = getFreeConnection();
            // 重新再试,直到获得可用的连接,如果  
            //getFreeConnection() 返回的为 null
            // 则表明创建一批连接后也不可获得可用连接
        }        
        return conn;
    }
    
}
package my;
import java.sql.*;
/** *//**
 * 内部使用的用于保存连接池中连接对象的类
 * 
 * 此类中有两个成员,一个是数据库的连接,另一个是指示此连接是否正在使用的标志
 * 
 * @author leonardo
 *
 */
public class PooledConnection {
    private Connection conn = null;;// 数据库连接
    boolean busy = false;// 此连接是否正在使用的标志,默认没有正在使用
    
     // 构造函数,根据一个 Connection 构告一个 PooledConnection 对象
    
    public PooledConnection (Connection connection) {
        
        this.conn = connection;
    }
    public Connection getConn() {
        return conn;
    }
    public void setConn(Connection conn) {
        this.conn = conn;
    }
    // 获得对象连接是否忙
    public boolean isBusy() {
        return busy;
    }
    // 设置对象的连接正在忙
    public void setBusy(boolean busy) {
        this.busy = busy;
    }
    
}
package my;
import java.sql.*;
public class Test {
    public static void main(String[] args) {
        
        ConnectionPool pool = new ConnectionPool(
"com.microsoft.sqlserver.jdbc.SQLServerDriver","jdbc:sqlserver://localhost:1433;databaseName=pubs","sa","sa");
                                                    //参数视不同数据库而定
        try {
            pool.createPool();
            Connection conn = pool.getConnection();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
分享到:
评论

相关推荐

    db2数据库入门教程(官方中文版)

    2.2.1数据库分区............................................................................................................23 2.2.2连接集中器(Connection Concentrator ).................................

    vc数据库编程,注释高清版

    - **概念**: 连接池是一种用于管理数据库连接的技术,可以提高应用程序的性能和响应速度。 - **实现方法**: 在VC中可以通过自定义类来管理连接池,例如创建一个包含多个预打开数据库连接的容器类。 - **优化策略**: ...

    JAVA上百实例源码以及开源项目源代码

    内容索引:JAVA源码,媒体网络,飞鸽传书 Java局域网通信——飞鸽传书源代码,大家都知道VB版、VC版还有Delphi版的飞鸽传书软件,但是Java版的确实不多,因此这个Java文件传输实例不可错过,Java网络编程技能的提升很有...

    db2数据库入门官方教程(中文版)

    2.2.1数据库分区............................................................................................................23 2.2.2连接集中器(Connection Concentrator ).................................

    MyEclipse+Hibernate项目开发快速入门

    3. 配置hibernate.cfg.xml:这是Hibernate的核心配置文件,用于设置数据库连接信息、方言、缓存策略等。在src目录下创建此文件并进行相应配置。 4. 实体类和映射文件:为每个数据库表创建对应的Java实体类,并在类...

    rmi原理与入门demo.zip

    远程方法调用(Remote Method Invocation,RMI)是Java提供的一种分布式计算技术,它允许一个Java对象调用网络另一端的Java对象的方法。...在实际应用中,RMI常用于构建分布式系统,如数据库连接池、分布式缓存服务等。

    新手入门必备java开发规范

    5. **数据访问与数据库操作**:在数据库操作上,规约推荐合理使用事务管理,避免SQL注入,以及正确处理连接池的配置和使用,以提高性能和资源利用率。 6. **日志记录**:日志是诊断问题的重要手段。规约建议根据...

    关于java dao的入门介绍

    在实际开发中,DAO的设计和实现还应考虑到性能优化,比如使用连接池管理数据库连接,使用PreparedStatement防止SQL注入,以及合理使用批处理操作提高数据处理效率。此外,对于复杂的业务场景,可能需要考虑事务的...

    jsp教程(很好的入门级教程)

    - 使用JNDI(Java Naming and Directory Interface)查找数据库连接池。 - **5.2 JDBC-ODBC桥接器** - 用于连接非Java数据库,如MS Access。 - **5.3 查询记录** - 包括顺序查询、游动查询、随机查询等多种查询...

    JSP 程序设计从入门到精通 PDF 教程

    - 这部分介绍了数据库连接缓冲池的概念,以及如何使用连接池来提高数据库访问效率。 ##### 6.4 JNDI 和RowSet - 这部分讨论了如何使用JNDI(Java Naming and Directory Interface)来查找和配置RowSet对象。 ####...

    java简单的学生管理系统

    可以使用连接池(如C3P0、HikariCP)来高效地复用数据库连接。 - **事务管理**:对涉及多条SQL语句的操作,应考虑事务的使用,确保数据的一致性和完整性。 5. **异常处理**: - Java中的try-catch-finally结构...

    Java学习从入门到精通.pdf

    学习如何使用JDBC(Java Database Connectivity)与数据库交互,理解事务的概念和管理,以及数据库连接池的使用。 通过全面学习上述知识点,开发者将能够构建、测试和部署完整的Java应用程序,无论是简单的Web应用...

    jsp从入门到高级编程

    11.1 JDBC2.0和JDBC数据库连接池 11.1.1 JDBC2.0新功能概述 11.1.2 结果集增强功能 11.1.3 批处理更新 11.1.4 对Java对象的持久性 11.1.5 数据库连接池使用实例 11.2 文件上载 11.2.1 实现机理 11.2.2 文件...

    jsp servlet 入门学习资料-新手一看就懂

    11.1 JDBC2.0和JDBC数据库连接池 11.1.1 JDBC2.0新功能概述 11.1.2 结果集增强功能 11.1.3 批处理更新 11.1.4 对Java对象的持久性 11.1.5 数据库连接池使用实例 11.2 文件上载 11.2.1 实现机理 11.2.2 文件...

    VB语言VB企业生产管理系统设计(论文+源代码+开题报告+答辩PPT)

    - **单例模式**:用于数据库连接池的管理,提高资源利用率。 #### 3. 关键技术实现 - **数据库操作**:通过ADO.NET实现对数据库的增删改查操作。 - **报表生成**:利用Crystal Reports等第三方组件生成各类统计报表...

    阿里巴巴Java开发手册 终极版

    8. **性能优化**:手册中也涉及了一些性能优化技巧,如避免过度使用反射和动态代理,减少不必要的对象创建,以及正确使用数据库连接池等。 9. **日志与监控**:日志是排查问题的重要工具,手册推荐使用统一的日志...

    真正的Java学习从入门到精通

    虽然Tomcat严格意义上并非一个完整的应用服务器,但它是一个支持Servlet和JSP的Web容器,并具备一些扩展功能,如JNDI、数据库连接池、用户事务处理等。Tomcat在中小型Java Web应用中非常受欢迎。Tomcat的官方网站是...

    阿里巴巴java开发手册最新版本

    5. **数据库操作**:手册会涉及JDBC的最佳实践,包括连接池的使用(如Druid)、SQL优化、事务管理等,强调减少数据库资源消耗,提升查询效率。 6. **安全性**:在网络安全日益重要的今天,手册会讲解如何防止SQL...

Global site tag (gtag.js) - Google Analytics