完整版见 https://jadyer.github.io/ /**********【目标】*************************************************************/ //该实例讲解了用Java如何编写实现一个数据库连接池 //通过编写数据库连接池,了解连接池内部运行机制和核心代码的实现 /**********【需解决的核心问题】**************************************************/ //首先,需要在程序中实例一个对象容器,保存建立好的数据库连接对象 //通过它对所有数据连接统一管理,获取连接池中连接的方法应该使用设计模式中的单子模式 //让所有想获得连接池中连接的外部程序只能通过同一个连接池管理对象来获得 //最好还有相应的处理机制,能够把超过一定时限没有使用的数据库连接对象强行收回 //同时能够调整连接池中的连接数,也就是可以设定连接池的初始大小 /**********【程序待改进的地方】**************************************************/ //这里编写的连接池程序功能上还很简单 //在实际中使用数据库连接池,不仅需要缓冲连接对象,通常还有缓冲Statement对象 //因为每申请一个连接(Connection)会在物理网络上建立一个用于通讯的连接 //在此连接上还可以申请一定数量的Statement //一般的应用中,有多少个程序调用,则会产生多少次物理连接 //每个Statement单独占用一个物理连接,这是极大的资源浪费 //我们可以通过资源池实现对Statement对象的缓冲,让Statement对象也资源池化 //这样就可以几十、几百个Statement占占用一个物理连接,发挥数据库的最大性能 /**********【步骤】*************************************************************/ //1..本例中使用的数据库是Oracle10g_10.2.0.1.0数据库,用到的JDBC驱动为ojdbc6.jar // 建表脚本如下: // create table guestbook(id number primary key, name varchar2(20) not null); //2..在项目的根目录下新建一个dbpool.properties文件,这个文件是一个文本文件 // 记录了建立数据库连接池所需要的数据库参数,所以称该文件为连接池的配置文件 // 如果需要改变数据库的类型,只需要修改该文件中的内容,不需要修改连接池程序代码 // dbpool.properties内容如下: // driverClassName=oracle.jdbc.driver.OracleDriver // username=scott // password=oracle // url=jdbc:oracle:thin:@127.0.0.1:1521:ora10 // poolSize=10 // 下面简要说明dbpool.properties文件中的几个参数含义: // driverClassName----------代表数据库的驱动类 // username和password-------代表数据库的用户名和密码 // url----------------------代表建立数据库连接所需要的URL // poolSize-----------------代表连接池创建的初始连接数 //3..ConnectionPool.java------连接池的核心类 // ConnectionPoolTest.java--连接池的测试类 //4..下面会附上ConnectionPool.java和ConnectionPoolTest.java的具体代码 /******************************************************************************/package com.jadyer.dbpool; import java.io.FileInputStream; import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; import java.util.Vector; /** * 实现连接池的核心类 * @author Administrator * */ public class ConnectionPool { private String url; private String username; private String password; private String driverClassName; private Vector<Connection> pool; //用Vector类型的对象pool作为容器保存所有的数据库连接 private int poolSize = 1; //连接池的大小,也就是连接池中有多少个数据库连接 private static ConnectionPool instance = null; //代表当前类的一个对象,它是静态的 /** * 私有的构造方法,禁止外部创建本类的对象 * @see 要想获得该类的对象,可以通过<code>getIstance()</code>方法 * @see 使用了单子模式,保证只有一个ConnectionPool对象供使用 */ private ConnectionPool() { init(); } /** * 连接池初始化方法,读取属性文件的内容,建立连接池中的初始连接 */ private void init() { pool = new Vector<Connection>(poolSize); readConfig(); //读取配置文件 addConnection(); //向连接池中添加指定数目的连接 } /** * 返回连接到连接池中 * @see 该方法让使用连接池的程序释放所使用的数据库连接 * @see 并把被释放的连接重新添加到连接池中,供重复使用 */ public synchronized void release(Connection conn) { pool.add(conn); } /** * 关闭连接池中的所有数据库连接 * @see 当结束使用使用连接池的时候,可以调用该方法 */ public synchronized void closePool() { for (int i = 0; i < pool.size(); i++) { try { //先获取每一个Connection对象,然后调用它们的close()方法,将其关闭 ((Connection) pool.get(i)).close(); } catch (SQLException e) { e.printStackTrace(); } pool.remove(i); //从Vector中将其移除掉 } } /** * 返回当前连接池的一个对象 */ public static ConnectionPool getInstance() { if (instance == null) { instance = new ConnectionPool(); } return instance; } /** * 返回连接池中的一个数据库连接 */ public synchronized Connection getConnection() { if (pool.size() > 0) { Connection conn = pool.get(0); //从Vector中获取,获取的都是当前的第一个 pool.remove(conn); //获取完之后,就从当前连接池中临时的移除掉 return conn; } else { return null; } } /** * 在连接池中创建初始设置的的数据库连接 * @see 即建立数据库连接并且加入到pool对象中 */ private void addConnection() { Connection conn = null; for (int i = 0; i < poolSize; i++) { try { Class.forName(driverClassName); conn = java.sql.DriverManager.getConnection(url, username, password); pool.add(conn); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } } } /** * 读取设置连接池的属性文件 */ private void readConfig() { try { //获取配置文件的路径 String path = System.getProperty("user.dir") + "//dbpool.properties"; //通过IO的方式加载配置文件 FileInputStream is = new FileInputStream(path); Properties props = new Properties(); props.load(is); //根据参数名读取指定参数的值 this.driverClassName = props.getProperty("driverClassName"); this.username = props.getProperty("username"); this.password = props.getProperty("password"); this.url = props.getProperty("url"); this.poolSize = Integer.parseInt(props.getProperty("poolSize")); } catch (Exception e) { e.printStackTrace(); System.err.println("读取属性文件出错.."); } } } package com.jadyer.dbpool; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; /** * 连接池的测试类 * @author Administrator * */ public class ConnectionPoolTest { //分别计算使用连接池获得连接和传统方式获得连接所花费的时间 //为了让对比更加明显,我们这里计算的是循环100次所花费的时间 public static void main(String[] args) throws Exception { String sql = "select id,name from guestbook"; long start = System.currentTimeMillis(); //获取当前的毫秒数 ConnectionPool pool = null; for (int i = 0; i < 100; i++) { pool = ConnectionPool.getInstance(); //获得一个连接池对象 Connection conn = pool.getConnection(); //向连接池发出请求,获得一个连接对象 Statement stmt = conn.createStatement(); //获得到连接对象之后,用法就几乎一样了 ResultSet rs = stmt.executeQuery(sql); while (rs.next()) {} rs.close(); stmt.close(); pool.release(conn); //将连接对象返回给连接池中,供重复使用 } pool.closePool(); //使用完连接池对象后,将所有的连接池对象释放掉 System.out.println("经过100次的循环调用,使用连接池花费的时间:" + (System.currentTimeMillis() - start) + "ms"); /** * ************************************************ * 【上面为使用连接池的代码,下面为不使用连接池的代码】 * ************************************************ */ //这里亦可使用String driverClass = "oracle.jdbc.driver.OracleDriver"; //二者的区别是oracle.jdbc.OracleDriver继承了oracle.jdbc.driver.OracleDriver String driverClass = "oracle.jdbc.OracleDriver"; String url = "jdbc:oracle:thin:@127.0.0.1:1521:ora10"; String user = "scott"; String password = "oracle"; start = System.currentTimeMillis(); for (int i = 0; i < 100; i++) { Class.forName(driverClass); Connection conn = DriverManager.getConnection(url, user, password); 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"); } } /****【下面为ConnectionPoolTest.java的运行结果】****/ //经过100次的循环调用,使用连接池花费的时间:1125ms //经过100次的循环调用,未使用连接池花费时间:2453ms /**************************************************/