第十章:数据库连接池与DBUtils工具
学习目标
一: 数据库连接池
二: DBUtils工具
三:任务十.使用DBUtils实现增删改查
学习内容
由于每次对数据库进行操作时都需要创建和关闭Connection对象的过程会不断重复出现这种模式这种频繁的操作会导致大量时间浪费并且增加了代码量所以在实际开发中开发人员通常会采用数据库连接池机制来解决这些问题apache组织还提供了一个名为DBUtils的工具类库这个库实现了对JDBC协议的便捷封装从而在不影响性能的前提下极大地方简化了 JDBC 的编程工作
一:数据库连接池
在JDBC编程过程中每次创建或释放Connection对象都会消耗一定的时间与IO资源。这是因为当Java程序试图与数据库建立连接时系统需进行身份认证并为其分配必要的资源而Java程序则需将表示该连接的java.sql.Connection实例加载到内存中这样会产生较大的性能消耗尤其是处理高并发操作时尤为明显例如一个每天有10万次访问需求的网站在服务器端将需要为每个请求创建并释放10万个JDBC链接这必然会导致资源占用问题进而影响整体系统的性能稳定性和可靠性
为了减少对数据库连接的频繁创建需求,数据库连接池技术应运而生。该技术通过管理分配和释放数据库连接来优化资源利用率,并使应用程序能够有效地重复使用已有的数据库连接而无需重新建立新的连接。
在初始化阶段, 数据库连接池会预先分配一定数量的数据库链接并将这些链接存放在专门设置好的队列里.当应用程序执行数据存取操作时, 并不直接生成新的DBLink对象, 而是由队列自动管理资源.在这种机制下, 如果当前队列中有未被占用的DBLink实例, 系统会自动释放并归还给其他线程请求.只有当队列中的资源耗尽时, 才会产生新的DBLink实例.完成任务后,默认情况下会回收当前使用的DBLink实例并将未被使用的链接释放以便其他线程使用

10.1.2 DataSource接口
1.Connection getConnection()
2.Connection getConnection(String username,String password)
这两种重载的方法均可用于获取Connection对象。其区别在于第一种方法无需参数即可完成与数据库的连接建立过程,而第二种方法则需要预先提供必要的登录信息以实现与数据库的连接建立
大多数接口都会有一个实现类,在此情况下除非常规情况除外,一般称其为数据源.从字面意义来说,就是用来获取数据的地方.这些连接信息记录在数据源中.与查找文件时通过文件名定位类似,也可以找到相应的数据库连接.
数据库连接池构成了数据源的基础。当数据被视为水时,在数据库中扮演的角色就像水库中的水源一样不可替代。因此,
一个有效的数据库系统可以被理解为一条通往水库水源的管道系统。
多个开源组织已开发出各自独立的数据流管理方案,
其中DBCP系列解决方案最为常用,
而C3P0则以其稳定性和灵活性著称。
10.1.3 DBCP数据源
DBCP即为数据库连接池的简称,在Apache组织下提供了开源实现,并广泛应用于Tomcat服务器环境中的连接池组件使用。当单独使用DBCP数据源时,则需在应用中导入两个JAR文件以满足需求
1.commons-dbcp.jar包
commons-dbcp.jar包:作为DBCP数据源的实现包,在其内部包含了多种数据库连接信息以及建立连接池所需的相关功能,并且不仅实现了该接口的所有getConnection()方法
2.commons-pool.jar包
commons-pool.jar包:作为基于Java的一键式数据库连接池实现的关键组件存在,在此框架中为 commons-dbcp.jar 包提供了必要的功能辅助构建块。换一种说法就是说缺少了这个关键组件的话, commons-dbcp.jar 包中的许多功能就无法正常运行了。
commons-dbcp-jar软件包中包含了两大核心组件,分别是 BasicDataSourceFactory 和 BasicDataSource,它们都具备获取数据库连接对象的方法。
BasicDataSourceFactory:是DataSource接口的实现类,
该类实现了为生成BasicDataSource实例而创建实例的功能,并包含一个名为createDataSource()的方法用于返回 BasicDataSource 实例。该方法基于获取配置文件中的相关信息来生产相应的 数据源 对象,并供调用者进行后续操作。这种方法将数据库 连接 参数与初始化设置提取到配置文件中以便于管理与维护 使得代码实现更加直观且逻辑更为清晰 并且当采用 DBCP 作为 数据源 时,默认会先创建一个默认的数据 源 实例 在这样的情况下 首先要创建 数据 源 对象 数据 源 对象 的 创建 方式 具体包括两种情况
1.通过BasicDataSource类直接创建数据源对象
当使用BasicDataSource类创建一个对象时
2.通过读取配置文件创建数据源对象
除了直接使用BasicDataSource创建数据源对象之外, 另外一种方式是通过BasicDataSourceFactory工厂类读取配置文件来生成数据源实例, 并且随后从中获取数据库连接对象
第一类:直接使用BasicDataSource类构造数据源对象
package cn.itcast.chapter10.example;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
public class Example01 {
public static DataSource ds = null;
static {
// 获取DBCP数据源实现类对象
BasicDataSource bds = new BasicDataSource();
// 设置连接数据库需要的配置信息
bds.setDriverClassName("com.mysql.jdbc.Driver");
bds.setUrl("jdbc:mysql://localhost:3306/jdbc");
bds.setUsername("root");
bds.setPassword("itcast");
// 设置连接池的参数
bds.setInitialSize(5);
bds.setMaxActive(5);
ds = bds;
}
public static void main(String[] args) throws SQLException {
// 获取数据库连接对象
Connection conn = ds.getConnection();
//获取数据库连接信息
DatabaseMetaData metaData = conn.getMetaData();
//打印数据库连接信息
System.out.println(metaData.getURL()
+",UserName="+metaData.getUserName()
+","+metaData.getDriverName());
}
}
运行结果为:

第二种:通过从配置文件中读取设置创建数据源对象,并由此获得数据库连接对象。
示例
package cn.itcast.chapter10.example;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
public class Example02 {
public static DataSource ds = null;
static {
// 新建一个配置文件对象
Properties prop = new Properties();
try {
// 通过类加载器找到文件路径,读配置文件
InputStream in = new Example02().getClass().getClassLoader()
.getResourceAsStream("dbcpconfig.properties");
// 把文件以输入流的形式加载到配置对象中
prop.load(in);
// 创建数据源对象
ds = BasicDataSourceFactory.createDataSource(prop);
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
public static void main(String[] args) throws SQLException {
// 获取数据库连接对象
Connection conn = ds.getConnection();
//获取数据库连接信息
DatabaseMetaData metaData = conn.getMetaData();
//打印数据库连接信息
System.out.println(metaData.getURL()
+",UserName="+metaData.getUserName()
+","+metaData.getDriverName());
}
}
10.1.4 C3PO数据源
C3PO是一个广受欢迎的开源数据库连接池方案,在遵循标准开发接口的同时支持JDBC 2和JDBC 3规范体系。相比其他解决方案,C3PO展现出卓越的扩展性和高性能特点,这一点得到了Hibernate和Spring等主流框架的认可。当在开发中使用C3PO数据源时,开发者应掌握ComboPooledDataSource这一核心组件,它提供了相关方法以实现对数据库资源的有效管理。通过这一接口,CSSM PO能够高效地管理大量数据源对象,从而提升整体应用性能。
利用ComboPooledDataSource()构造方法来创建数据源对象时,需要手动配置其属性值,并随后获取数据库连接对象。
将JAR文件c3p0- -0.9.1.2.jar引入到项目chapter10中,并在itcast.chapter 10.example包内的cn目录下定义example03类。随后,在itcast.chapter 10.example包内定义example03类时,默认采用C3P0数据源手动代码实现对Process Connection对象的获取功能。
package cn.itcast.chapter10.example;
import java.sql.SQLException;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class Example03 {
public static DataSource ds = null;
// 初始化C3P0数据源
static {
ComboPooledDataSource cpds = new ComboPooledDataSource();
// 设置连接数据库需要的配置信息
try {
cpds.setDriverClass("com.mysql.jdbc.Driver");
cpds.setJdbcUrl("jdbc:mysql://localhost:3306/jdbc");
cpds.setUser("root");
cpds.setPassword("itcast");
// 设置连接池的参数
cpds.setInitialPoolSize(5);
cpds.setMaxPoolSize(15);
ds = cpds;
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
public static void main(String[] args) throws SQLException {
// 获取数据库连接对象
System.out.println(ds.getConnection());
}
}
运行main()方法后,程序的运行结果为:

- 采用ComboPooledDataSource(String configName)构造方法读取c3p0-config.xml配置文件。
进而生成数据源实例。
随后获得数据库连接信息。
以下将通过一个实例来说明如何实现这一点。
详细步骤如下:
为位于src目录根部的项目需求而创建一个位于c3p0-config目录下的xml配置文件,在指定位置存储数据库连接参数以及数据源初始化信息。
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">
jdbc:mysql://localhost:3306/jdbc
</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="checkoutTimeout">30000</property>
<property name="initialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">100</property>
<property name="minPoolSize">10</property>
<property name="maxStatements">200</property>
</default-config>
<named-config name="itcast">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">
jdbc:mysql://localhost:3306/jdbc
</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">15</property>
</named-config>
</c3p0-config>
基于cn.itcast.chapter10.example包生成一个名为Example04的类,并使该类采用C3P0数据源从配置文件中获取Connection对象。
package cn.itcast.chapter10.example;
import java.sql.SQLException;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class Example04 {
public static DataSource ds = null;
// 初始化C3P0数据源
static {
// 使用c3p0-config.xml配置文件中的named-config节点中name属性的值
ComboPooledDataSource cpds = new ComboPooledDataSource("itcast");
ds = cpds;
}
public static void main(String[] args) throws SQLException {
System.out.println(ds.getConnection());
}
}
2)运行main()方法后,程序的运行结果如图:

二: DBUtils工具
10.2.1 DBUtils 工具介绍
为了使使用 JDBC 更加简便,Apache 提供了一个 DBUtils 工具。这是一个操作数据库的重要组件,在不影响性能的前提下极大简化了 JDBC 编码工作的复杂度
其核心在于org.apache.commons.dbutils.QueryRunner类及其相关接口org.apache.commons.dbutils.ResultSetHandler,在学习和使用DBUtils工具时熟悉这些组件至关重要。
QueryRunner类优化了处理SQL语句的代码,在与其结合使用时能够有效地处理大多数数据库操作,并显著地降低了编码的工作量。
QueryRunner类支持一个参数的构造方法,并接收javax.sql.DataSource类型作为参数来获取Connection对象。该类还实现了一系列通用功能以应对不同类型的数据库操作,请具体参阅以下所述功能:
getQuery(String sql, ResultSetHandler rsh, Object...params) 方法负责执行查询操作,并通过使用提供的数据源或调用setDataSource()方法来建立连接
(2)update(String sql,object…params)方法
该方法可用于处理增删改操作,在此过程中,参数params标识SQL语句中的替换参数
(3)update(String sql)方法
该方法用来执行插入,更新或者删除操作,它不需要置换参数
10.2.3 ResultSetHandler接口
该接口用于管理结果集(ResultSet),能够将结果集中的数据转换为多种不同的格式。基于结果集中的数据类型划分,该接口提供了多种具体的实现方案来满足不同需求:
例如布尔值、整数值、字符串值以及日期时间值等多种数据类型的处理方式。
BeanHandler:将第一行数据记录生成为相应的JavaBean对象
BeanListHandler:将查询结果集中的每个记录封装到一个JavaBean实例对象中,并添加到列表中。
ScalarHandler:负责处理某个数据记录实例中的一条记录中指定字段中的数据,并将其转换为Object对象
(4)handle(java.sql.ResultSet rs):若未预先实现所需功能,则可通过自定义一个满足需求的ResultSetHandler接口类,并在重写其handle()方法时完成结果集的具体处理
BeanHandler和BeantlistHandler
这两个类实现了将结果集中的数据封装到对应的JavaBean实例中的功能。
这种方法在实际开发中应用最为广泛。
接下来我们将通过代码实践来掌握如何使用这两个类及其间的差异。
具体步骤如下。
(1)在名为jdbc的数据库中创建数据表user,创建语句如下:
CREATE TABLE user(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20) NOT NULL,
password VARCHAR(20) NOT NULL);
向user表插入3条数据,具体语句如下:
NSERT INTO user VALUES(1,'zhangyanju','123456'),
(2,'yiyi','123456'),
(3,'lili','123456')
为了查看数据是否添加成功,使用SELECT语句查询user表.

将下载的DBUtis工具中的JAR文件commons- dbutils- 1.6.jar放置于项目指定目录下的lib文件夹内,并将其复制至目标目录下的cn.itcast.chapter10.example子目录中。
在chapter10项目的cn.itcast.chapter10.example目录下采用顿号建立一个名为BaseDao的新类,并在其内部实现通用查询功能。
package cn.itcast.chapter10.example;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.commons.dbutils.ResultSetHandler;
public class BaseDao {
// 优化查询
public static Object query(String sql, ResultSetHandler<?> rsh,
Object... params) throws SQLException {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
// 定义一个返回结果
Object obj = null;
try {
// 获得连接
conn = JDBCUtils.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();
// 让调用者去实现对结果集的处理
obj = rsh.handle(rs);
} catch (Exception e) {
// 出现异常,返回封装的异常信息
return new Exception(e.getMessage());
}finally {
// 释放资源
JDBCUtils.release(rs, pstmt, conn);
}
return obj;
}
}
基于cn.itcast.chapter 10.example包中的框架,在其中注册一个名为User的实体类,并通过该类来包装或实现与User对象交互的功能。
package cn.itcast.chapter10.example;
public class User {
private int id;
private String name;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
在cnitcast.chapter10.example目录下定义一个名为ResultSetTest1的类用于说明BeanHandler对象如何处理结果集的具体代码如下
package cn.itcast.chapter10.example;
import java.sql.SQLException;
import org.apache.commons.dbutils.handlers.BeanHandler;
public class ResultSetTest1 {
public static void testBeanHandler() throws SQLException {
BaseDao basedao = new BaseDao();
String sql = "select * from user where id=?";
// 获取查询结果
Object object = basedao.query(sql, new BeanHandler(User.class), 1);
// 判断查询结果,如果是User类就进行打印,否则打印查询的结果信息
if (object!=null && object instanceof User){
User user= (User) object;
System.out.print("id为1的User对象的name值为:" + user.getName());
}else {
System.out.println("查询结果为空: "+object);
}
}
public static void main(String[] args) throws SQLException {
testBeanHandler();
}
}
结果为:

在cni Cast chapter 1.example包下实现一个名为RsesTetet的类旨在演示Bensustander类对结果集的处理逻辑,并附上具体代码实现。
package cn.itcast.chapter10.example;
import java.sql.SQLException;
import java.util.ArrayList;
import org.apache.commons.dbutils.handlers.BeanListHandler;
public class ResultSetTest2 {
public static void testBeanListHandler() throws SQLException {
BaseDao basedao = new BaseDao();
String sql = "select * from user ";
ArrayList<User> list = (ArrayList<User>) basedao.query(sql,
new BeanListHandler(User.class));
for (int i = 0; i < list.size(); i++) {
System.out.println("第" + (i + 1) + "条数据的username值为:"
+ list.get(i).getName());
}
}
public static void main(String[] args) throws SQLException {
testBeanListHandler();
}
}

(7)ScalarHandler
当在处理数据库操作时,在结果集某一行中获取特定字段值的一种方法是采用ScalarHandler类进行实现。以下将通过一个示例展示如何利用ScalarHandler类完成相应的操作。
(1) 在项目根目录下的cn.itcast.chapter10.example 包中创建名为ResultSetTest3的新类,默认情况下该类将用于演示scalar field value的方法。
package cn.itcast.chapter10.example;
import java.sql.SQLException;
import org.apache.commons.dbutils.handlers.ScalarHandler;
public class ResultSetTest3 {
public static void testScalarHandler() throws SQLException {
BaseDao basedao = new BaseDao();
String sql = "select * from user where id=?";
Object arr = (Object) basedao.query(sql,
new ScalarHandler("name"), 1);
System.out.println(arr);
}
public static void main(String[] args) throws SQLException {
testScalarHandler();
}
}
结果为:

三:任务十.使用DBUtils实现增删改查
于 chapter10 目录下的 src 包中 创建一个名为 cn.itcast.jdbc.utils 的新包,并在其内 生成一个 C3pOUtils 类。该类将专门用于构建数据源,请参阅以下代码段以获取具体实现细节。
于 chapter10 目录下的 src 包中 建立一个名为 cn.itcast.jdbc.utils 的新存储结构,并在其内 生成 C3pOUtils 类体。该类体将被设计用于构建数据源,请查阅以下代码段以了解其具体实现细节和功能模块分布。
package cn.itcast.jdbc.utils;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class C3p0Utils {
private static DataSource ds;
static {
ds = new ComboPooledDataSource();
}
public static DataSource getDataSource() {
return ds;
}
}
在项目chapter10的src目录中,我们构建了一个名为cn.itcast.jdbc.demo的包,并在其内部开发了一个DBUtilsDao类。该类旨在支持对user表的基本增删改查操作。具体代码如下:
package cn.itcast.jdbc.demo;
import java.sql.SQLException;
import java.util.List;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import cn.itcast.chapter10.example.User;
import cn.itcast.jdbc.utils.C3p0Utils;
public class DBUtilsDao {
// 查询所有,返回List集合
public List findAll() throws SQLException {
// 创建QueryRunner对象
QueryRunner runner = new QueryRunner(C3p0Utils.getDataSource());
// 写SQL语句
String sql = "select * from user";
// 调用方法
List list = (List) runner.query(sql,
new BeanListHandler(User.class));
return list;
}
// 查询单个,返回对象
public User find(int id) throws SQLException {
// 创建QueryRunner对象
QueryRunner runner = new QueryRunner(C3p0Utils.getDataSource());
// 写SQL语句
String sql = "select * from user where id=?";
// 调用方法
User user = (User) runner.query(sql,
new BeanHandler(User.class), new Object[] { id });
return user;
}
// 添加用户的操作
public Boolean insert(User user) throws SQLException {
// 创建QueryRunner对象
QueryRunner runner = new QueryRunner(C3p0Utils.getDataSource());
// 写SQL语句
String sql = "insert into user (name,password) values (?,?)";
// 调用方法
int num = runner.update(sql,
new Object[] { user.getName(), user.getPassword() });
if (num > 0)
return true;
return false;
}
// 修改用户的操作
public Boolean update(User user) throws SQLException {
// 创建QueryRunner对象
QueryRunner runner = new QueryRunner(C3p0Utils.getDataSource());
// 写SQL语句
String sql = "update user set name=?,password=? where id=?";
// 调用方法
int num = runner.update(sql, new Object[] { user.getName(),
user.getPassword(),user.getId() });
if (num > 0)
return true;
return false;
}
// 删除用户的操作
public Boolean delete(int id) throws SQLException {
// 创建QueryRunner对象
QueryRunner runner = new QueryRunner(C3p0Utils.getDataSource());
// 写SQL语句
String sql = "delete from user where id=?";
// 调用方法
int num = runner.update(sql, id);
if (num > 0)
return true;
return false;
}
}
实施增删改查操作的测试
package cn.itcast.jdbc.demo;
import java.sql.SQLException;
import cn.itcast.chapter10.example.User;
public class DBUtilsDaoTest1 {
private static DBUtilsDao dao = new DBUtilsDao();
public static void testInsert() throws SQLException {
User user = new User();
user.setName("zhaoliu");
user.setPassword("666666");
boolean b = dao.insert(user);
System.out.println(b);
}
public static void main(String[] args) throws SQLException {
testInsert();
}
}
(2)更新数据,在cn.itcast.jdbc.demo包目录下生成类DBUtilsDaoTest2用于测试增加操作。
package cn.itcast.jdbc.demo;
import java.sql.SQLException;
import cn.itcast.chapter10.example.User;
public class DBUtilsDaoTest2 {
private static DBUtilsDao dao = new DBUtilsDao();
public static void testupdate() throws SQLException {
User user = new User();
user.setName("zhaoliu");
user.setPassword("666777");
user.setId(4);
boolean b = dao.update(user);
System.out.println(b);
}
public static void main(String[] args) throws SQLException {
testupdate();
}
}
请在指定路径下清空现有数据,并在指定位置创建一个名为DBUtilsDaoTest3的测试类,用于对新增操作进行功能测试。
package cn.itcast.jdbc.demo;
import java.sql.SQLException;
public class DBUtilsDaoTest3 {
private static DBUtilsDao dao = new DBUtilsDao();
public static void testdelete() throws SQLException {
boolean b = dao.delete(4);
System.out.println(b);
}
public static void main(String[] args) throws SQLException {
testdelete();
}
}
(4)获取数据,并在指定的 JDBC 包中定义测试类DBUtilsDaoTest4以实现增删查改的基本功能测试。
package cn.itcast.jdbc.demo;
import java.sql.SQLException;
import cn.itcast.chapter10.example.User;
public class DBUtilsDaoTest4 {
private static DBUtilsDao dao = new DBUtilsDao();
public static void testfind() throws SQLException {
User user = dao.find(2);
System.out.println(user.getId() + "," + user.getName() + ","
+ user.getPassword());
}
public static void main(String[] args) throws SQLException {
testfind();
}
}
2020080605041
