Java 进阶 & JDBC数据库连接池常用的三种实现方式
连接池的使用
数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出。 一个数据库连接对象均对应一个物理数据库连接,每次操作都打开一个物理连接,使用完都关闭连接,这样造成系统的 性能低下。
数据库连接池的解决方案是在应用程序启动时建立足够的数据库连接,并讲这些连接组成一个连接池,由应用程序动态地对池中的连接进行申请、使用和释放。
对于多于连接池中连接数的并发请求,应该在请求队列中排队等待。并且应用程序可以根据池中连接的使用率,动态增加或减少池中的连接数。
连接池技术尽可能多地重用了消耗内存地资源,大大节省了内存,提高了服务器地服务效率,能够支持更多的客户服务。通过使用连接池,将大大提高程序运行效率,同时,我们可以通过其自身的管理机制来监视数据库连接的数量、使用情况等。
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。
连接池的工作原理
连接池的工作原理主要由三部分组成,分别为连接池的建立、连接池中连接的使用管理、连接池的关闭。
第一、连接池的建立。一般在系统初始化时,连接池会根据系统配置建立,并在池中创建了几个连接对象,以便使用时能从连接池中获取。连接池中的连接不能随意创建和关闭,这样避免了连接随意建立和关闭造成的系统开销。Java中提供了很多容器类可以方便的构建连接池,例如Vector、Stack等。
第二、连接池的管理。连接池管理策略是连接池机制的核心,连接池内连接的分配和释放对系统的性能有很大的影响。其管理策略是: 当客户请求数据库连接时,首先查看连接池中是否有空闲连接,如果存在空闲连接,则将连接分配给客户使用;如果没有空闲连接,则查看当前所开的连接数是否已经达到最大连接数,如果没达到就重新创建一个连接给请求的客户;如果达到就按设定的最大等待时间进行等待,如果超出最大等待时间,则抛出异常给客户。
当客户释放数据库连接时,先判断该连接的引用次数是否超过了规定值,如果超过就从连接池中删除该连接,否则保留为其他客户服务。
该策略保证了数据库连接的有效复用,避免频繁的建立、释放连接所带来的系统资源开销。
第三、连接池的关闭。当应用程序退出时,关闭连接池中所有的连接,释放连接池相关的资源,该过程正好与创建相反。
传统数据库连接对比数据库连接池
æ°æ®åºå¸¸ç¨è¿æ¥æ± æ»ç»
image.gif
不使用数据库连接池的步骤:
TCP建立连接的三次握手
MySQL认证的三次握手
真正的SQL执行
MySQL的关闭
TCP的四次握手关闭
可以看到,为了执行一条SQL,却多了非常多我们不关心的网络交互。
优点:实现简单
缺点:
- 网络IO较多
- 数据库的负载较高
- 响应时间较长及QPS较低
- 应用频繁的创建连接和关闭连接,导致临时对象较多,GC频繁
- 在关闭连接后,会出现大量TIME_WAIT 的TCP状态(在2个MSL之后关闭)
使用数据库连接池的步骤:
第一次访问的时候,需要建立连接。 但是之后的访问,均会复用之前创建的连接,直接执行SQL语句。
优点:
较少了网络开销
系统的性能会有一个实质的提升
没了麻烦的TIME_WAIT状态
目前存在多个开源的java数据库连接池,这些连接池都是在java.sql基础上编写而成。
mysql建数据库、创建表
CREATE TABLE `user` (
`userid` INT(11) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(255) DEFAULT NULL,
`password` VARCHAR(255) DEFAULT NULL,
`email` VARCHAR(255) DEFAULT NULL,
`phone` VARCHAR(255) DEFAULT NULL,
`status` VARCHAR(255) NOT NULL DEFAULT '0',
`code` VARCHAR(255) DEFAULT NULL,
PRIMARY KEY (`userid`)
) ENGINE=INNODB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8;
常用连接池
DBCP连接池
DBCP 是一个开源的连接池,是Apache Common成员之一,在企业开发中也比较常见,tomcat内置的连接池。该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用。
应用程序应在系统中增加如下两个 jar 文件:
导入jar包
- mysql-connector-java-5.1.37-bin.jar:数据库驱动
- commons-dbutils-1.6.jar:提供QueryRunner类方便进行增删改查操作
- commons-dbcp-1.4.jar:连接池的实现
- commons-pool-1.5.6.jar:连接池实现的依赖库,提供高效的数据库连接池技术
核心类:org.apache.commons.dbcp2.BasicDataSource extends Object implements DataSource
案例代码
实现数据库连接池工具类
/*
* 使用DBCP实现数据库的连接池
* 连接池配置,自定义类,
* 最基本四项完整
* 对于数据库连接池其他配置,自定义
*/
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
public class JDBCUtils{
//创建出BasicDataSource类对象
private static BasicDataSource datasource = new BasicDataSource();
//静态代码块,对象BasicDataSource对象中的配置,自定义
static{
//数据库连接信息,必须的
datasource.setDriverClassName("com.mysql.jdbc.Driver");
datasource.setUrl("jdbc:mysql://localhost:3306/user");
datasource.setUsername("root");
datasource.setPassword("123");
//对象连接池中的连接数量配置,可选的
datasource.setInitialSize(10);//初始化的连接数
datasource.setMaxActive(8);//最大连接数量
datasource.setMaxIdle(5);//最大空闲数
datasource.setMinIdle(1);//最小空闲
}
//定义静态方法,返回BasicDataSource类的对象
public static DataSource getDataSource(){
return datasource;
}
}
image.gif
工具类的测试
/*
* 测试写好的工具类,
* 提供的是一个DataSource接口的数据源
* QueryRunner类构造方法,接收DataSource接口的实现类
* 后面,调用方法update,query,无需传递他们Connection连接对象
*/
import java.sql.SQLException;
import java.util.List;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.ArrayListHandler;
import cn.itcast.jdbcutils.JDBCUtils;
public class QueryRunnerDemo{
public static void main(String[] args) {
select();
}
//定义2个方法,实现数据表的添加,数据表查询
//QueryRunner类对象,写在类成员位置
private static QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());
//数据表查询
public static void select(){
String sql = "SELECT * FROM user";
try{
List<Object[]> list = qr.query(sql, new ArrayListHandler());
for(Object[] objs : list){
for(Object obj : objs){
System.out.print(obj+"\t");
}
System.out.println();
}
}catch(SQLException ex){
throw new RuntimeException("数据查询失败");
}
}
//数据表添加数据
public static void insert(){
String sql = "INSERT INTO user(username,password,email,phone,status,code)VALUES(?,?,?,?,?,?)";
Object[] params = {"AAA",123456,"AAA@163.COM","150000000","0","234"};
try{
int row = qr.update(sql, params);
System.out.println(row);
}catch(SQLException ex){
throw new RuntimeException("数据添加失败");
}
}
}
image.gif
C3P0连接池
最常用的连接池技术!Spring框架默认支持C3P0连接池技术!
导入jar包
- mysql-connector-java-5.1.37-bin.jar:数据库驱动
- commons-dbutils-1.6.jar:提供QueryRunner类方便进行增删改查操作
- c3p0-0.9.1.2.jar:连接池的实现
核心类:com.mchange.v2.c3p0.ComboPooledDataSource implements PooledDataSource extends DataSource
案例代码:
package com.test.common.util;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
public class DataSourceFactory
{
private static Map<String, ComboPooledDataSource> dsMap = new HashMap();
public static synchronized ComboPooledDataSource getDataSource() {
return getDataSource("default");
}
public static synchronized ComboPooledDataSource getDataSource(String dbName) {
ComboPooledDataSource ds = (ComboPooledDataSource)dsMap.get(dbName);
if (ds == null) {
ds = new ComboPooledDataSource(dbName);
dsMap.put(dbName, ds);
}
return ds;
}
public static synchronized Connection getConnection()
{
return getConnection("default");
}
public static synchronized Connection getConnection(String dbName)
{
ComboPooledDataSource ds = getDataSource(dbName);
Connection conn = null;
try
{
conn = ds.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
}
image.gif
package com.test.common.util;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.MapHandler;
import org.apache.commons.dbutils.handlers.MapListHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DBUtil
{
private static Logger log = LoggerFactory.getLogger(DBUtil.class);
private static final String DEFAULT_DB_NAME = "default";
public static void updateSQL(String sql)
{
updateSQL("default", sql);
}
public static void updateSQL(String dbName, String sql)
{
QueryRunner queryRunner = new QueryRunner(DataSourceFactory.getDataSource(dbName));
try {
log.debug("sql:" + sql);
int i = queryRunner.update(sql);
SeleniumUtil.logInfo("结果:" + i + " 行数据已经更新");
} catch (SQLException e) {
e.printStackTrace();
}
}
public static List<Map<String, Object>> getMultiResult(String sql)
{
return getMultiResult("default", sql);
}
public static List<Map<String, Object>> getMultiResult(String dbName, String sql)
{
List list = null;
QueryRunner queryRunner = new QueryRunner(DataSourceFactory.getDataSource(dbName));
try {
log.debug("sql:" + sql);
list = (List)queryRunner.query(sql, new MapListHandler());
return list;
} catch (SQLException e) {
e.printStackTrace();
}
return list;
}
public static Map<String, Object> getSingleResult(String sql)
{
return getSingleResult("default", sql);
}
public static Map<String, Object> getSingleResult(String dbName, String sql)
{
Map map = null;
QueryRunner queryRunner = new QueryRunner(DataSourceFactory.getDataSource(dbName));
try {
log.debug("sql:" + sql);
map = (Map)queryRunner.query(sql, new MapHandler());
return map;
} catch (SQLException e) {
e.printStackTrace();
}
return map;
}
}
image.gif
工程的src\main\resources路径下新增配置文件 c3p0-config.xml,程序会自动加载src下c3p0的配置文件【c3p0-config.xml】
内容如下
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<!-- 命名的配置,可以通过方法调用实现 -->
<named-config name="default">
<property name="user">demotest</property>
<property name="password">demotest</property>
<property name="jdbcUrl">jdbc:oracle:thin:@192.168.200.**:****:***</property>
<property name="driverClass">oracle.jdbc.driver.OracleDriver</property>
<!-- 如果池中数据连接不够时一次增长多少个 -->
<property name="acquireIncrement">5</property>
<!-- 初始化数据库连接池时连接的数量 -->
<property name="initialPoolSize">20</property>
<!-- 数据库连接池中的最大的数据库连接数 -->
<property name="maxPoolSize">25</property>
<!-- 数据库连接池中的最小的数据库连接数 -->
<property name="minPoolSize">5</property>
</named-config>
<named-config name="test2">
<property name="user">demotest2</property>
<property name="password">demotest2</property>
<property name="jdbcUrl">jdbc:oracle:thin:@192.168.200.**:****:***</property>
<property name="driverClass">oracle.jdbc.driver.OracleDriver</property>
<!-- 如果池中数据连接不够时一次增长多少个 -->
<property name="acquireIncrement">5</property>
<!-- 初始化数据库连接池时连接的数量 -->
<property name="initialPoolSize">20</property>
<!-- 数据库连接池中的最大的数据库连接数 -->
<property name="maxPoolSize">25</property>
<!-- 数据库连接池中的最小的数据库连接数 -->
<property name="minPoolSize">5</property>
</named-config>
</c3p0-config>
image.gif
DRUID连接池
Druid是阿里巴巴开发的号称为监控而生的数据库连接池。可以监控数据库访问性能,Druid内置提供了一个功能强大的StatView插件,能够详细统计SQL的执行性能,这对于线上分析数据库访问性能有帮助。
导入jar包:
- druid-1.1.5.jar
- mysql-connector-java-5.1.41.jar
核心类 com.alibaba.druid.pool.DruidDataSource extends DruidAbstractDataSource implements DataSource
/*
* 使用实现了javax.sql.DataSource接口的子类--druid连接池硬编码
*/
@Test
public void test6(){
try {
//1.驱动注册程序
DruidDataSource dds=new DruidDataSource();
dds.setDriverClassName(driverClassName);
dds.setUrl(url);
dds.setUsername(userName);
dds.setPassword(userPassword);
//2.获取连接对象
con=dds.getConnection();
//3.准备sql语句
String sql="select * from user";
//4.创建prepareStatement
pst=con.prepareStatement(sql);
//5.执行sql语句,得到返回结果
rs= pst.executeQuery();
//6.遍历结果,索引从1开始
while(rs.next()){
System.out.println(rs.getString(1)+" "+rs.getString(2)+" "+rs.getString(3));
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/*
* 使用实现了javax.sql.DataSource接口的子类--druid连接池软编码
*/
@Test
public void test7(){
try {
//1.驱动注册程序
Properties pro=new Properties();
DataSource dds=null;
InputStream inStream=SqlDriverManage.class.getResourceAsStream("druid.properties");
pro.load(inStream);
dds=com.alibaba.druid.pool.DruidDataSourceFactory.createDataSource(pro);
//2.获取连接对象
con=dds.getConnection();
//3.准备sql语句
String sql="select * from user";
//4.创建prepareStatement
pst=con.prepareStatement(sql);
//5.执行sql语句,得到返回结果
rs= pst.executeQuery();
//6.遍历结果,索引从1开始
while(rs.next()){
System.out.println(rs.getString(1)+" "+rs.getString(2)+" "+rs.getString(3));
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
image.gif
工程的src\main\resources路径下新增配置文件druid.properties。druid.properties文件内容如下:
#druid.properties
driverClassName=com.mysql.cj.jdbc.Driver
username=root
password=123456
url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=truecharacterEncoding=UTF-8&useSSL=false&serverTimezone=UTC
image.gif