zoukankan      html  css  js  c++  java
  • .数据库连接池技术:DBCP和C3P0

    数据库连接池技术:DBCP和C3P0

    1.什么是数据库连接池

    已知的方法是需要访问数据库的时候进行一次数据库的连接,对数据库操作完之后再释放这个连接,通常这样业务是缺点很明显的:
    用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出,拓机。
    所有,有了数据库连接池的概念,在程序启动的时候,就自动创建几个数据库连接,放在一个池子里(集合),需要用的时候从这个池子里面获取

    连接池技术
    连接池技术有两种,一种是自定义连接池,但是这样使用过于繁琐,不可能在每个项目都都重新定义一个连接池。第二种是使用一些开源组件,常用的数据库连接池组件有:DBCP组件和C3P0组件,下面分别讲解

    2.自定义连接池
    自定义连接池程序思路:
    1.指定“初始化连接数目”(假设为3),在app启动的时候,就自行创建
    2.指定“最大连接数”(假设为6)
    3.指定“当前使用连接个数”(不能超出最大连接数)

    代码逻辑:
    1.dbPool.java连接池类
    2.指定全局参数:初始化数目,最大连接数目,当前连接,连接池集合
    3.构造函数:循环创建3个连接
    4.写一个创建连接的方法
    5.获取连接

    • 判断:池中有连接,直接拿,如果池中没有连接再判断是否达到最大连接数
    • 如果没有达到最大连接数,直接创建新连接,如果已经大多最大连接数,抛出异常
      6.释放连接:连接放回集合中或者连接池已满,可以直接释放掉

    代码实现:
    实现过程中如果直接使用realeaseConnection(con1);来释放是没有问题的,但是如果使用close()来释放,就需要再对这个方法使用代理方法来监控close方法,如果close方法被调用,则触发另外的命令
    如何对Connection对象,生成一个代理对象:
    |--Proxy

    static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h );
    ClassLoader loader, 当前使用的类加载器
    Class<?>[] interfaces, 目标对象(Connection)实现的接口类型
    InvocationHandler h 事件处理器:当执行上面接口中的方法的时候,就会自动触发事件处理器代码,把当前执行的方法(method)作为参数传入。

    package db;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    import java.util.LinkedList;
    
    /**
     * 自定义连接池,管理连接池
     * Created by cenyu on 16-12-18.
     */
    public class dbPool {
        //初始化变量
        private int init_count = 3;//初始化连接池数目
        private  int max_count = 6;//初始化最大连接数
        private int current_count = 0;//记录当前使用连接数
        //连接池(存放所有的初始化连接)
        private LinkedList<Connection> pool = new LinkedList<Connection>();
    
        //1.构造函数中,初始化连接放入连接池
        public dbPool(){
            //初始化连接
            for (int i = 0; i < init_count; i++) {
                //记录当前连接数据
                current_count++;
                //创建原始的连接对象
                Connection con = createConnection();
                //把连接加入连接池
                pool.addLast(con);
            }
        }
    
        //2.创建一个新的连接的方法
        private Connection createConnection() {
            try {
                Class.forName("com.mysql.jdbc.Driver");
                //原始目标对象
                final Connection con =DriverManager.getConnection("jdbc:mysql://db", "root", "root");
    
    
                /**********对con对象代理**************/
    
                // 对con创建其代理对象
                Connection proxy = (Connection) Proxy.newProxyInstance(
    
                        con.getClass().getClassLoader(),    // 类加载器
                        //con.getClass().getInterfaces(),   // 当目标对象是一个具体的类的时候
                        new Class[]{Connection.class},      // 目标对象实现的接口
    
                        new InvocationHandler() {			// 当调用con对象方法的时候, 自动触发事务处理器
                            @Override
                            public Object invoke(Object proxy, Method method, Object[] args)
                                    throws Throwable {
                                // 方法返回值
                                Object result = null;
                                // 当前执行的方法的方法名
                                String methodName = method.getName();
    
                                // 判断当执行了close方法的时候,把连接放入连接池
                                if ("close".equals(methodName)) {
                                    System.out.println("begin:当前执行close方法开始!");
                                    // 连接放入连接池
                                    pool.addLast(con);
                                    System.out.println("end: 当前连接已经放入连接池了!");
                                } else {
                                    // 调用目标对象方法
                                    result = method.invoke(con, args);
                                }
                                return result;
                            }
                        }
                );
                return proxy;
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
        //3. 获取连接
        public Connection getConnection(){
    
            // 3.1 判断连接池中是否有连接, 如果有连接,就直接从连接池取出
            if (pool.size() > 0){
                return pool.removeFirst();
            }
    
            // 3.2 连接池中没有连接: 判断,如果没有达到最大连接数,创建;
            if (current_count < max_count) {
                // 记录当前使用的连接数
                current_count++;
                // 创建连接
                return createConnection();
            }
    
            // 3.3 如果当前已经达到最大连接数,抛出异常
            throw new RuntimeException("当前连接已经达到最大连接数目 !");
        }
    
        //4. 释放连接
        public void realeaseConnection(Connection con) {
            // 4.1 判断: 池的数目如果小于初始化连接,就放入池中
            if (pool.size() < init_count){
                pool.addLast(con);
            } else {
                try {
                    // 4.2 关闭
                    current_count--;
                    con.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    
        public static void main(String[] args) throws SQLException {
            dbPool pool = new dbPool();
            System.out.println("当前连接: " + pool.current_count);  // 3
    
            // 使用连接
            pool.getConnection();
            pool.getConnection();
            Connection con4 = pool.getConnection();
            Connection con3 = pool.getConnection();
            Connection con2 = pool.getConnection();
            Connection con1 = pool.getConnection();
    
            // 释放连接, 连接放回连接池
    //		pool.realeaseConnection(con1);
    		/*
    		 * 希望:当关闭连接的时候,要把连接放入连接池!【当调用Connection接口的close方法时候,希望触发pool.addLast(con);操作】
    		 * 																			把连接放入连接池
    		 * 解决1:实现Connection接口,重写close方法
    		 * 解决2:动态代理
    		 */
            con1.close();
    
            // 再获取
            pool.getConnection();
    
            System.out.println("连接池:" + pool.pool.size());      // 0
            System.out.println("当前连接: " + pool.current_count);  // 3
        }
     }
    
    
    

    2.DBCP连接池

    开源数据库连接池
    在java的web服务器上,都提供了DataSoruce的,即连接池实现的,sun公司规定,如果是自己写的连接池,要实现javax.sql.DataSource接口
    目前使用最多的开源数据库连接池有:

    • DBCP 数据库连接池(tomcat)
    • C3P0 数据库连接池(hibernate)
      实际应用时不需要编写连接数据库代码,直接从数据源获得数据库的连接。程序员变成时也应尽量使用这些技术,以提升程序的数据库访问性能

    DBCP数据源
    DBCP是Apache软件基金组织下的开源连接池实现,使用DBCO数据源
    使用的时候需要导入两个jar文件

    • commons-dbcp.jar:连接池的实现
    • commons-pool.jar:连接池实现的依赖库

    Tomcat的连接池正是采用该连接池来实现的,该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用

    使用方法有两种,一种是硬编码,由于不好维护,所以不推荐,推荐使用第二种用配置文件法。
    第二种配置方式实现需要注意点是:配置文件中的key与BaseDataSouce中的属性一致。(去掉set然后第一个字母小写)
    代码示例:

    package dao;
    
    
    import org.apache.commons.dbcp.BasicDataSource;
    import org.apache.commons.dbcp.BasicDataSourceFactory;
    import org.junit.Test;
    
    import javax.sql.DataSource;
    import java.io.InputStream;
    import java.sql.Connection;
    import java.sql.SQLException;
    import java.util.Properties;
    
    /**
     * 测试DBCP数据源的使用方式
     * Created by cenyu on 16-12-18.
     */
    public class testDBCP {
    
        //1.硬编码方式实现连接池
        @Test
        public void test1() throws SQLException {
            //DBCP连接池核心类
            BasicDataSource dataSource = new BasicDataSource();
            //连接池参数配置:初始化参数,最大连接数/连接字符串,驱动,用户名,密码等
            dataSource.setUrl("jdbc:mysql://localhost:3306/db");
            dataSource.setDriverClassName("com.mysql.jdbc.Driver");
            dataSource.setUsername("root");
            dataSource.setPassword("root");
    
            dataSource.setInitialSize(3);//初始化连接数量
            dataSource.setMaxActive(6);//最大连接数量
            dataSource.setMaxIdle(3000);//最大空闲时间,超过这个时间就会释放
    
            //获取连接
            Connection con = dataSource.getConnection();
            con.prepareStatement("DELETE FROM Admin WHERE id =2").executeUpdate();
            //关闭
            con.close();
        }
    
    
        //2.【推荐使用】配置方式实现连接池,便于维护
        @Test
        public void test2() throws Exception {
            //加载prop配置文件
            Properties prop = new Properties();
            //获取文件流
            InputStream inputStream=testDBCP.class.getClassLoader().getResourceAsStream("db.properties");
            //加载属性配置文件
            prop.load(inputStream);
            //根据prop配置,直接创建数据源对象
            DataSource dataSource = BasicDataSourceFactory.createDataSource(prop);
            //获取连接
            Connection con = dataSource.getConnection();
            con.prepareStatement("DELETE FROM Admin WHERE id =4").executeUpdate();
            //关闭
            con.close();
        }
    }
    
    

    3.C3P0连接池

    C3P0连接池组件是最常用的连接池技术,Spring框架默认是支持C3P0连接池技术的。
    C3P0连接池的核心类:
    CombopooledDataSource ;

    使用方法:
    1.下载,引入jar文件:c3p0-0.9.1.2.jar
    2.使用连接池,创建连接

    • a.硬编码方法
    • b.配置方式(xml配置)
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    import org.junit.Test;
    
    import java.beans.PropertyVetoException;
    import java.sql.Connection;
    import java.sql.SQLException;
    import java.util.concurrent.ConcurrentNavigableMap;
    
    /**
     * 使用C3P0连接池技术的两种方法
     * Created by cenyu on 16-12-18.
     */
    public class testC3P0 {
    
        //1.硬编码方式,使用c3p0连接池管理连接
        @Test
        public void testCode() throws Exception {
            //创建连接池核心工具类
            ComboPooledDataSource dataSource=new ComboPooledDataSource();
            //设置连接参数:url、驱动、用户密码、初始连接数、最大连接数
            dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/db");
            dataSource.setDriverClass("com.mysql.jdbc.Driver");
            dataSource.setUser("root");
            dataSource.setPassword("root");
            dataSource.setInitialPoolSize(3);
            dataSource.setMaxPoolSize(6);
            dataSource.setMaxIdleTime(1000);
    
            //从连接池对象中,获取连接对象
            Connection conn = dataSource.getConnection();
            //执行跟新
            conn.prepareStatement("DELETE FROM Admin WHERE id=1").executeUpdate();
            //关闭
            conn.close();
        }
    
        //2.xml配置方式,使用c3p0连接池管理连接
        @Test
        public void testXML() throws Exception{
            //创建C3P0连接核心工具类
            //自动加载src下c3p0的配置文件【c3p0-config.xml】
            ComboPooledDataSource dataSource = new ComboPooledDataSource("mysql");//使用mysql的配置
            //获取连接
            Connection conn = dataSource.getConnection();
            //执行连接
            conn.prepareStatement("delete from Admin where id=3").executeUpdate();
            //关闭
            conn.close();
        }
    }
    
    
    

    c3p0-config.xml配置文件修改之后放在项目src的根目录下就可以了,编译之后会把这个文件编译到WEB-INF/classes下的
    常用的配置文件如下:

        <?xml version="1.0" encoding="UTF-8"?>  
        <c3p0-config>  
            <!-- This is default config! -->  
            <default-config>  
                <property name="initialPoolSize">10</property>  
                <property name="maxIdleTime">30</property>  
                <property name="maxPoolSize">100</property>  
                <property name="minPoolSize">10</property>  
                <property name="maxStatements">200</property>  
            </default-config>  
          
            <!-- This is my config for mysql-->  
            <named-config name="mysql">  
                <property name="driverClass">com.mysql.jdbc.Driver</property>  
                <property name="jdbcUrl">jdbc:mysql://localhost:3306/test</property>  
                <property name="user">root</property>  
                <property name="password"></property>  
                <property name="initialPoolSize">10</property>  
                <property name="maxIdleTime">30</property>  
                <property name="maxPoolSize">100</property>  
                <property name="minPoolSize">10</property>  
                <property name="maxStatements">200</property>  
            </named-config>  
              
              
            <!-- This is my config for oracle -->  
            <named-config name="oracle">  
                <property name="driverClass">oracle.jdbc.driver.OracleDriver</property>  
                <property name="jdbcUrl">jdbc:oracle:thin:@localhost:1521:orcl</property>  
                <property name="user">scott</property>  
                <property name="password">liang</property>  
                <property name="initialPoolSize">10</property>  
                <property name="maxIdleTime">30</property>  
                <property name="maxPoolSize">100</property>  
                <property name="minPoolSize">10</property>  
                <property name="maxStatements">200</property>  
            </named-config>  
        </c3p0-config>  
    
    
  • 相关阅读:
    UVA 11488 Hyper Prefix Sets (字典树)
    UVALive 3295 Counting Triangles
    POJ 2752 Seek the Name, Seek the Fame (KMP)
    UVA 11584 Partitioning by Palindromes (字符串区间dp)
    UVA 11100 The Trip, 2007 (贪心)
    JXNU暑期选拔赛
    计蒜客---N的-2进制表示
    计蒜客---线段的总长
    计蒜客---最大质因数
    JustOj 2009: P1016 (dp)
  • 原文地址:https://www.cnblogs.com/cenyu/p/6195467.html
Copyright © 2011-2022 走看看