zoukankan      html  css  js  c++  java
  • javaEE(12)_数据库连接池

    一、直接获取数据库连接和通过池获取示意图:

    二、编写数据库连接池

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

    •在DataSource构造函数中批量创建与数据库的连接,并把创建的连接加入LinkedList对象中.
    •实现getConnection方法,让getConnection方法每次调用时,从LinkedList中取一个Connection返回给用户.
    •当用户使用完Connection,调用Connection.close()方法时,Collection对象应保证将自己返回到LinkedList中,而不要把conn还给数据库.
    •Collection保证将自己返回到LinkedList中是此处编程的难点.

    2、通过DataSource来取代 DriverManager来获取Connection,像这样内存级别的操作相对于创建连接走公网来说速度快的多,相当于将时间转移到了服务器启动的时候,可以大幅度提高数据库的访问速度(一个请求最大的时间消耗在连接的建立上),通过DataSource获得的Connection都是已经被包裹过的(不是驱动原来的连接),他的close方法已经被修改.我们的程序只和DataSource打交道,不会直接访问连接池.

    3、装饰模式实现连接池代码如下:

    //自己实现连接池
    public class JdbcPool implements DataSource {
    
        private static LinkedList<Connection> list = new LinkedList<Connection>();
        private static Properties config = new Properties();
        static{
            try {
                config.load(JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("db.properties"));
                
                Class.forName(config.getProperty("driver"));
                for(int i=0;i<10;i++){
                    Connection conn = DriverManager.getConnection(config.getProperty("url"), 
                            config.getProperty("username"), config.getProperty("password"));
                    list.add(conn);
                }
            } catch (Exception e) {
                throw new ExceptionInInitializerError(e);
            }
        }
        
        //conn.close(),不满足需求,为了保持用户关闭连接的习惯
        /* 在实际开发,发现对象的方法满足不了开发需求时,有三种方式对其进行增强
         * 1.写一个connecton子类,覆盖close方法,增强close方法
         * 2.用包装设计模式,MyConnection也可以叫代理
         * 3.用动态代理   aop  面向切面编程
         */
        //要加锁,否则多线程情况下不同请求会拿到相同连接
        public synchronized Connection getConnection() throws SQLException {
            
            if(list.size()<=0){
                throw new RuntimeException("数据库忙,请稍会再来!!");
            }
            Connection conn = list.removeFirst();   //list.get()不行
            MyConnection my = new MyConnection(conn);
            return my; 
        }
        
        //1.定义一个类,实现与被增强相同的接口
        //2.在类中定义一个变量,记住被增强对象
        //3.定义一个构造函数,接收被增强对象
        //4.覆盖想增强的方法
        //5.对于不想增强的方法,直接调用目标对象(被增强对象)的方法
        class MyConnection implements Connection{
            private Connection conn;
            public MyConnection(Connection conn){
                this.conn = conn;
            }
            public void close(){
                list.add(this.conn);
            }
            ....
        }
        
       public static void release(Connection conn,Statement st,ResultSet rs){
            if(rs!=null){
                try{
                    rs.close();   
                }catch (Exception e) {
                    e.printStackTrace();
                }
                rs = null;
            }
            if(st!=null){
                try{
                    st.close();
                }catch (Exception e) {
                    e.printStackTrace();
                }
                st = null;
            }
            if(conn!=null){
                try{
                    conn.close();
                }catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    4、使用动态代理模式实现连接池代码,将getConnection()改为如下:

    public synchronized Connection getConnection() throws SQLException {
        
        if(list.size()<=0){
            throw new RuntimeException("数据库忙,请稍会再来!!");
        }
        Connection conn = list.removeFirst();   //list.get()不行
        return new MyConnectionHandler(conn,this).getWarpConn();
    }
    class MyConnectionHandler implements InvocationHandler {
        private Connection realConnection;
        private Connection warpedConnection;
        private MyDataSource dataSource;
    
        private int maxUseCount = 5;
        private int currentUserCount = 0;
    
        MyConnectionHandler(Connection conn,MyDataSource dataSource) {
            this.realConnection=conn;
            this.dataSource = dataSource;
        }
    
        Connection getWarpConn() {
            warpedConnection = (Connection) Proxy.newProxyInstance(this
                    .getClass().getClassLoader(), new Class[] { Connection.class },this);
            return warpedConnection;
        }
    
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            if ("close".equals(method.getName())) {
                currentUserCount++;
                if (currentUserCount < maxUseCount)
                    dataSource.connectionsPool.addLast(warpedConnection);
                else {
                    realConnection.close();
                    dataSource.currentCount--;
                }
            }
            return method.invoke(realConnection, args);
        }
    }

    三、开源数据库连接池

    1、现在很多WEB服务器(Weblogic, WebSphere, Tomcat)都提供了DataSoruce的实现,即连接池的实现.通常我们把DataSource的实现,按其英文含义称之为数据源,数据源中都包含了数据库连接池的实现.
    2、也有一些开源组织提供了数据源的独立实现:
    •DBCP 数据库连接池
    •C3P0 数据库连接池
    3、实际应用时不需要编写连接数据库代码,直接从数据源获得数据库的连接.程序员编程时也应尽量使用这些数据源的实现,以提升程序的数据库访问性能.

    DBCP数据源&C3P0数据源

    1、DBCP 是 Apache 软件基金组织下的开源连接池实现,使用DBCP数据源,应用程序应在系统中增加如下两个 jar 文件:
    •Commons-dbcp.jar:连接池的实现
    •Commons-pool.jar:连接池实现的依赖库
    Tomcat 的连接池正是采用该连接池来实现的.该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用.
    2、使用DBCP数据源后,Jdbcutils改为如下:
    public class JdbcUtils_DBCP {
        
        private static DataSource ds = null;
        static{
            try{
                InputStream in = JdbcUtils_DBCP.class.getClassLoader().
    getResourceAsStream("dbcpconfig.properties"); Properties prop = new Properties(); prop.load(in); BasicDataSourceFactory factory = new BasicDataSourceFactory(); ds = factory.createDataSource(prop); }catch (Exception e) { throw new ExceptionInInitializerError(e); } } public static Connection getConnection() throws SQLException{ return ds.getConnection(); } public static void release(Connection conn,Statement st,ResultSet rs){ if(rs!=null){ try{ rs.close(); }catch (Exception e) { e.printStackTrace(); } rs = null; } if(st!=null){ try{ st.close(); }catch (Exception e) { e.printStackTrace(); } st = null; } if(conn!=null){ try{ conn.close(); }catch (Exception e) { e.printStackTrace(); } } } }
    #连接设置
    driverClassName=com.mysql.jdbc.Driver
    url=jdbc:mysql://localhost:3306/jdbc
    username=root
    password=
    
    #<!-- 初始化连接 -->
    initialSize=10
    
    #最大连接数量
    maxActive=50
    
    #<!-- 最大空闲连接 -->
    maxIdle=20
    
    #<!-- 最小空闲连接 -->
    minIdle=5
    
    #<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
    maxWait=60000
    
    #JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;] 
    #注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们.
    connectionProperties=useUnicode=true;characterEncoding=gbk
    
    #指定由连接池所创建的连接的自动提交(auto-commit)状态.
    defaultAutoCommit=true
    
    #driver default 指定由连接池所创建的连接的只读(read-only)状态.
    #如果没有设置该值,则"setReadOnly"方法将不被调用.(某些驱动并不支持只读模式,如:Informix)
    defaultReadOnly=
    
    #driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation).
    #可用值为下列之一:(详情可见javadoc.)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
    defaultTransactionIsolation=READ_UNCOMMITTED

    3、使用c3p0数据源后,Jdbcutils改为如下:

    public class JdbcUtils_C3P0 {
        
        private static ComboPooledDataSource ds = null;
        static{
            try{
                ds = new ComboPooledDataSource("mysql");可通过名称获取那个配置
            }catch (Exception e) {
                throw new ExceptionInInitializerError(e);
            }
        }
    
        public static Connection getConnection() throws SQLException{
            return ds.getConnection();
        }
        
        public static void release(Connection conn,Statement st,ResultSet rs){
            if(rs!=null){
                try{
                    rs.close();   
                }catch (Exception e) {
                    e.printStackTrace();
                }
                rs = null;
            }
            if(st!=null){
                try{
                    st.close();
                }catch (Exception e) {
                    e.printStackTrace();
                }
                st = null;
            }
            if(conn!=null){
                try{
                    conn.close();
                }catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }    

    c3p0-config.xml配置如下,必须是这个名称,服务器会自动搜索.

    <c3p0-config>
        <default-config>
            <property name="driverClass">com.mysql.jdbc.Driver</property>
            <property name="jdbcUrl">jdbc:mysql://localhost:3306/day16</property>
            <property name="user">root</property>
            <property name="password">root</property>
            
            <property name="initialPoolSize">10</property>
            <property name="maxIdleTime">30</property>
            <property name="maxPoolSize">20</property>
            <property name="minPoolSize">5</property>
            <property name="maxStatements">200</property>
        </default-config>
        
        <named-config name="mysql">
            <property name="acquireIncrement">50</property>
            <property name="initialPoolSize">100</property>
            <property name="minPoolSize">50</property>
            <property name="maxPoolSize">1000</property>
            <property name="maxStatements">0</property>
            <property name="maxStatementsPerConnection">5</property>
        </named-config>
        
        <named-config name="oracle">
            <property name="acquireIncrement">50</property>
            <property name="initialPoolSize">100</property>
            <property name="minPoolSize">50</property>
            <property name="maxPoolSize">1000</property>
            <property name="maxStatements">0</property>
            <property name="maxStatementsPerConnection">5</property>
        </named-config>
    </c3p0-config>

    4、配置Tomcat数据源(tomcat自带有连接池),查看Tomcat文档,示例代码:

    <Context>
         <Resource name="jdbc/EmployeeDB" auth="Container"
                type="javax.sql.DataSource"
                username="root"
                password="root"
                driverClassName="com.mysql.jdbc.Driver"
                url="jdbc:mysql://localhost:3306/day16"
                initialSize="10"
                maxActive="30"
                maxIdle="4"/>
    </Context>
    public class JdbcUtils_Tomcat {
        private static DataSource ds;
        static {
            try {
                Context initCtx = new InitialContext();
                Context envCtx = (Context) initCtx.lookup("java:comp/env");
                ds = (DataSource) envCtx.lookup("jdbc/EmployeeDB");
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        public static Connection getConnection() throws SQLException{
            return ds.getConnection();
        }
    }

    ps:此种配置可以在tomcat server.xml文件中或者是MATA-INF下新建context.xml文件都可以(可以参考apache提供的文档),其实这种用法是将datasource配置(实际就是一个对象)放在了JNDI容器中,这种情况下驱动的jar文件需放置在tomcat的lib下.

    四、JNDI技术简介

    1.JNDI(Java Naming and Directory Interface),Java命名和目录接口,它对应于J2SE中的javax.naming包,这套API的主要作用在于:它可以把Java对象放在一个容器中(JNDI容器),并为容器中的java对象取一个名称,以后程序想获得Java对象,只需通过名称检索即可.
    2、其核心API为Context,它代表JNDI容器,其lookup方法为检索容器中对应名称的对象.
    ps:举个例子,Servlet中要用到request对象,可以将它作为方法参数传递进去,也可以将它放在JNDI容器中.
    3、JNDI容器图示:

    五、各种连接池性能比较

    测试执行申请归还连接1,000,000次总耗时性能对比.

    Java6 Environment

    OS OS X 10.8.2
    CPU intel i7 2GHz 4 core
    JVM java version "1.6.0_37"

    Java6 Benchmark Result

    Jdbc Connection Pool 1 thread 2 threads 5 threads 10 threads 20 threads 50 threads
    Druid 1,102 1,509 1,889 1,904 2,027 1,977
    tomcat-jdbc 1,399 1,378 2,257 2,289 2,305 2,503
    DBCP 3,144 3,834 6,276 6,408 6,563 6,783
    BoneCP 4,327 3,598 3,800 5,242 9,402 19,066
    jboss-datasource 4,912 3,049 6,868 6,512 40,146 43,748
    C3P0 18,570 19,467 15,270 19,294 28,195 66,677
    Proxool 16,221 14,455 24,688 38,905 48,087(Exception) 58,238(Exception)

     Java7 Environment

    OS OS X 10.8.2
    CPU intel i7 2GHz 4 core
    JVM java version "1.7.0_05"

    Java7 Benchmark Result

    Jdbc Connection Pool 1 thread 2 threads 5 threads 10 threads 20 threads 50 threads
    Druid 898 1,191 1,324 1,362 1,325 1,459
    tomcat-jdbc 1,269 1,378 2,029 2,103 1,879 2,025
    DBCP 2,324 5,055 5,446 5,471 5,524 5,415
    BoneCP 3,738 3,150 3,194 5,681 11,018 23,125
    jboss-datasource 4,377 2,988 3,680 3,980 32,708 37,742
    C3P0 10,841 13,637 10,682 11,055 14,497 20,351
    Proxool 16,337 16,187 18,310(Exception) 25,945 33,706(Exception) 39,501 (Exception)

    结论

    1. Druid是性能最好的数据库连接池,tomcat-jdbc和druid性能接近.
    2. proxool在激烈并发时会抛异常,完全不靠谱.
    3. c3p0和proxool都相当慢,慢到影响sql执行效率的地步.
    4. bonecp性能并不优越,采用LinkedTransferQueue并没有能够获得性能提升.
    5. 除了bonecp,其他的在JDK 7上跑得比JDK 6上快
    6. jboss-datasource虽然稳定,但是性能很糟糕
  • 相关阅读:
    [转]jQuery知识总结
    sqlserver2008 函数1
    使用触发器生成流水号
    日期格式
    数据库正在使用,删除不了的问题
    continue 语句
    逻辑语句和函数
    ASP.NET中的随机密码生成
    相对路径
    “基类包括字段,但其类型与控件的类型不兼容”的解决方法
  • 原文地址:https://www.cnblogs.com/wangweiNB/p/5064172.html
Copyright © 2011-2022 走看看