c3p0数据库连接池实现原理笔记

来源:互联网 发布:java udp编程 编辑:程序博客网 时间:2024/06/06 02:24

为什么用连接池

-我们最初jdbc连接数据库需要
1. 安装驱动
2. 取得连接
3. 打开窗口
4. 书写语句、执行语句和查看结果
5. 关闭到数据库的连接
其中1,2和5完全可以重用,这样就加快了执行。也即:
 同一个连接可以打开多个窗口
 同一个窗口可以多次执行语句
我们发现jdbc连接数据库时对于同一个项目来说,如果每次操作数据库的话,每一次查询添加…,使用上面的方法就是要建立多次数据库连接,而每一次获得连接对于Java代码来说都是重复动作,不仅占用了空间,而且在执行效率上也产生了浪费

  • 解决连接重用的方案分析
    我们想象火车站售票大厅的情况,平时客流比较少时,我们只开几个售票窗口,不管有没有顾客来买票:多时间处于空闲状态。在如节假日等高峰期,所有售票窗口都会启用。特别地,在春运这样的最高峰期,可能会有临时窗口。但临时窗口也不是无限制地多开,开窗口要耗费物力人力。这样在高峰期只能让客户等待长一点的时间。春运最高峰时排队,这个时间客户就需要等待。
    我们在重用数据库连接时也可以采用类似的方案。我们先打开一批连接等待客户使用(如同非高峰期我们开几个窗口) ,不管用没有客户使用。在高峰期到来时,我们可以多开几个连接让浏览器客户使用。如同春运一样不可能为了让每个人都有一个窗口服务,我们的数据库连接也不能无限开启(数据库连接要占用内存等资源,同时每个数据库能支持的并发连接数也不是无限的).在不能为需要的数据的客户立马服务时,只能让客户排队等待。如果用户一直无法得到一个连接对象时,就不要让用户无限期等待。
    从上面可以看出,我们重用连接的方案,需要提供以下信息:
    1. 预先开启的连接数
    2. 能开启的最大连接数
    3. 一个连接要被多个客户使用,因而连接用完不能关闭
    4. 一个超时时间,如果超过这个时间,用户就无法获取连接而得到数据。
      在JDBC连接数据库的连接重用方案里,我们称为连接池。
      连接池中可以重用的包括Connection对象和Statement对象

连接池的作用及原理
1. 预先开启的连接数
2. 能开启的最大连接数
3. 一个连接要被多个客户使用,因而连接用完不能关闭
4. 一个超时时间,如果超过这个时间,用户就无法获取连接而得到数据。

自定义连接池

原理
定义一个集合来保存一定数量的connection连接
重写Connection的close方法—不真正关闭而是把它放置到连接池中

  • 导入相关jar包
    -这里使用的是Oracle数据库我们就导入ojdbc6.jar
  • jdbc.properties配置文件
    -url=jdbc:oracle:thin:@localhost:1521:orcl
    user=lgh
    password=lgh
    className=oracle.jdbc.OracleDriver
  • 编写数据库连接工具类
package com.jt.demo07;import java.io.IOException;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import java.util.Properties;/** * 数据库连接工具类 * 此类主要是加载驱动创建连接 * */public class JDBCUnti {    private static String url = null;    private static String user = null;    private static String password = null;    private static String className = null;    private static Properties into = new Properties();    static {        try {            // 读取文件和类在一起就用该方法            into.load(JDBCUnti.class.getResourceAsStream("jdbc.properties"));            url = into.getProperty("url");            user = into.getProperty("user");            password = into.getProperty("password");            className = into.getProperty("className");        } catch (IOException e) {            e.printStackTrace();        }    }    static {        try {            // DriverManager.registerDriver(new OracleDriver());            // 这种注册方式实际上注册了两次驱动            Class.forName(className);        } catch (ClassNotFoundException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }    public static Connection getConnection() throws SQLException {        return DriverManager.getConnection(url, user, password);    }    public static void free(Statement stmt, Connection conn) {        if (stmt != null) {            try {                stmt.close();            } catch (SQLException e) {                e.printStackTrace();            }        }        if (conn != null) {            try {                conn.close();            } catch (SQLException e) {                e.printStackTrace();            }        }    }    public static void free(ResultSet res, Statement stmt, Connection conn) {        if (res != null) {            try {                res.close();            } catch (SQLException e) {                e.printStackTrace();            }        }        free(stmt, conn);    }}

编写连接池需实现java.sql.DataSource接口。DataSource接口中定义了两个重载的getConnection方法:

Connection getConnection()Connection getConnection(String username, String password)

实现DataSource接口,并实现连接池功能的步骤:

在DataSource构造函数中批量创建与数据库的连接,并把创建的连接加入LinkedList对象中。实现getConnection方法,让getConnection方法每次调用时,从LinkedList中取一个Connection返回给用户。当用户使用完Connection,调用Connection.close()方法时,Collection对象应保证将自己返回到List中,而不要把conn还给数据库。Collection保证将自己返回到List中是此处编程的难点。
package com.jt.demo07;import java.io.PrintWriter;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.sql.Connection;import java.sql.SQLException;import java.sql.SQLFeatureNotSupportedException;import java.util.ArrayList;import java.util.List;import java.util.logging.Logger;import javax.sql.DataSource;     /**     * List 集合模拟一个池子 存放lianjie     * 静态代码块在类被加载时就获得执行      * 预期存入5个数据库连接     * */public class MyDatasource implements DataSource {    public static List<Connection> pool = new ArrayList<Connection>();    static{        for(int i=0;i<5;i++){            try {                pool.add(JDBCUnti.getConnection());            } catch (SQLException e) {                e.printStackTrace();            }        }    }    @Override    public PrintWriter getLogWriter() throws SQLException {        // TODO Auto-generated method stub        return null;    }    @Override    public void setLogWriter(PrintWriter out) throws SQLException {        // TODO Auto-generated method stub    }    @Override    public void setLoginTimeout(int seconds) throws SQLException {        // TODO Auto-generated method stub    }    @Override    public int getLoginTimeout() throws SQLException {        // TODO Auto-generated method stub        return 0;    }    @Override    public Logger getParentLogger() throws SQLFeatureNotSupportedException {        // TODO Auto-generated method stub        return null;    }    @Override    public <T> T unwrap(Class<T> iface) throws SQLException {        // TODO Auto-generated method stub        return null;    }    @Override    public boolean isWrapperFor(Class<?> iface) throws SQLException {        // TODO Auto-generated method stub        return false;    }    @Override    public Connection getConnection() throws SQLException {        if(pool.size()>0){             final Connection conn = pool.remove(0);             System.out.println("conn de ++++"+conn.getClass());            return (Connection)Proxy.newProxyInstance(null, new Class[]{Connection.class}, new InvocationHandler() {                @Override                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//这是一个方法内部类 通过动态代理的方法来实现对  Connection的close方法的//重写,以达到调用close方法不会关闭连接,而是把它放到连接池中            System.out.println("_____"+this.getClass());                    if(method.getName().equals("close")){                        System.out.println("aaaaaaaa"+this.getClass());                        pool.add(conn);                    }else{                        return method.invoke(conn, args);                    }                    return null;                }            });        }        return null;    }    @Override    public Connection getConnection(String username, String password)            throws SQLException {        // TODO Auto-generated method stub        return null;    }}

编写Java程序测试
编写查询数据库操作代码

package com.jt.demo07;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import oracle.jdbc.OracleDriver;public class JdbcSelect {    /**     * 测试MyDatasource连接池     *      * */    public static void main(String[] args) throws SQLException {        Connection conn = null;        Statement stmt = null;        ResultSet res = null;        MyDatasource md = new MyDatasource();        try {            conn = md.getConnection();            stmt = conn.createStatement();            String sql = "select * from t_user";            res = stmt.executeQuery(sql);            System.out.println("id\tname\tpwd\tage\tbirthday");            while (res.next()) {                System.out.println(res.getInt("id") + "\t"                        + res.getString("name") + "\t" + res.getString("pwd")                        + "\t" + res.getInt("age") + "\t"                        + res.getDate("birthday"));            }        } catch (Exception e) {            e.printStackTrace();        } finally {            JDBCUnti.free(res, stmt, conn);        }    }}

执行程序
查询结果

第一次写博客,没什么经验,主要是想把学习的内容写出来,做个总结,有什么不足之处还请谅解

0 0
原创粉丝点击