数据库连接池与DBUtils工具
JDBC的基本用法和操作,每操作一次数据库,都会执行一次创建和断开Connection对象的操作,频繁的操作Connection对象十分影响数据库的访问效率,并且增加了代码量,所以在实际开发中,开发人员通常会使用数据库连接池来解决这些问题。Apache组织提供了一个DBUtils工具类库,该类库实现了对JDBC的简单封装,能在不影响数据库访问性能的情况下,极大地简化JDBC的编码工作。
01. 数据库连接池
在JDBC编程中,每次创建和断开Connection对象都会消耗一定的时间和IO资源。在Java程序与数据库之间建立连接时,数据库端要验证用户名和密码,并且要为这个连接分配资源,Java程序则要把代表连接的java.sql.Connection对象等加载到内存中,所以建立数据库连接的开销很大,尤其是在大量的并发访问时。假如某网站一天的访问量是10万,那么该网站的服务器就需要创建、断开连接10万次,频繁地创建、断开数据库连接会影响数据库的访问效率,甚至导致数据库崩溃。
为了避免频繁的创建数据库连接,数据库连接池技术应运而生。数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用现有的数据库连接,而不是重新建立。简单地说,数据库连接池就是为数据库建立的一个“缓冲池”。预先在“缓冲池”中放入一定数量的连接,当需要建立数据库连接时,只需要从“缓冲池”中取出一个,使用完毕后再放回“缓冲池”即可。
数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,当应用程序访问数据库时并不是直接创建Connection,而是向连接池“申请”一个Connection。如果连接池中有空闲的Connection,则返回一个链接给应用程序,否则应用程序需要创建新的Connection。使用完毕后,连接池会将Connection回收,重新放入连接池以供其他的线程使用,从而减少创建和断开数据库连接的次数,提高数据库的访问效率。
01. DataSource接口
为了获取数据库连接对象(Connection),JDBC提供了javax.sql.DataSource接口,javax.sql.DataSource接口负责
与数据库建立连接,并定义了返回值为Connection对象的方法,具体如下。
Connection getConnection() 。
Connection getConnection(String username, String password)。
注意:上述两个重载的方法,都能用来获取Connection对象。不同的是,第一个方法是通过无参的方式建立与数据库的连
接,第二个方法是通过传入登录信息的方式建立与数据库的连接。
接口通常都会有实现类,javax.sql.DataSource接口也不例外,通常习惯性的把实现了javax.sql.DataSource接口的类
称为数据源,顾名思义,数据源即数据的来源。每创建一个数据库连接,这个数据库连接信息都会存储到数据源中。
数据源用于管理数据库连接池。如果数据是水,数据库就是水库,数据库连接池就是连接到水库的管道,终端用户看到的数
据集是管道里流出来的水。一些开源组织提供了数据库连接池的独立实现,常用的有DBCP数据库连接池和C3P0数据库连接
池。
02. DBCP数据库连接池
DBCP即数据库连接池(DataBase Connection Pool),是Apache组织下的开源连接池实现,也是Tomcat服务器使用的连
接池组件。使用DBCP数据库连接池时,需要在应用程序中导入commons-dbcp2.jar和commons-pool2.jar两个JAR包。
commons-dbcp2.jar包是DBCP数据库连接池的实现包,包含所有操作数据库连接信息和数据库连接池初始化信息的方法,
并实现了DataSource接口的getConnection()方法。
commons-pool2.jar包是commons-dbcp2.jar包的依赖包,为commons-dbcp2.jar包中的方法提供了支持。缺少该依赖
包,commons-dbcp2.jar包中的很多方法就没有办法实现。
commons-dbcp2.jar包含两个核心类,分别是BasicDataSourceFactory和BasicDataSource,它们都包含获取DBCP数据库连接池对象的方法。
03. C3P0数据库连接池
C3P0是目前最流行的开源数据库连接池之一,它实现了DataSource数据源接口,支持JDBC2和JDBC3的标准规范,易于扩展
并且性能优越,著名的开源框架Hibernate和 Spring都支持该数据库连接池。在使用C3P0数据库连接池开发时,需要了解
C3P0中DataSource接口的实现类ComboPooledDataSource,它是C3P0的核心类,提供了数据源对象的相关方法。
package com.miao.jdbcutils;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbcp2.BasicDataSourceFactory;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.Properties;
/**
* \* Created with IntelliJ IDEA.
* \* User: maomao
* \* Date: 2022/3/31
* \* Time: 10:29
* \* Description:
* \
*/
public class Demo03 {
public static DataSource ds = null;
static {
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
// 设置数据库链接需要的配置信息
try {
comboPooledDataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
comboPooledDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/jdbc?serverTimezone=GMT%2B8");
comboPooledDataSource.setUser("root");
comboPooledDataSource.setPassword("123456");
// 设置连接池参数
comboPooledDataSource.setMaxPoolSize(10);
comboPooledDataSource.setInitialPoolSize(5);
ds = comboPooledDataSource;
} catch (PropertyVetoException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws SQLException {
System.out.println(ds.getConnection());
}
}
02. DBUtils工具
为了更加简单地使用JDBC,Apache组织提供了一个DBUtils工具,它是操作数据库的一个组件,实现了对JDBC的简单封
装,可以在不影响数据库访问性能的情况下简化JDBC的编码工作量。DBUtils工具要有三个作用。
写数据,DBUtils可以通过编写SQL语句对数据表进行增、删、改操作。
读数据,DBUtils工具可以将从数据表中读取的数据结果集转换成Java常用类集合,以方便对结果进行处理。
优化性能,在使用DBUtils工具的基础上,程序可以使用数据源、JNDI、数据库连接池等技术减少代码冗余。
DBUtils核心类库主要包括DBUtils类、QueryRunner类和ResultSetHandler接口。DBUtils工具主要通过这三个核心API
进行JDBC的所有操作。
01. DBUtils类
DBUtils类主要提供了加载JDBC驱动、关闭资源等方法,DBUtils类中的方法一般为静态方法,可以直接使用类名进行调
用。
方法名称 | 功能描述 |
---|---|
void close(Connection conn) | 当连接不为NULL时,关闭连接 |
void close(Statement stat) | 当声明不为NULL时,关闭声明 |
void close(ResultSet rs) | 当结果集不为NULL时,关闭结果集 |
void closeQuietly(Connection conn) | 当连接不为NULL时,关闭连接,并隐藏一些在程序中抛出的SQL异常 |
void closeQuietly(Statement stat) | 当声明不为NULL时,关闭声明,并隐藏一些在程序中抛出的SQL异常 |
void closeQuietly(ResultSet rs) | 当结果集不为NULL时,关闭结果集,并隐藏一些在程序中抛出的SQL异常 |
void commitAndCloseQuietly(Connection conn) | 提交连接后关闭连接,并隐藏一些在程序中抛出的SQL异常 |
Boolean loadDriver(String driveClassName) | 装载并注册JDBC驱动程序,如果成功就返回true |
02. QueryRunner类
QueryRunner类简化了执行SQL语句的代码,它与ResultSetHandler配合就能完成大部分的数据库操作,大大减少了编码
量。QueryRunner类提供了带有一个参数的构造方法,该方法以javax.sql.DataSource的实例对象作为参数传递到
QueryRunner的构造方法中来获取Connection对象。针对不同的数据库操作,QueryRunner类提供不同的方法。
方法名称 | 功能描述 |
---|---|
Object query(Connection conn,String sql,ResultSetHandler rsh,Object[] params) | 执行查询操作,传入的Connection对象不能为空 |
Object query (String sql, ResultSetHandler rsh,Object[] params) | 执行查询操作 |
Object query (Connection conn,String sql, ResultSetHandler rsh) | 执行一个不需要置换参数的查询操作 |
int update(Connection conn, String sql, ResultSetHandler rsh) | 执行一个更新(插入、删除、更新)操作 |
int update(Connection conn, String sql) | 执行一个不需要置换参数的更新操作 |
int batch(Connection conn,String sql, Object[] []params) | 批量添加、修改、删除 |
int batch(String sql, Object[][] params) | 批量添加、修改、删除 |
03. ResultSetHandler接口
ResultSetHandler接口用于处理ResultSet结果集,它可以将结果集中的数据转换为不同的形式。根据结果集中不同的数
据类型,ResultSetHandler提供了几种常见的实现类。
BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中。
BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,并存放到List里。
ColumnListHandler:将某列属性的值封装到List集合中。
ScalarHandler:将结果集中某一条记录的某一列数据存储成Object对象。
ResultSetHandler接口还提供了一个单独的方法handle (java.sql.ResultSet rs),如果上述实现类没有提供想要的功
能,可以自定义一个实现ResultSetHandler接口的类,然后通过重写handle()方法,实现结果集的处理。
04. ResultSetHandler实现类
BeanHandler和BeanListHandler实现类是将结果集中的数据封装到对应的JavaBean中。在封装时,表中数据的字段和
JavaBean的属性是相互对应的,一条数据记录被封装进一个对应的JavaBean对象中。BeanHandler和BeanListHandler的
对比如下表所示。
类名称 | 相同点 | 不同点 |
---|---|---|
BeanHandler | 都要先将结果集封装进JavaBean | 封装单条数据,把结果集的第一条数据的字段放入一个JavaBean中 |
BeanListHandler | 封装多条数据,把每条数据的字段值各放入一个JavaBean中,再把所有JavaBean都放入List集合中 |
package com.miao.jdbcutils;
import org.apache.commons.dbutils.ResultSetHandler;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* \* Created with IntelliJ IDEA.
* \* User: maomao
* \* Date: 2022/3/31
* \* Time: 11:28
* \* Description:
* \
*/
public class BaseDao {
// 优化查询
public static Object query(String sql, ResultSetHandler<?> rsh,
Object... params) throws SQLException {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = 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();
// 让调用者去实现对结果集的处理
Object obj = rsh.handle(rs);
return obj;
} catch (Exception e) {
e.printStackTrace();
}finally {
// 释放资源
JDBCUtils.release(rs, pstmt, conn);
}
return rs;
}
}
package com.miao.jdbcutils;
import java.sql.*;
/**
* \* Created with IntelliJ IDEA.
* \* User: maomao
* \* Date: 2022/3/31
* \* Time: 11:33
* \* Description:
* \
*/
public class JDBCUtils {
// 加载驱动,并建立数据库连接
public static Connection getConnection() throws SQLException,
ClassNotFoundException {
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/jdbc?serverTimezone=GMT%2B8";
String username = "root";
String password = "123456";
Connection conn = DriverManager.getConnection(url, username,
password);
return conn;
}
// 关闭数据库连接,释放资源
public static void release(Statement stmt, Connection conn) {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
public static void release(ResultSet rs, Statement stmt,
Connection conn){
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs = null;
}
release(stmt, conn);
}
}
package com.miao.pojo;
import java.io.Serializable;
public class User implements Serializable {
private String name;
private String sex;
private int age;
public User() {
}
public User(String name, String sex, int age) {
this.name = name;
this.sex = sex;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
'}';
}
}
package com.miao.jdbcutils;
import com.miao.pojo.User;
import org.apache.commons.dbutils.handlers.BeanHandler;
import java.sql.SQLException;
/**
* \* Created with IntelliJ IDEA.
* \* User: maomao
* \* Date: 2022/3/31
* \* Time: 11:44
* \* Description:
* \
*/
public class ResultTest01 {
public static void testBeanHandler() throws SQLException {
BaseDao basedao = new BaseDao();
String sql = "select * from users where id=?";
User user = (User) basedao.query(sql,
new BeanHandler(User.class), 1);
System.out.print("id为1的User对象的name值为:" + user.getName());
}
public static void main(String[] args) throws SQLException {
testBeanHandler();
}
}
package com.miao.jdbcutils;
import com.miao.pojo.User;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import java.sql.SQLException;
import java.util.ArrayList;
/**
* \* Created with IntelliJ IDEA.
* \* User: maomao
* \* Date: 2022/3/31
* \* Time: 11:44
* \* Description:
* \
*/
public class ResultTest02 {
public static void testBeanListHandler() throws SQLException {
BaseDao basedao = new BaseDao();
String sql = "select * from users ";
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();
}
}
package com.miao.jdbcutils;
import com.miao.pojo.User;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ColumnListHandler;
import java.sql.SQLException;
import java.util.ArrayList;
/**
* \* Created with IntelliJ IDEA.
* \* User: maomao
* \* Date: 2022/3/31
* \* Time: 11:44
* \* Description:
* \
*/
public class ResultTest03 {
public static void testColumnListHandler() throws SQLException {
BaseDao basedao = new BaseDao();
String sql = "select * from users";
Object arr = (Object) basedao.query(sql,
new ColumnListHandler("name"));
System.out.println(arr);
}
public static void main(String[] args) throws SQLException {
testColumnListHandler();
}
}
package com.miao.jdbcutils;
import org.apache.commons.dbutils.handlers.ColumnListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import java.sql.SQLException;
/**
* \* Created with IntelliJ IDEA.
* \* User: maomao
* \* Date: 2022/3/31
* \* Time: 11:44
* \* Description:
* \
*/
public class ResultTest04 {
public static void testScalarHandler() throws SQLException {
BaseDao basedao = new BaseDao();
String sql = "select * from users 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();
}
}