14-数据库连接池和jdbc优化

一、数据库连接池

1. 什么是连接池

传统的开发模式下,Servlet处理用户的请求,找Dao查询数据,dao会创建与数据库之间的连接,完成数据查询后会关闭数据库的链接。

这样的方式会导致用户每次请求都要向数据库建立链接而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、宕机。

 

解决方案就是数据库连接池

连接池就是数据库连接对象的一个缓冲池

我们可以先创建10个数据库连接缓存在连接池中,当用户有请求过来的时候,dao不必创建数据库连接,而是从数据库连接池中获取一个,用完了也不必关闭连接,而是将连接换回池子当中,继续缓存

 

使用数据库连接池可以极大地提高系统的性能

 

2. 实现数据库连接池

jdbc统一了数据库的操作  定义了规范

jdbc针对数据库连接池也定义的接口java.sql.DataSource,所有的数据库连接池实现都要实现该接口

该接口中定义了两个重载的方法

Connection getConnection()

Connection getConnection(String username, String password)

 

数据库连接池实现思路

1) 定义一个类实现java.sql.DataSource接口

2) 定义一个集合用于保存Connection对象,由于频繁地增删操作,用LinkedList比较好

3) 实现getConnection方法,在方法中取出LinkedList集合中的一个连接对象返回

注意:

    返回的Connection对象不是从集合中获得,而是删除

    用户用完Connection,会调用close方法释放资源,此时要保证连接换回连接池,而不是关闭连接

    重写close方法是难点,解决方案: 装饰设计模式、动态代理

 

二、 数据源

通常我们把DataSource的实现,按其英文含义称之为数据源,数据源中都包含了数据库连接池的实现。

 

一些开源组织提供了数据源的独立实现,常用的有:

DBCP 数据库连接池

C3P0 数据库连接池

1.  DBCP 数据源

介绍

DBCP 是 Apache 软件基金组织下的开源连接池实现

tomcat服务器就是使用DBCP作为数据库连接池

使用DBCP数据源,需要导入两个jar包

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

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

 

DBCP核心 API

BasicDataSource

数据源实现

BasicDataSourceFactory

用于创建数据源的工厂类

 

dbcp 创建连接池

方法1:  直接创建对象,设置参数

BasicDataSource bds = new BasicDataSource();

 

// 设置连接数据库需要的配置信息

bds.setDriverClassName("com.mysql.jdbc.Driver");

bds.setUrl("jdbc:mysql://localhost:3306/jdbc3");

bds.setUsername("root");

bds.setPassword("root");

 

// 设置连接池的参数

bds.setInitialSize(5);

bds.setMaxActive(10);

 

ds = bds

 

方法2: 通过工厂类创建对象,读取配置文件

try {

   Properties prop = new Properties();

   //
读配置文件

   InputStream in =

      JdbcUtils.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");

   prop.load(in);

   ds = BasicDataSourceFactory.createDataSource(prop);

}catch (Exception e) {

   throw new ExceptionInInitializerError(e);

}

 

配置文件为dbcpconfig.properties

#连接设置

driverClassName=com.mysql.jdbc.Driver

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

username=root

password=root

 

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

initialSize=5

 

#最大连接数量

maxActive=10

 

#<!--
最大空闲连接 -->

maxIdle=10

 

 

#<!--
超时等待时间以毫秒为单位 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

2.  C3P0 数据源

介绍

c3p0是一个开源的jdbc连接池,我们熟悉的 Hibernate和 Sprint 框架使用的都是该数据源

 

创建连接池对象

方法1:直接创建对象,设置参数

ComboPooledDataSource cpds = new ComboPooledDataSource();

cpds.setDriverClass("com.mysql.jdbc.Driver");

cpds.setJdbcUrl("jdbc:mysql://localhost:3306/jdbc3");

cpds.setUser("root");

cpds.setPassword("root");

cpds.setInitialPoolSize(5);

cpds.setMaxPoolSize(15);

方法2:读取配置文件

ComboPooledDataSource cpds = new ComboPooledDataSource("itcast");

配置文件为c3p0-config.xml
该文件需要放在类路径下

<c3p0-config>

 

   <default-config>

      <!—-
默认配置 –->

     
<property name="initialPoolSize">5</property>

     
<property name="maxPoolSize">15</property>

     
<property name="driverClass">com.mysql.jdbc.Driver</property>

     
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc3</property>

     
<property name="user">root</property>

     
<property name="password">root</property>

   </default-config>

   <named-config name="xwh">

     
<property name="initialPoolSize">5</property>

     
<property name="maxPoolSize">15</property>

     
<property name="driverClass">com.mysql.jdbc.Driver</property>

     
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc3</property>

     
<property name="user">root</property>

     
<property name="password">root</property>

   </named-config>

</c3p0-config>

 

三、ResultSetMetaData对象

元数据,可以理解为描述数据的数据

jdbc中的元数据是指数据库、表、列的定义信息

 

ResultSetMetaData对象表示结果集 ResultSet对象的元数据

获得该对象:

ResultSetMetaData metaData = rs.getMetaData();

 

常用方法:

getColumnCount()  返回resultset对象的列数

getColumnName(int column)  获得指定列的名称

getColumnTypeName(int column) 获得指定列的类型

 

四、jdbc优化

使用jdbc对数据库进行crud操作时,会有很多重复的代码,仔细分析不难发现其实变化的只是其中几行代码

 

对于 cud(增删改) 操作,代码几乎完全一样, 唯一的区别就是sql语句不同,我们完全可以把相同的代码抽取出来定义在一个工具方法中,然后定义一个参数来接收sql语句

 

对于 r(查询) 操作,除SQL语句不同之外,根据操作的实体不同,对ResultSet结果集的处理也有所不相同,因此可义一个query方法,除以参数形式接收变化的SQL语句外,可以使用策略模式由qurey方法的调用者决定如何把ResultSet中的数据映射到实体对象中

 

优化后的工具类 JdbcUtils

//
通用的增删改方法

publicstaticint update(String sql, Object[] params)
throws SQLException {

   Connection conn =
null;

   PreparedStatement pstmt =
null;

   ResultSet rs =
null;

   try {

     
// 获得连接

     
conn = getConnection();

     
// 预编译sql

     
pstmt = conn.prepareStatement(sql);

     
// 将参数设置进去

     
for(int i=0; 
params!=null&&i<params.length; i++) {

        
pstmt.setObject(i+1, params[i]);

     
}

     
// 发送sql

     
int num = pstmt.executeUpdate();

     
return num;

   }
finally
{

     
// 释放资源

     
release(conn, pstmt, rs);

   }

}

 

//
优化查询

publicstatic Object query(String sql, Object[] params, ResultSetHandler rsh)
throws SQLException {

   Connection conn =
null;

   PreparedStatement pstmt =
null;

   ResultSet rs =
null;

   try {

     
// 获得连接

     
conn = getConnection();

     
// 预编译sql

     
pstmt = conn.prepareStatement(sql);

     
// 将参数设置进去

     
for(int i=0; params!=null&&i<params.length;
i++) {

        
pstmt.setObject(i+1, params[i]);

     
}

     
// 发送sql

     
rs = pstmt.executeQuery();

     
// 不知道别人想如何处理结果集

     
// 干脆想别人所要一个结果集的处理器

     
// 为了让当前代码继续,定义一个结果集处理器接口

     
// 策略模式,规定算法,具体的算法留给将来的调用者实现

     
Object obj = rsh.handle(rs);

     
return obj;

   }
finally
{

     
// 释放资源

     
release(conn, pstmt, rs);

   }

}

 

 

结果集处理器接口

public interface ResultSetHandler {

   //
处理结果集的方法

   public Object handle(ResultSet rs);

}

 

实现类:

BeanListHandler

public class BeanListHandler implements ResultSetHandler {

 

   private Class clazz;

   public BeanListHandler(Class clazz) {

     
this.clazz = clazz;

   }

   public Object handle(ResultSet rs) {

     
try {

        
// 取出结果集所有的记录,封装到bean,存入list返回

        
List list = new ArrayList();

        
while (rs.next()) {

           
Object bean = clazz.newInstance();

           
// 获得元数据

           
ResultSetMetaData metaData = rs.getMetaData();

           
// 获得列的数量

           
int count = metaData.getColumnCount();

           
// 遍历列

           
for(int i=1; i<=count; i++) {

               
// 取列名

               
String columnName = metaData.getColumnName(i);

               
// 取这列的值

               
Object value = rs.getObject(columnName);

               
// 反射出属性

               
Field field = clazz.getDeclaredField(columnName);

               
// 设置属性

               
field.setAccessible(true);

               
field.set(bean, value);

           
}

           
// 加入list

           
list.add(bean);

        
}

        
return list;

     
} catch (Exception e) {

        
throw new RuntimeException(e);

     
}

   }

 

}

 

BeanHandler

public class BeanHandler implements ResultSetHandler {

   private Class clazz;

   public BeanHandler(Class clazz) {

     
this.clazz = clazz;

   }

   public Object handle(ResultSet rs) {

     
// 不知道有几列数据,不知道列名,不知道封装到什么样的bean

     
// 表的列明和javabean的字段名一致

     
try {

        
if(rs.next()) {

           
// 创建bean

           
Object bean = clazz.newInstance();

           
// 封装数据

           
// 获得结果集的元数据

           
ResultSetMetaData metaData = rs.getMetaData();

           
int count = metaData.getColumnCount();

           
// 迭代取每一列的数据

           
for(int i=1; i<=count; i++) {

               
// 获得列名 
username

               
String columnName = metaData.getColumnName(i);

               
// 获得数据 ddd

               
Object value = rs.getObject(columnName);

               
// 根据列名反射出映射的属性 username

               
Field field = clazz.getDeclaredField(columnName);

               
// 为属性赋值

               
field.setAccessible(true);

               
field.set(bean, value);

           
}

           
return bean;

        
}

        
return null;

     
} catch (Exception e) {

        
throw new RuntimeException(e);

     
}

   }

 

}

 

ArrayHandler

// 取出第一行的所有记录存入一个Object数组

public class ArrayHandler implements ResultSetHandler {

 

   public Object handle(ResultSet rs) {

     
try {

        
if (rs.next()) {

           
// 指向了第一行的记录

           
// 获得元数据

           
ResultSetMetaData metaData = rs.getMetaData();

           
// 获得列数

           
int count = metaData.getColumnCount();

           
// 创建数组

           
Object[] arr = new Object[count];

           
// 迭代所有列的值,存入数组

           
for(int i=1; i<=count; i++) {

               
Object value = rs.getObject(i);   // 获得指定列的值

               
arr[i-1] = value;

           
}

           
return arr;

        
}

        
return null;

     
} catch (Exception e) {

        
throw new RuntimeException(e);

     
}

   }

 

}

 

批处理

 

处理大数据

Clob Character large Object

text

 

Blob binary  large object

 

 

 

 

 

时间: 2025-01-20 10:03:50

14-数据库连接池和jdbc优化的相关文章

JDBC编程学习笔记之数据库连接池的实现

在JDBC编程的时候,获取到一个数据库连接资源是很宝贵的,倘若数据库访问量超大,而数据库连接资源又没能得到及时的释放,就会导致系统的崩溃甚至宕机.造成的损失将会是巨大的.再看有了数据库连接池的JDBC,就会较好的解决资源的创建与连接问题,其主要还是针对于连接资源使用层面的改进.下面我就谈一谈我对数据库连接池的理解. 数据库连接池理论基础 对于创建一个数据库连接池,需要做好准备工作.原理就是先实现DataSource接口,覆盖里面的getConnection()方法,这个方法是我们最为关注的.既然

01_数据库连接池,数据源,ResultSetMetaData,jdbc优化

 一.数据库连接池 1. 什么是连接池 传统的开发模式下,Servlet处理用户的请求,找Dao查询数据,dao会创建与数据库之间的连接,完成数据查询后会关闭数据库的链接. 这样的方式会导致用户每次请求都要向数据库建立链接而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长.假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出.宕机.   解决方案就是数据库连接池 连接池就是数据库连接对象的一个缓冲池 我们可以先创建10

项目重构之数据源配置与优化:log4j 配置数据库连接池Druid,并实现日志存储到数据库

一. 前言   泥瓦匠又和大家见面了,最近两天我在Code Review , 顺便代码小小的Refactoring(重构)下.先了解这个项目吧,这次解决的是数据源配置优化.因为这web项目中配置数据源的地方很多.例如 JDBC要配置数据源,Mybatis要配置数据源,Quartz定时任务要配置数据源,还有Log4j存记录到数据库也要配置-   如题目,兴许大家的疑惑看了前面的说明会明白.这次给大家带来的 数据源配置与优化:log4j 配置数据库连接池Druid.   提纲: 二.准备知识 三.正

基于JDBC的数据库连接池技术研究与应用

数据|数据库|数据库连接 摘 要 本文介绍了Java访问数据库的原理及其存在的问题,提出了解决办法-数据库连接池,并对其关键问题进行了分析,构建了一个简便易用的连接池并结合当前热门技术Servlet说明了其如何在开发时使用. 关键词 JDBC,Jsp/Servlet,数据库连接池,多数据库服务器和多用户,多线程 引言 近年来,随着Internet/Intranet建网技术的飞速发展和在世界范围内的迅速普及,计算机 应用程序已从传统的桌面应用转到Web应用.基于B/S(Browser/Server

基于JDBC的数据库连接池技术研究与设计

设计|数据|数据库|数据库连接 摘 要 本文介绍了基于JDBC的数据库连接池的工作原理,阐述了连接池技术的事务处理.多数据库服务器等各项关键技术,提出了一个高效的连接池管理策略,最后详细说明了数据库连接池应用的具体实现过程. 关键词 JDBC; 数据库; 连接池; 事务处理 随着信息技术的高速发展与广泛应用,数据库技术在信息技术领域中的位置越来越重要,尤其是网络应用和电子商务的迅速发展,都需要数据库技术支持动态Web站点的运行,而传统的开发模式是:首先在主程序(如Servlet.Beans)中建

基于JDBC的数据库连接池高效管理策略

在基于JDBC的数据库应用开发中,数据库连接的管理是一个难点,因为它是决定该应用性能的一个重要因素.本文在对数据库连接进行透彻分析的基础上,提出并实现了一个高效的连接管理策略,使得开发高性能的数据库应用变得相对容易.特别是,对于连接管理中的两个难点:事务和多线程问题进行了深入的剖析,并给出了一个基于设计模式的解决方案. 介绍 在使用Java语言进行和数据库有关的的应用开发中,一般都使用JDBC来进行和数据库的交互,其中有一个关键的概念就是Connection(连接),它在Java中是一个类,代表

基于JDBC 的数据库连接池

介绍 在使用Java语言进行和数据库有关的的应用开发中,一般都使用JDBC来进行和数据库的交互,其中有一个关键的概念就是Connection(连接),它在Java中是一个类,代表了一个通道.通过它,使用数据的应用就可以从数据库访问数据了. 对于一个简单的数据库应用,由于对于数据库的访问不是很频繁.这时可以简单地在需要访问数据库时,就新创建一个连接,用完后就关闭它,这样做也不会带来什么明显的性能上的开销.但是对于一个复杂的数据库应用,情况就完全不同了.频繁的建立.关闭连接,会极大的减低系统的性能,

自定义JDBC数据库连接池小例子

上篇文章中写了一个JDBC的小例子,这篇文章写个数据库连接池的小例子吧. package com.zkn.newlearn.jdbc.mysql.third; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.LinkedLi

tomcat-web编程数据库连接池jdbc的一个问题,连接池感觉弄好了,还是不行哪里错了

问题描述 web编程数据库连接池jdbc的一个问题,连接池感觉弄好了,还是不行哪里错了 org.apache.jasper.JasperException: An exception occurred processing JSP page /exa7_7.jsp at line 18 15: Context initCtx = new InitialContext(); //这里四句是连接池的关键语句,见教材 16: Context envCtx = (Context) initCtx.loo