1.应用程序直接获取数据库连接的缺点

用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、拓机。如下图所示:
JDBC专题(八)-数据库连接池_数据库连接池

2.使用数据库连接池优化程序性能

2.1.数据库连接池的基本概念

数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现的尤为突出.对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标.数据库连接池正式针对这个问题提出来的.数据库连接池负责分配,管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。如下图所示:
JDBC专题(八)-数据库连接池_数据库连接池_02
数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中, 这些数据库连接的数量是由最小数据库连接数来设定的.无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量.连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中.

数据库连接池的最小连接数和最大连接数的设置要考虑到以下几个因素:
最小连接数:是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费.
最大连接数:是连接池能申请的最大连接数,如果数据库连接请求超过次数,后面的数据库连接请求将被加入到等待队列中,这会影响以后的数据库操作
如果最小连接数与最大连接数相差很大:那么最先连接请求将会获利,之后超过最小连接数量的连接请求等价于建立一个新的数据库连接.不过,这些大于最小连接数的数据库连接在使用完不会马上被释放,他将被放到连接池中等待重复使用或是空间超时后被释放.

3.自定义数据连接池

我们可以通过自定义的方式实现连接池!分析连接池类应该包含特定的属性和方法!

  • 属性: 集合
    放置Connection
  • 方法:
    获取连接方法
    回收连接方法

代码实现

/**
* @author bruceliu
* @description 自定义数据库连接池
*/
public class Pool {

static LinkedList<Connection> list = new LinkedList<Connection>();

private static String driverClass;
private static String url;
private static String username;
private static String password;

static {
try {
Properties p = new Properties();
p.load(BaseDao.class.getClassLoader().getResourceAsStream("jdbc.properties"));
driverClass = (String) p.get("driverClass");
url = (String) p.get("url");
username = (String) p.get("username");
password = (String) p.get("password");
} catch (Exception e) {
e.printStackTrace();
}

for (int i = 0; i < 10; i++) {
Connection conn = openConn();
list.add(conn);
}
}

private static Connection openConn() {
try {
Class.forName(driverClass);
Connection conn = DriverManager.getConnection(url, username, password);
System.out.println(conn);
return conn;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

/**
* 01-从数据库连接池中获取一个连接
*
* @return
*/
public static Connection getConn() {
if (list.isEmpty()) {
Connection conn = openConn();
list.add(conn);
}
Connection conn = list.removeFirst();
return conn;
}


/**
* 02-把连接对象放入连接池
*
* @param conn
*/
public static void closeConn(Connection conn) {
if (list.size() >= 10) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
} else {
System.out.println("归还连接对象:" + conn);
list.addLast(conn);
}
}

/**
* 03-获取连接池的大小
*
* @return
*/
public static int getSize() {
return list.size();
}

}

改造BaseDao类

/**
* @time
* @Description
*/
package com.bruce.utils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class BaseDao {

private Connection conn=null;
private PreparedStatement ps=null;
private ResultSet rs=null;

private void openConn(){
conn=Pool.getConn();
System.out.println("连接池大小是:"+Pool.getSize()+",在使用的连接对象是:"+conn);
}

public int myexecuteUpdate(String sql,Object[] params){
this.openConn();
try {
ps=conn.prepareStatement(sql);

if(params!=null){
for (int i = 0; i < params.length; i++) {
ps.setObject(i+1, params[i]);
}
}
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}finally{
this.closConn();
}
return 0;
}

public ResultSet myexecuteQuery(String sql,Object[] parms){
this.openConn();
try {
ps=conn.prepareStatement(sql);
if(parms!=null){
for (int i = 0; i < parms.length; i++) {
ps.setObject(i+1, parms[i]);
}
}
rs=ps.executeQuery();
} catch (Exception e) {
e.printStackTrace();
}finally{

}
return rs;
}

protected void closConn() {
try {
if (rs != null) {
rs.close();
}
if (ps != null) {
ps.close();
}
if (conn != null) {
Pool.closeConn(conn);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}

4.开源数据库连接池(第三方企业中使用的连接池)

通常我们把DataSource的实现,按其英文含义称之为数据源,数据源中都包含了数据库连接池的实现。
也有一些开源组织提供了数据源的独立实现:

  • DBCP 数据库连接池(过时)
  • C3P0 数据库连接池
  • Druid(德鲁伊) 数据库连接池

在使用了数据库连接池之后,在项目的实际开发中就不需要编写连接数据库的代码了,直接从数据源获得数据库的连接。

4.1.DBCP数据源

DBCP(DataBase connection pool),数据库连接池。是 apache 上的一个 java 连接池项目,也是 tomcat 使用的连接池组件。单独使用dbcp需要2个包:commons-dbcp.jar,commons-pool.jar由于建立数据库连接是一个非常耗时耗资源的行为,所以通过连接池预先同数据库建立一些连接,放在内存中,应用程序需要建立数据库连接时直接到连接池中申请一个就行,用完后再放回去。

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

手动配置
JDBC专题(八)-数据库连接池_数据库连接_03
DBCP配置文件的方式
Class BasicDataSourceFactory 对象中提供如下方法
JDBC专题(八)-数据库连接池_数据库连接池_04
根据给定的配置文件创建一个javax.sql.DataSource的实例。不需要我们自己去读取properties中内容。
同样根据key=value 根据key去取值。key要满足dbcp的需求。

只需要把Properties 对象传递给该方法。dbcp就会自己去读取配置文件中的连接的参数。根据这些参数去创建连接池。dbcp自己读取Properties。key需要满足dbcp的格式。

1.创建一个配置文件dbcp.properites.
文件的内容:

url=jdbc:mysql://localhost:3306/day08
username=root
password=123456
driverClassName=com.mysql.jdbc.Driver

2、加载配置文件properties加载成properties对象
从src的目录下开始寻找文件。

InputStream is = new FileInputStream("src/dbcp.properties");
prop.load(is);

3、调用dbcp提供方法创建连接池的实现类

javax.sql.DataSource ds = BasicDataSourceFactory.createDataSource(prop);

JDBC专题(八)-数据库连接池_数据库连接池_05

4.2.C3P0连接池

C3P0是一个开源的JDBC连接池,支持JDBC规范。目前使用它的开源项目有Hibernate,Spring等。
C3P0与dbcp区别

1.回收空闲连接的功能
dbcp没有自动回收空闲连接的功能
c3p0有自动回收空闲连接功能

2. 读取配置文件
dbcp需要手动设置配置文件(自己写Java代码度配置文件)
c3p0不需要手动设置(自动读取配置文件)

3.配置文件的书写要求
Dbcp的配置文件没有特殊的要求(文件名、属性名等)
c3p0的配置文件需要按着要求设置文件名和属性名等

手动配置
JDBC专题(八)-数据库连接池_数据库连接_06
配置文件方式
c3p0 要求把一个名为c3p0-config.xml的配置文件放到src目录下,c3p0 会读取这个xml里面的信息。

<c3p0-config>
<!--
C3P0的缺省(默认)配置,
如果在代码中“ComboPooledDataSource ds = new ComboPooledDataSource();”这样写就表示使用的是C3P0的缺省(默认)配置信息来创建数据源
-->
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcstudy</property>
<property name="user">root</property>
<property name="password">123</property>
</default-config>


</c3p0-config>

代码:
JDBC专题(八)-数据库连接池_数据库连接池_07

4.3.Druid连接池

Druid 是目前比较流行的高性能的,它有如下几个特点:
一. 亚秒级查询
druid提供了快速的聚合能力以及亚秒级的查询能力,多租户的设计,是面向用户分析应用的理想方式。
二.实时数据注入
druid支持流数据的注入,并提供了数据的事件驱动,保证在实时和离线环境下事件的实效性和统一性
三.可扩展的PB级存储
druid集群可以很方便的扩容到PB的数据量,每秒百万级别的数据注入。即便在加大数据规模的情况下,也能保证时其效性
四.多环境部署
druid既可以运行在商业的硬件上,也可以运行在云上。它可以从多种数据系统中注入数据,包括hadoop,spark,kafka,storm和samza等
五.丰富的社区
druid拥有丰富的社区,供大家学习

4.3.1.druid连接池的使用
  • 导入jar包
    JDBC专题(八)-数据库连接池_数据库连接_08
  • 修改BaseDao
private static DruidDataSource ds;

static {

try {
Properties p = new Properties();
p.load(BaseDao.class.getClassLoader().getResourceAsStream("jdbc.properties"));
driverClass = (String) p.get("driverClass");
url = (String) p.get("url");
username = (String) p.get("username");
password = (String) p.get("password");
maxActive = (String) p.get("maxActive");

// /实例化数据库连接池对象
ds = new DruidDataSource();
// 设置驱动类全称
ds.setDriverClassName(driverClass);
// 设置连接的数据库
ds.setUrl(url);
// 设置用户名
ds.setUsername(username);
// 设置密码
ds.setPassword(password);
// 设置最大连接数量
ds.setMaxActive(Integer.parseInt(maxActive));

//显示SQL
ds.setFilters("stat");

} catch (Exception e) {
e.printStackTrace();
}
}

private void openConn() {
try {
conn = ds.getConnection();
System.out.println(conn);
} catch (Exception e) {
e.printStackTrace();
}
}
4.3.2.德鲁伊数据库连接池可视化监控

JDBC专题(八)-数据库连接池_连接池_09
德鲁伊数据库连接池,本身自带一个管理员监控程序,方便数据库管理员对数据源进行监控!
查看SQL的执行状况

//显示SQL
ds.setFilters(“stat”);

配置Web.xml

<!--连接池启用Web监控统计功能start -->
<filter>
<filter-name>DruidWebStatFilter</filter-name>
<filter-class>com.alibaba.druid.support.http.WebStatFilter</filter-class>
<init-param>
<param-name>exclusions</param-name>
<param-value>*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>DruidWebStatFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>DruidStatView</servlet-name>
<servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
<init-param>
<!-- 不允许清空统计数据 -->
<param-name>resetEnable</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<!-- 用户名 -->
<param-name>loginUsername</param-name>
<param-value>bruce</param-value>
</init-param>
<init-param>
<!-- 密码 -->
<param-name>loginPassword</param-name>
<param-value>123</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>DruidStatView</servlet-name>
<url-pattern>/druid/*</url-pattern>
</servlet-mapping>
<!--连接池启用Web监控统计功能end-->