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

为什么用连接池

-我们最初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);
        }
    }

}

执行程序
查询结果

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

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值