Java 之 JDBC:(八)数据库连接池

一、JDBC 数据库连接池的必要性

  在使用开发基于数据库的 Web 程序时,传统的模式基本是按以下步骤:

    ① 在主程序(如servlet、beans)中建立数据库连接;

    ② 进行SQL 操作;

    ③ 断开数据库连接;

  这种模式开发,存在的问题:

    普通的 JDBC 数据库连接使用 DriverManager 来获取,每次向数据库建立连接的时候都要将 Connection 加载到内存中,再验证用户名和密码(需要花费0.05~1s的时间)。需要数据库连接的时候,就向数据库要求一个,执行完成后再断开连接。这样的方式将会消耗大量的资源和时间。数据库的连接资源并没有得到很好的重复利用。若同时有几百人甚至几千人在线,频繁的进行数据库连接操作将占用很多的系统资源,严重的甚至会造成服务器的崩溃。

    对于每一次数据库连接,使用完后都得断开。否则,如果程序出现异常而未能关闭,将会导致数据库系统中的内存泄漏,最终将导致重启数据库。(Java 的内存泄漏)

    这种开不能控制被创建的连接对象数,系统资源会被毫无顾忌的分配出去,如连接过多,也可能导致内存泄漏,服务器崩溃。

二、数据库连接池技术

    为了解决传统开发中的数据库连接问题,可以采用数据库连接池技术。

    数据库连接池的基本思想:就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。

    数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个

    数据库连接池在初始化时创建一定数量的数据库连接放到连接池中,这些数据库连接的数量是由最小数据库连接数来设定的。无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量。连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中。

    

     工作原理

      

     数据库连接池技术的优点:

    (1)资源重用

      由于数据库连接得以重用,避免了频繁创建,释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增加了系统运行环境的平稳性。

    (2)更快的系统反应速度

      数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于连接池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用链接,避免了数据库连接初始化和释放过程的时间开销,从而减少了系统的响应时间。

    (3)新的资源分配手段

      对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接池的配置,实现某一应用最大可用数据库连接数的限制,避免某一应用独占所有的数据库资源。

    (4)统一的连接管理,避免数据库连接泄漏

      在较为完善的数据库连接池实现中,可根据预先的占用超时设定,强制回收被占用连接,从而避免了常规数据库操作中可能出现的资源泄漏;

    数据库连接池:

      标准接口:javax.sql 包下的 DataSource

      常用方法:

获取连接:getConnection()
归还连接:Connection.close()

       Connection 是从连接池中获取的,那么调用 Connection.close() 方法,则不会再关闭连接了,而是归还连接。

      对于 DataSource 接口的实现,一般不需要我们去实现,由不同的数据库厂商来实现:

        ① C3P0:数据库连接池技术;

        ② Druid:数据库连接池技术,由阿里巴巴提供;

 

三、多种开源的数据库连接池

  JDBC 的数据库连接池使用 javax.sql.DataSource 来表示,DataSource 只是一个接口,该接口通用由无服务器(Weblogic,WebSphere,Tomcat)提供实现,也有一些开源组织提供实现:

    ① DBCP 是 Apache 提供的数据库连接池。Tomcat 服务器自带 dbcp 数据库连接池。速度相对 c3p0 较快,但因自身存在BUG,Hibernate3已不再提供支持;

    ② C3P0 是一个开源组织提供的一个数据库连接池,速度相对较慢,稳定性还可以。Hibernate 官方推荐使用;

    ③ Proxool 是 sourceforge 下的一个开源项目数据库连接池,有监控连接池状态的功能,稳定性较 c3p0 差一点

    ④ BoneCP 是一个开源组织提供的数据库连接池,速度快;

    ⑤ Druid 是阿里提供的数据库连接池,据说是集 DBCP、C3P0、Proxool 优点与一身的数据库连接池,但是速度不确定是否有 BoneCP 快;

  DataSource 通常被称为数据源,它包含连接池和连接池管理两个部分,习惯上也经常把 DataSource 称为连接池;

  DataSource 用来取代 DriverManager 来获取 Connection,获取速度快,同时可以大幅度提高数据库访问速度。

  特别注意:

    ① 数据源和数据库连接不同,数据源无需创建多个,它是产生数据库连接的工厂,因此整个应用只需要一个数据源即可。

    ② 当数据库访问结束后,程序还是像以前一样关闭数据库连接:conn.close();但 conn.close() 并没有关闭数据库的物理连接,它仅仅把数据库连接释放,归还给了数据库连接池。  

四、C3P0 数据库连接池

  1、C3P0

    C3P0:是一个开放源代码的JDBC连接池,它在lib目录中与Hibernate一起发布,包括了实现jdbc3和jdbc2扩展规范说明的Connection 和Statement 池的DataSources 对象。C3p0拥有比DBCP更丰富的配置属性。

  2、获取连接

    在获取连接之前,需要导入对应的 jar 包。

    

    获取连接方式一:使用数据库连接池方式

 1     //使用C3P0数据库连接池的方式,获取数据库的连接:不推荐
 2     @Test
 3     public void testGetConnection() throws Exception {
 4         //获取c3p0数据库连接池
 5         ComboPooledDataSource cpds = new ComboPooledDataSource();
 6         cpds.setDriverClass("com.mysql.jdbc.Driver");
 7         cpds.setJdbcUrl("jdbc:mysql://localhost:3306/test" );
 8         cpds.setUser("root");
 9         cpds.setPassword("root");
10 
11         //通过设置相关的参数,对数据库连接池进行管理:
12         //设置初始时数据库连接池中的连接数
13         cpds.setInitialPoolSize(10);
14 
15         Connection conn = cpds.getConnection();
16         System.out.println(conn);
17 
18         //销毁c3p0数据库连接池
19         //DataSources.destroy( cpds );
20 
21     }

    获取连接方式二:使用配置文件方式

1     //方式二:使用配置文件
2     @Test
3     public void testGetConnection2() throws Exception {
4         ComboPooledDataSource cpds = new ComboPooledDataSource("hellc3p0");
5         Connection conn = cpds.getConnection();
6         System.out.println(conn);
7     }

    其中,src 下的配置文件为:【c3p0-config.xml】(必须是这个名字)

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <c3p0-config>
 3 
 4     <named-config name="hellc3p0">
 5         <!-- 提供获取连接的4个基本信息 -->
 6         <property name="driverClass">com.mysql.jdbc.Driver</property>
 7         <property name="jdbcUrl">jdbc:mysql:///test</property>
 8         <property name="user">root</property>
 9         <property name="password">root</property>
10         
11         <!-- 进行数据库连接池管理的基本信息 -->
12         <!-- 当数据库连接池中的连接数不够时,c3p0一次性向数据库服务器申请的连接数 -->
13         <property name="acquireIncrement">5</property>
14         <!-- c3p0数据库连接池中初始化时的连接数 -->
15         <property name="initialPoolSize">10</property>
16         <!-- c3p0数据库连接池维护的最少连接数 -->
17         <property name="minPoolSize">10</property>
18         <!-- c3p0数据库连接池维护的最多的连接数 -->
19         <property name="maxPoolSize">100</property>
20         <!-- c3p0数据库连接池最多维护的Statement的个数 -->
21         <property name="maxStatements">50</property>
22         <!-- 每个连接中可以最多使用的Statement的个数 -->
23         <property name="maxStatementsPerConnection">2</property>
24 
25     </named-config>
26 </c3p0-config>

     可以在配置文件中设置默认的连接池对象:

 1 <c3p0-config>
 2   <!-- 使用默认的配置读取连接池对象 -->
 3   <default-config>
 4       <!--  连接参数 -->
 5     <property name="driverClass">com.mysql.jdbc.Driver</property>
 6     <property name="jdbcUrl">jdbc:mysql://localhost:3306/bookstore</property>
 7     <property name="user">root</property>
 8     <property name="password">root</property>
 9     
10     <!-- 连接池参数 -->
11     <!--初始化连接的数量-->
12     <property name="initialPoolSize">5</property>
13     <!--最大的连接数量-->
14     <property name="maxPoolSize">10</property>
15     <!--超时时间-->
16     <property name="checkoutTimeout">3000</property>
17   </default-config>
18 
19   <!--通过指定的名字来获取连接的数据库-->
20   <named-config name="otherc3p0"> 
21     <!--  连接参数 -->
22     <property name="driverClass">com.mysql.jdbc.Driver</property>
23     <property name="jdbcUrl">jdbc:mysql://localhost:3306/bookstore</property>
24     <property name="user">root</property>
25     <property name="password">root</property>
26     
27     <!-- 连接池参数 -->
28     <property name="initialPoolSize">5</property>
29     <property name="maxPoolSize">8</property>
30     <property name="checkoutTimeout">1000</property>
31   </named-config>
32 </c3p0-config>

    注意:在配置文件中可以连接多个不同的数据库,用<name-config> 声明即可,到时候使用 name 属性来调用即可,如果没有指定 name,那么调用默认的数据库。

    案例:

 1   public static void main(String[] args) throws SQLException {
 2         // 创建数据库连接对象
 3         //DataSource ds = new ComboPooledDataSource("");        //连接默认的数据库
 4         DataSource ds = new ComboPooledDataSource("otherc3p0"); //连接 name=otherc3p0数据库
 5 
 6         //2 获取连接
 7         Connection conn = ds.getConnection();
 8 
 9         Statement stmt = conn.createStatement();
10 
11         // 3.归还连接
12         conn.close();
13 
14     }

    封装获取连接工具类

 1     /**
 2      * 
 3      * @Description 使用C3P0的数据库连接池技术
 4      * @return
 5      * @throws SQLException
 6      */
 7     //数据库连接池只需提供一个即可。
 8     private static ComboPooledDataSource cpds = new ComboPooledDataSource("hellc3p0");
 9     public static Connection getConnection1() throws SQLException{
10         Connection conn = cpds.getConnection();
11         
12         return conn;
13     }

    注意:这里只需要一个连接池就可以了,然后不断从连接池中获取连接。

五、DBCP数据库连接池

  1、概述

    DBCP Apache 软件基金组织下的开源连接池实现,该连接池依赖该组织下的另一个开源系统:Commonpool。如需使用该连接池实现,应在系统中增加如下两个 jar 文件:

      Commons-dbcp.jar:连接池的实现

      Commons-pool.jar:连接池实现的依赖库

      

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

    数据源和数据库连接不同,数据源无需创建多个,它是产生数据库连接的工厂,因此整个应用只需要一个数据源即可。

    当数据库访问结束后,程序还是像以前一样关闭数据库连接:conn.close(); 但上面的代码并没有关闭数据库的物理连接,它仅仅把数据库连接释放,归还给了数据库连接池。

    配置属性说明:

属性 默认
说明
initialSize 0 连接池启动时创建的初始化连接数量
maxActive 8 连接池中可同时连接的最大的连接数
maxIdle 8 连接池中最大的空闲的连接数,超过的空闲连接将被释放,如果
设置为负数表示不限制
minIdle 0 连接池中最小的空闲的连接数,低于这个数量会被创建新的连
接。该参数越接近maxIdle,性能越好,因为连接的创建和销
毁,都是需要消耗资源的;但是不能太大。
maxWait 无限
最大等待时间,当没有可用连接时,连接池等待连接释放的最大
时间,超过该时间限制会抛出异常,如果设置-1表示无限等待
poolPreparedStatements false 开启池的Statement是否prepared
maxOpenPreparedStatements 无限
开启池的prepared 后的同时最大连接数
minEvictableIdleTimeMillis 连接池中连接,在时间段内一直空闲, 被逐出连接池的时间
removeAbandonedTimeout 300 超过时间限制,回收没有用(废弃)的连接
removeAbandoned false 超过removeAbandonedTimeout时间后,是否进 行没用连接
(废弃)的回收

  2、获取连接

    获取连接方式一:

 1     //测试DBCP的数据库连接池技术
 2     @Test
 3     public void testGetConnection() throws SQLException {
 4         //创建了DBCP的数据库连接池
 5         BasicDataSource source = new BasicDataSource();
 6 
 7         //设置基本信息
 8         source.setDriverClassName("com.mysql.jdbc.Driver");
 9         source.setUrl("jdbc:mysql:///test");
10         source.setUsername("root");
11         source.setPassword("root");
12 
13         //还可以设置其他涉及数据库连接池管理的相关属性:
14         source.setInitialSize(10);
15         source.setMaxActive(10);
16         //。。。
17 
18         Connection conn = source.getConnection();
19         System.out.println(conn);
20     }

    获取连接方式二:

 1     //使用dbcp数据库连接池的配置文件方式,获取数据库的连接:推荐
 2     @Test
 3     public void testGetConnection1() throws Exception{
 4         Properties pros = new Properties();
 5 
 6         //方式1:
 7         //InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("dbcp.properties");
 8         //方式2:
 9         FileInputStream is = new FileInputStream(new File("src/dbcp.properties"));
10 
11 
12         pros.load(is);
13         //根据提供的BasicDataSourceFactory创建对应的DataSource对象
14         DataSource source = BasicDataSourceFactory.createDataSource(pros);
15 
16         Connection conn = source.getConnection();
17         System.out.println(conn);
18     }

    其中,src 下的配置文件为 【dbcp.properties】

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///test
#url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true&useServerPrepStmts=false
username=root
password=root

initialSize=10
#...

    封装连接工具类:

 1    /**
 2      * 
 3      * @Description 使用DBCP数据库连接池技术获取数据库连接
 4      * @return
 5      * @throws Exception
 6      */
 7     //创建一个DBCP数据库连接池
 8     private static DataSource source;
 9     static{
10         try {
11             Properties pros = new Properties();
12             FileInputStream is = new FileInputStream(new File("src/dbcp.properties"));
13             pros.load(is);
14             source = BasicDataSourceFactory.createDataSource(pros);
15         } catch (Exception e) {
16             e.printStackTrace();
17         }
18     }
19     public static Connection getConnection2() throws Exception{
20         
21         Connection conn = source.getConnection();
22         
23         return conn;
24     }

六、Druid(德鲁伊)数据库连接池

  1、概述

    Druid是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0DBCPProxoolDB池的优点,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况,可以说是针对监控而生的DB连接池,可以说是目前最好的连接池之一

    使用步骤

      (1)导入 jar 包:

        

      (2)定义配置文件

          名称:是properties形式的,需要手动加载;

          路径:可以放在任意目录下(建议放在 src 目录下)

      (3)加载配置文件 properties

      (4)获取数据连接池对象:通过工厂来获取 DruidDataSourceFactory

      (5)获取连接:getConnection

      (6)归还连接:close()     

  2、获取连接

    获取数据库连接:

 1     @Test
 2     public void getConnection() throws Exception{
 3         Properties pros = new Properties();
 4 
 5         InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");
 6 
 7         pros.load(is);
 8 
 9         DataSource source = DruidDataSourceFactory.createDataSource(pros);
10         Connection conn = source.getConnection();
11         System.out.println(conn);
12         conn.close(); //归还连接
13     }

     其中,src 下的配置文件为:【druid.properties】

#url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
url=jdbc:mysql:///test
username=root
password=root
driverClassName=com.mysql.jdbc.Driver

initialSize=10  #初始化连接数量
maxActive=10    #最大连接数
maxWait=1000    #延迟时间
filters=wall

     封装数据库连接工具类:

 1     /**
 2      * 使用Druid数据库连接池技术
 3      */
 4     private static DataSource source;
 5     static{
 6         try {
 7             Properties pros = new Properties();
 8             
 9             InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");
10             
11             pros.load(is);
12             
13             source = DruidDataSourceFactory.createDataSource(pros);
14         } catch (Exception e) {
15             e.printStackTrace();
16         }
17     }
18     public static Connection getConnection3() throws SQLException{
19         
20         Connection conn = source1.getConnection();
21         return conn;
22     }    

  3、详细配置参数

配置缺省说明
name   配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。 如果没有配置,将会生成一个名字,格式是:”DataSource-” + System.identityHashCode(this)
url   连接数据库的url,不同数据库不一样。例如:mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto
username   连接数据库的用户名
password   连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter。详细看这里:https://github.com/alibaba/druid/wiki/%E4%BD%BF%E7%94%A8ConfigFilter
driverClassName   根据url自动识别 这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName(建议配置下)
initialSize 0 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
maxActive 8 最大连接池数量
maxIdle 8 已经不再使用,配置了也没效果
minIdle   最小连接池数量
maxWait   获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
poolPreparedStatements false 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
maxOpenPreparedStatements -1 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
validationQuery   用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。
testOnBorrow true 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
testOnReturn false 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
testWhileIdle false 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
timeBetweenEvictionRunsMillis   有两个含义: 1)Destroy线程会检测连接的间隔时间2)testWhileIdle的判断依据,详细看testWhileIdle属性的说明
numTestsPerEvictionRun   不再使用,一个DruidDataSource只支持一个EvictionRun
minEvictableIdleTimeMillis    
connectionInitSqls   物理连接初始化的时候执行的sql
exceptionSorter   根据dbType自动识别 当数据库抛出一些不可恢复的异常时,抛弃连接
filters   属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
proxyFilters   类型是List,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系
原文地址:https://www.cnblogs.com/niujifei/p/15025233.html