数据库连接池C3P0,DBCP教程详解示例

连接池

实际开发中“获得连接”或“释放资源”是非常消耗系统资源的两个过程,为了解决此类性能问题,通常情况我们采用连接池技术,来共享连接Connection。这样我们就不需要每次都创建连接、释放连接了,这些操作都交给了连接池

1.1 连接池概述

l 概念

用池来管理Connection,这样可以重复使用Connection。有了池,所以我们就不用自己来创建Connection,而是通过池来获取Connection对象。当使用完Connection后,调用Connection的close()方法也不会真的关闭Connection,而是把Connection“归还”给池。池就可以再利用这个Connection对象了。

l 规范

Java为数据库连接池提供了公共的接口:javax.sql.DataSource,各个厂商需要让自己的连接池实现这个接口。这样应用程序可以方便的切换不同厂商的连接池!

常见的连接池:DBCP、C3P0。

接下来,我们就详细的学习连接池。

1.2 C3P0连接池

C3P0开源免费的连接池!目前使用它的开源项目有:Spring、Hibernate等。使用第三方工具需要导入jar包,c3p0使用时还需要添加配置文件c3p0-config.xml

1.2.1 导入jar包

我们使用的0.9.2版本,需要导入2个jar包

1.2.2 核心类

@Test
public void demo01() throws Exception{
    //1 获得连接池(数据源)
    ComboPooledDataSource dataSource = new ComboPooledDataSource();
    //1.1 设置基本项
    dataSource.setDriverClass("com.mysql.jdbc.Driver");
 dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/webdb_4");
    dataSource.setUser("root");
    dataSource.setPassword("root");
    //1.2其他项
    // * 初始化连接池中连接的个数
    dataSource.setInitialPoolSize(5);
    // * 最小|最大连接池中连接的个数
    dataSource.setMinPoolSize(2);
    dataSource.setMaxPoolSize(10);
    // * 最大空闲数
    dataSource.setMaxIdleTime(60);
    // * 每次增长个数
    dataSource.setAcquireIncrement(2);
    //2获得连接
    Connectionconn = dataSource.getConnection();
    System.out.println(conn);
}

1.2.3 配置文件

  • 配置文件名称:c3p0-config.xml (固定)
  • 配置文件位置:src (类路径)
  • 配置文件内容:命名配置
<c3p0-config>
    <!-- 命名的配置 -->
    <named-configname="itheima">
        <!--连接数据库的4项基本参数 -->
 <propertyname="driverClass">com.mysql.jdbc.Driver</property>
   <propertyname="jdbcUrl">jdbc:mysql://127.0.0.1:3306/webdb</property>
        <propertyname="user">root</property>
        <propertyname="password">root</property>
        <!--如果池中数据连接不够时一次增长多少个-->
        <propertyname="acquireIncrement">5</property>
        <!--初始化连接数 -->
        <propertyname="initialPoolSize">20</property>
        <!--最小连接受 -->
        <propertyname="minPoolSize">10</property>
        <!--最大连接数 -->
        <propertyname="maxPoolSize">40</property>
        <!---JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量 -->
        <propertyname="maxStatements">0</property>
        <!--连接池内单个连接所拥有的最大缓存statements数 -->
<propertyname="maxStatementsPerConnection">5</property>
    </named-config>
</c3p0-config>
  • 配置文件内容:默认配置
<c3p0-config>
    <!-- 默认配置,如果没有指定则使用这个配置 -->
    <default-config>
      <propertyname="driverClass">com.mysql.jdbc.Driver</property>
      <propertyname="jdbcUrl">jdbc:mysql://127.0.0.1:3306/webdb</property>
        <propertyname="user">root</property>
        <propertyname="password">root</property>
        <propertyname="checkoutTimeout">30000</property>
       <propertyname="idleConnectionTestPeriod">30</property>
        <propertyname="initialPoolSize">10</property>
        <propertyname="maxIdleTime">30</property>
        <propertyname="maxPoolSize">100</property>
        <propertyname="minPoolSize">10</property>
        <propertyname="maxStatements">200</property>
        <user-overridesuser="test-user">
            <propertyname="maxPoolSize">10</property>
            <propertyname="minPoolSize">1</property>
            <propertyname="maxStatements">0</property>
        </user-overrides>
</default-config>

1.2.4 常见配置项

分类

属性

描述

必须项

user

用户名

password

密码

driverClass

驱动mysql驱动,com.mysql.jdbc.Driver

jdbcUrl

路径mysql路径,jdbc:mysql://localhost:3306/数据库

基本配置

acquireIncrement

连接池无空闲连接可用时,一次性创建的新连接数默认值:3

initialPoolSize

连接池初始化时创建的连接数默认值:3

maxPoolSize

连接池中拥有的最大连接数默认值:15

minPoolSize

连接池保持的最小连接数。

maxIdleTime

连接的最大空闲时间。如果超过这个时间,某个数据库连接还没有被使用,则会断开掉这个连接,如果为0,则永远不会断开连接。默认值:0

管理连接池的大小和连接的生存时间(扩展)

maxConnectionAge

配置连接的生存时间,超过这个时间的连接将由连接池自动断开丢弃掉。当然正在使用的连接不会马上断开,而是等待它close再断开。配置为0的时候则不会对连接的生存时间进行限制。默认值0

maxIdleTimeExcessConnections

这个配置主要是为了减轻连接池的负载,配置不为0,则会将连接池中的连接数量保持到minPoolSize,为0则不处理。

配置PreparedStatement缓存(扩展)

maxStatements

连接池为数据源缓存的PreparedStatement的总数。由于PreparedStatement属于单个Connection,所以这个数量应该根据应用中平均连接数乘以每个连接的平均PreparedStatement来计算。为0的时候不缓存,同时maxStatementsPerConnection的配置无效。

maxStatementsPerConnection

连接池为数据源单个Connection缓存的PreparedStatement数,这个配置比maxStatements更有意义,因为它缓存的服务对象是单个数据连接,如果设置的好,肯定是可以提高性能的。为0的时候不缓存。

1.2.5 编写工具类

C3P0提供核心工具类:ComboPooledDataSource,如果要使用连接池,必须创建该类的实例对象。

  • new ComboPooledDataSource(“名称”); 使用配置文件“命名配置” <named-config name="itcast">
  • new ComboPooledDataSource(); 使用配置文件“默认配置” <default-config>
public class C3P0Utils{
    //使用默认配置
//  privatestatic ComboPooledDataSource dataSource = new ComboPooledDataSource();
    //使用命名配置
    private static ComboPooledDataSource dataSource = new ComboPooledDataSource("itheima");
    /**
     * 获得数据源(连接池)
     * @return
     */
    public static DataSource getDataSource(){
        returndataSource;
    }
    /**
     * 获得连接
     * @return
     * @throws SQLException
     */
    public static Connection getConnection(){
        try {
            returndataSource.getConnection();
        }catch (Exception e) {
            thrownew RuntimeException(e);
        }
    }
} 

1.3 DBCP连接池

DBCP也是一个开源的连接池,是Apache Common成员之一,在企业开发中也比较常见,tomcat内置的连接池。

1.3.1 导入jar包

1.3.2 核心类

@Test
public void demo01() throws Exception{
    //1 获得连接池
    BasicDataSource dataSource = new BasicDataSource();
    //1.1 基本项
    dataSource.setDriverClassName("com.mysql.jdbc.Driver");
   dataSource.setUrl("jdbc:mysql://localhost:3306/webdb_4");
    dataSource.setUsername("root");
    dataSource.setPassword("root");
    //1.2 其他项
    // * 初始化连接池中连个的个数
    dataSource.setInitialSize(5);
    // * 最大活动数
    dataSource.setMaxActive(10);
    //2获得连接
    Connectionconn = dataSource.getConnection();
    System.out.println(conn);
 }

1.3.3 配置文件

  • 配置文件名称:*.properties
  • 配置文件位置:任意,建议src(classpath/类路径)
  • 配置文件内容:properties不能编写中文,不支持在STS中修改,必须使用记事本修改内容,否则中文注释就乱码了

#连接设置

driverClassName=com.mysql.jdbc.Driver

url=jdbc:mysql://localhost:3306/webdb

username=root

password=root

#<!-- 初始化连接 -->

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

1.3.4 常见配置项

分类

属性

描述

必须项

driverClassName

url

username

password

基本项

maxActive

最大连接数量

minIdle

最小空闲连接

maxIdle

最大空闲连接

initialSize

初始化连接

优化配置(扩展)

logAbandoned

连接被泄露时是否打印

removeAbandoned

是否自动回收超时连接

removeAbandonedTimeout

超时时间(以秒数为单位)

maxWait

超时等待时间以毫秒为单位 1000等于60秒

timeBetweenEvictionRunsMillis

在空闲连接回收器线程运行期间休眠的时间值,以毫秒为单位

numTestsPerEvictionRun

在每次空闲连接回收器线程(如果有)运行时检查的连接数量

minEvictableIdleTimeMillis

连接在池中保持空闲而不被空闲连接回收器线程

参考文档:http://commons.apache.org/proper/commons-dbcp/configuration.html

1.3.5 编写工具类

public class DBCPUtils{
    private static DataSource dataSource;
    static{
        try {
            //1加载配置文件,获得文件流
            InputStreamis =DBCPUtils.class.getClassLoader().getResourceAsStream("dbcp.properties");
            //2使用Properties处理配置文件
            Propertiesprops = new Properties();
            props.load(is);
            //3使用工具类创建连接池(数据源)
            dataSource= BasicDataSourceFactory.createDataSource(props);
        }catch (Exception e) {
            thrownew RuntimeException(e);
        }
    }
    public static DataSource getDataSource(){
        returndataSource;
    }
    public static Connection getConnection(){
        try {
            return  dataSource.getConnection();
        }catch (Exception e) {
            thrownew RuntimeException(e);

        }

    }

}

1.4 自定义连接池

1.4.1 案例分析

根据我们对连接池简单的理解,如果我们要编写自定义连接池,需要完成以下步骤

1.创建连接池实现(数据源),并实现接口 javax.sql.DataSource 。因为我们只使用该接口中getConnection()方法,简化本案例,我们将自己提供方法,而没有实现接口

2.提供一个集合,用于存放连接,因为移除/添加操作过多,所以选择LinkedList

3.本案例在静态代码块中,为连接池初始化3个连接。

4.之后程序如果需要连接,调用实现类的getConnection(),本方法将从连接池(容器List)获得连接。为了保证当前连接只能提供给一个线程使用,所以我们需要将连接先从连接池中移除。

5.当用户使用完连接,释放资源时,不执行close()方法,而是将连接添加到连接池中。

1.4.2 案例实现

1.4.2.1 提供容器及初始化

//#1 创建容器,用于存放连接Connection
private static LinkedList<Connection> pool =new LinkedList<Connection>();
//#1.1初始化连接池中的连接
static{
    try {
        //1 注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        for(int i = 0; i < 3; i++) {
            //2获得连接
            Connectionconn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day10_db","root", "1234");
            //3将连接添加到连接池中
            pool.add(conn);
        }
    } catch(Exception e) {
        thrownew RuntimeException(e);
    }
}

1.4.2.2 获得连接

/**
 * #2 获得连接,从连接池中获得连接
 * @return
 */
public static Connection getConnection(){
   returnpool.removeFirst();
}
1.4.2.3   归还连接
/**
 * #3 释放资源,当链接connection close时,归还给连接池
 * @paramconn
 * @param st
 * @param rs
 */
public static void release(Connection conn){
    try {
        if(conn != null) {
//          conn.close();   //不是真的关闭
            pool.add(conn); //将从连接池获得连接,归还给连接池
        }
    } catch(Exception e) {
    }
}

1.4.2.4 测试使用

为了体现连接池优势,我们将采用多线程并发访问,使同一个连接在不同的时段,被不同的线程使用。

public class TestCustomPool {
    public static void main(String[] args) {
        //1 获得连接
            Connectionconn = JdbcUtils.getConnection();
            System.out.println("使用:" + conn + " , " +Thread.currentThread()); 
            //2释放资源
            JdbcUtils.release(conn);
    }
}

1.5 自定义连接池:方法增强

1.5.1 需求

自定义连接池中存在严重问题,用户调用getConnection()获得连接后,必须使用release()方法进行连接的归还,如果用户调用 conn.close() 将连接真正的释放,连接池中将出现无连接可用。

此时我们希望,即使用户调用了close()方法,连接仍归还给连接池。close()方法原有功能时释放资源,期望功能:将当前连接归还连接池。说明close()方法没有我们希望的功能,我们将对close()方法进行增强,从而实现将连接归还给连接池的功能。

1.5.2 方法增强总结

1.继承,子类继承父类,将父类的方法进行复写,从而进行增强。

使用前提:必须有父类,且存在继承关系。

2.装饰者设计模式,此设计模式专门用于增强方法。

使用前提:必须有接口

缺点:需要将接口的所有方法都实现

3.动态代理:在运行时动态的创建代理类,完成增强操作。与装饰者相似

使用前提:必须有接口

难点:需要反射技术

1.5.3 装饰者设计模式

设计模式:专门为解决某一类问题,而编写的固定格式的代码。

装饰者固定结构:接口A,已知实现类C,需要装饰者创建代理类B

1.创建类B,并实现接口A

2.提供类B的构造方法,参数类型为A,用于接收A接口的其他实现类(C)

3.给类B添加类型为A成员变量,用于存放A接口的其他实现类

4.增强需要的方法

5.实现不需要增强的方法,方法体重调用成员变量存放的其他实现类对应的方法

A a = …C;
B b = new B(a);
class B implements A{
  private Aa;
  public B(Aa){
     this.a =a;
}
//增强的方法
public void close(){
}
  //不需要增强的方法
  public voidcommit(){
   this.a.commit();
}
}

1.5.4 实现

1.5.4.1 装饰类

public class MyConnection implements Connection {
    private Connection conn;
    private List<Connection> pool;
    public MyConnection(Connection conn){
        this.conn= conn;
    }

    /* 因为与自定义连接池有关系,所以需要另外添加一个构造方法 */

    public MyConnection(Connection conn,List<Connection> pool){
        this.conn= conn;
        this.pool= pool;
    }
    /* 以下是增强的方法 */
    @Override
    public void close() throws SQLException {
        //将调用当前close的链接Connection对象添加到链接池中
//      System.out.println("连接归还:" + this);
        this.pool.add(this);
    }
    /* 以下是不需要增强的方法  */

    @Override
    public void commit() throws SQLException {
        this.conn.commit();
    }
    ....
}

1.5.4.2 使用装饰类(包装类)

将由DriverManager创建的连接,使用装饰类包装一下,然后添加到连接池中,构造方法中将容器pool传递进去,方便连接的归还。

1.5.4.3 使用连接

记得分享给身边有需要的人

原文发布于微信公众号 - java学习(javaxxf)

原文发表时间:2018-04-04

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏名山丶深处

springboot集成schedule(深度理解)

2465
来自专栏bboysoul

手撕包菜搭建

最近做了两件事,一件事就是买了块1t硬盘,第二件事就是买了个百度云会员,无奈找不到资源下载,那就没办法了,搭建一个磁力链接搜索引擎来爬去链接,然后去找资源。

5231
来自专栏哈雷彗星撞地球

GCD API 理解 (一)资料先行

GCD 深入理解:第一部分 GCD 深入理解:第二部分 以上两篇文章是关于GCD讲的比较好的文章,翻译自raywenderlich,该网站有很多关于iOS ...

1242
来自专栏Esofar 开发日记

[译]ASP.NET Core依赖注入深入讨论

这篇文章我们来深入探讨ASP.NET Core、MVC Core中的依赖注入,我们将示范几乎所有可能的操作把依赖项注入到组件中。

1211
来自专栏IT杂记

Hadoop SequenceFile BLOCK压缩类型写入数据机制

最近测试Hadoop数据一致性,发现Hadoop SequenceFile BLOCK压缩类型(io.seqfile.compression.type=BLOC...

3375
来自专栏C/C++基础

shell脚本实例

检查脚本书写完成后,需要crontab来定期执行该脚本,意在每隔多长时间去检测一次。crontab命令选项如下:

1232
来自专栏草根专栏

用ASP.NET Core 2.0 建立规范的 REST API -- 预备知识 (2) + 准备项目

3150
来自专栏bboysoul

手撕包菜搭建

最近做了两件事,一件事就是买了块1t硬盘,第二件事就是买了个百度云会员,无奈找不到资源下载,那就没办法了,搭建一个磁力链接搜索引擎来爬去链接,然后去找资源。

2441
来自专栏纯洁的微笑

springboot(十二):springboot如何测试打包部署

有很多网友会时不时的问我,spring boot项目如何测试,如何部署,在生产中有什么好的部署方案吗?这篇文章就来介绍一下spring boot 如何开发、调试...

4356
来自专栏ROBOTEDU

控制程序运行指令

控制程序运行指令 一 子程序 1. 概述 在零件程序分为“主程序”和“子程序”时,就出现了“子程序”的概念。子程序指由主程序调用的零件程序。在目前的SINUME...

3114

扫码关注云+社区

领取腾讯云代金券