Mysql——DbUtils的使用
一、JDBC复习
作业:在 MySQL 中,创建以下数据表,使用 JDBC 连接 MySQL 数据库。同时,实现数据表的增、删、改、查(根据ID查询、分页查询)操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | drop database if exists jdbc_test;
create database if not exists jdbc_test ;
use jdbc_test;
create table `staffs`(
`id` int(10) primary key auto_increment,
`name` varchar(24) not null comment '姓名',
`age` int(10) default 18 comment '年龄',
`phone` char(11) comment '联系方式',
`sta_pos` varchar(20) comment '职位',
`add_time` datetime default current_timestamp comment '入职时间',
`update_time` datetime default current_timestamp comment '更新时间'
) engine=innodb charset=utf8 comment '员工记录表';
# 提示:需要自行下载连接MySQL的驱动程序
insert into staffs(name,age,phone,sta_pos) values ('张三',18,'13417747371','工程师');
insert into staffs(name,age,phone,sta_pos) values ('李四',19,'13417747372','工程师');
insert into staffs(name,age,phone,sta_pos) values ('王五',20,'13417747373','教授');
select * from staffs;
|
1、概念
JDBC(Java DataBase Connectity)是Java数据库连接技术的简称,提供连接各种常用数据库的能力;
它提供了操作数据库的接口(规范、标准)。JDBC实现由各数据库厂商提供(驱动程序);
所在包:java.sql
作用:用于连接 + 操作数据库
2、JDBC原理

DriverManager
提供者:sun公司
作用:载入各种不同的JDBC驱动
JDBC 驱动
提供者:数据库厂商
作用:负责连接各种不同的数据库
JDBC<躯壳> VS 驱动程序<灵魂>
3、JDBC的常用对象(API)

1)DriverManager
驱动管理器,管理驱动程序 – 载入各种不同的JDBC驱动程序
2)Connection
连接对象,实现数据库的连接并担任传送数据的任务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | package org.zing.util;
import java.sql.*;
/**
* 使用 JDBC 连接 MySQL 数据库
*
* @Author zqx
* @Date 2023-03-05
*/
public class DbUtil {
/**
* 连接驱动程序
*/
private static final String DRIVER = "com.mysql.cj.jdbc.Driver";
/**
* 连接URL
*/
private static final String URL = "jdbc:mysql://127.0.0.1:3306/jdbc_test?useUnicode=true;characterEncoding=utf8;serverTimezone=Asia/Shanghai";
/**
* 帐号
*/
private static final String USER = "root";
/**
* 密码
*/
private static final String PASS = "root";
static {
/*
* 加载驱动程序
*/
try {
Class.forName(DRIVER);
} catch (ClassNotFoundException e) {
System.out.println("加载驱动程序失败...");
e.printStackTrace();
}
}
/**
* 获取连接对象 -- Java程序 与 数据库之间的桥梁
*
* @return
*/
public static Connection getConnection() {
Connection conn = null;
try {
conn = DriverManager.getConnection(URL, USER, PASS);
} catch (SQLException e) {
System.out.println("获取连接对象失败...");
e.printStackTrace();
}
return conn;
}
/**
* 关闭相关的 JDBC 对象
* <p>
* DriverManager:驱动管理对象,获取连接对象
* <p>
* DriverManager.getConnection(URL, USER, PASS);
* <p>
* ResultSet:结果集对象 用于接收查询数据时,返回的结果
* <p>
* Statement:语句对象 用于执行SQL语句(PreparedStatement、CallableStatement)
* 增、删、改:executeUpdate() 查询:executeQuery()
* <p>
* <p>
* Connection:连接对象 建立JAVA程序与数据库之间的桥梁
*
* @param rst
* @param stmt 父类对象可以接收子类对象 - 多态
* @param conn
*/
public static void close(ResultSet rst, Statement stmt, Connection conn) {
if (rst != null) {
try {
rst.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
System.out.println(DbUtil.getConnection());
}
}
|
3)Statement
语句对象,由 Connection 产生、负责执行 SQL 语句
3.1)Statement
1 | Statement stmt = conn.createStatement() ; |
3.2)PreparedStatement
效率高、安全性高、方便
1 2 3 4 5 6 7 8 9 10 | //定义操作数据库的SQL语句->数据由?来占位 String sql = "select * from student where id=?"; //预编译SQL语句 PreparedStatement pstmt = conn.prepareStatement(sql); //填充数据,语法如下: // 语句对象.setXxx(占位符索引,数据) -> 从1开始 // Xxx:指是数据类型->Java的数据类型 pstmt.setInt(1, 5); |
3.3)CallableStatement
用于执行存储过程
4、ResultSet
结果集对象,负责保存语句对象执行后所产生的查询结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | ResultSet rst = stmt.executeQuery(sql) ;
//ResultSet rst = pstmt.executeQuery();
//1.循环迭代各行记录
while(rst.next()) {
//2.获取每行中,各字段的数据,语法如下:
//方法一:结果集对象.getXxx(字段索引) -> 从1开始,且是根据select后的字段来获取
//方法二:结果集对象.getXxx(字段名称)
//注:Xxx指的是字段的数据类型 -> 必须对应Java的数据类型
String stuNO = rst.getString(1) ;
String cardID = rst.getString(3) ;
//int id = rst.getInt("id") ;
//String name = rst.getString("name") ;
System.out.println(stuNO+"\t"+cardID);
}
|
5、ResultSetMetaData
元数据对象,可用于获取有关ResultSet对象中列的类型和属性的信息的对象。
1 2 3 4 5 6 | // 1.实例化对象:结果集对象.getMetaData() ; ResultSetMetaData metaData = rst.getMetaData() ; // 2.常用方法 // 2.1 结果集中包含列的数量:int getColumnCount(): // 2.2获取指定列的别名,索引从1开始:String getColumnLabel(int column) |
二、DbUtils
commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。因此dbutils成为很多不喜欢hibernate、MyBatis框架的首选。
commons-dbutils API:
- org.apache.commons.dbutils.QueryRunner:提供对sql语句操作的API
- org.apache.commons.dbutils.ResultSetHandler:用于定义select操作后,怎样封装结果集
- org.apache.commons.dbutils.DbUtils:工具类,定义了关闭资源与事务处理的方法
三、QueryRunner
该类简单化了SQL查询,它与ResultSetHandler组合在一起使用可以完成大部分的数据库操作,能够大大减少编码量。
QueryRunner类提供了两个构造方法:
- 默认的构造方法
- 需要一个 javax.sql.DataSource 来作参数的构造方法
常用方法
- public Object query(Connection conn, String sql, Object[] params, ResultSetHandler rsh) throws SQLException:执行一个查询操作,在这个查询中,对象数组中的每个元素值被用来作为查询语句的置换参数。该方法会自行处理 PreparedStatement 和 ResultSet 的创建和关闭。
- public Object query(String sql, Object[] params, ResultSetHandler rsh) throws SQLException: 几乎与第一种方法一样;唯一的不同在于它不将数据库连接提供给方法,并且它是从提供给构造方法的数据源(DataSource) 或使用的setDataSource 方法中重新获得 Connection。
- public Object query(Connection conn, String sql, ResultSetHandler rsh) throws SQLException : 执行一个不需要置换参数的查询操作。
- public int update(Connection conn, String sql, Object[] params) throws SQLException:用来执行一个更新(插入、更新或删除)操作。
- public int update(Connection conn, String sql) throws SQLException:用来执行一个不需要置换参数的更新操作。
四、ResultSetHandler
1.作用
该接口用于处理java.sql.ResultSet,将数据按要求转换为另一种形式。
2.ResultSetHandler接口的实现类
- ArrayHandler:把结果集中的第一行数据转成对象数组。
- ArrayListHandler:把结果集中的每一行数据都转成一个数组,再存放到List中。
- BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中。
- BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。
- ColumnListHandler:将结果集中某一列的数据存放到List中。
- KeyedHandler(name):将结果集中的每一行数据都封装到一个Map里,再把这些map再存到一个map里,其key为指定的key。
- MapHandler:将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。
- MapListHandler:将结果集中的每一行数据都封装到一个Map里,然后再存放到List
- ScalarHandler:它是用于单数据。如果:select count(*) from 表操作。
五、数据库连接池
数据库连接池是一种技术,用于管理应用程序与数据库之间的连接。连接池可以有效地减少数据库连接的开销,提高应用程序的性能和可伸缩性。
六、开源的数据源使用
1、DBCP
略
2、C3P0
作者使用了自己喜欢的一个机器人的代号命名
1)使用方法一
第一、引入jar包:c3p0-0.9.5.jar以及依赖的mchange-commons-java-0.2.9.jar
第二、创建ComboPooledDataSource对象
1 | private static ComboPooledDataSource cpds = new ComboPooledDataSource(); |
第三、设置必须的属性
1 2 3 4 | cpds.setDriverClass("com.microsoft.sqlserver.jdbc.SQLServerDriver");
cpds.setJdbcUrl("jdbc:sqlserver://localhost:1433;database=jdbc");
cpds.setUser("sa");
cpds.setPassword("123456");
|
第四、根据情况,设置可选的属性
第五、获取连接对象
1 | Connection conn = ds.getConnection() ; |
第六、关闭连接对象
1 | ds.close() ; |
2)使用方法二
第一、引入jar包:c3p0-0.9.5.jar以及依赖的mchange-commons-java-0.2.9.jar
第二、配置c3p0-config.xml或c3p0.properties文件(存放在构建路径)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | <?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<!-- 默认配置 -->
<default-config>
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/jdbc_test?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai</property>
<property name="user">root</property>
<property name="password">root</property>
<!-- 连接池初始化时创建的连接数 -->
<property name="initialPoolSize">10</property>
<!-- 连接池保持的最小连接数 -->
<property name="minPoolSize">10</property>
<!-- 连接池中拥有的最大连接数 -->
<property name="maxPoolSize">100</property>
<!-- 连接的最大空闲时间,如果超过这个时间,某个数据库连接还没有被使用,则会断开掉这个连接 -->
<property name="maxIdleTime">30</property>
<!-- 连接池用完,等待时间,设置0无限等待 -->
<property name="checkoutTimeout">30000</property>
<!-- 测试空闲连接的间隔时间 -->
<property name="idleConnectionTestPeriod">30</property>
<!-- 连接池为数据源缓存的PreparedStatement的总数 -->
<property name="maxStatements">200</property>
</default-config>
<!-- 功能同上,配置另一种情况。需要指定一个名称加以区分:other -->
<named-config name="other">
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc_test?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai</property>
<property name="user">root</property>
<property name="password">root</property>
<!-- 连接池初始化时创建的连接数 -->
<property name="initialPoolSize">10</property>
<!-- 连接池保持的最小连接数 -->
<property name="minPoolSize">10</property>
<!-- 连接池中拥有的最大连接数 -->
<property name="maxPoolSize">100</property>
<!-- 连接的最大空闲时间,如果超过这个时间,某个数据库连接还没有被使用,则会断开掉这个连接 -->
<property name="maxIdleTime">30</property>
<!-- 连接池用完,等待时间,设置0无限等待 -->
<property name="checkoutTimeout">30000</property>
<!-- 测试空闲连接的间隔时间 -->
<property name="idleConnectionTestPeriod">30</property>
<!-- 连接池为数据源缓存的PreparedStatement的总数 -->
<property name="maxStatements">200</property>
</named-config>
</c3p0-config>
|
第三、创建ComboPooledDataSource对象,并指定配置参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public class C3P0Util {
/**
* 定义C3P0数据源对象 - 使用默认配置创建ComboPooledDataSource对象
*/
private static ComboPooledDataSource cpds = new ComboPooledDataSource();
// 使用某配置创建ComboPooledDataSource对象
// private static ComboPooledDataSource cpds = new ComboPooledDataSource("other");
public static DataSource getDataSource() {
return cpds;
}
public static Connection getConnection() {
try {
return cpds.getConnection();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
|
第四、获取连接对象
1 | Connection conn = ds.getConnection() ; |
第五、关闭连接对象
1 | ds.close() ; |
3、Druid
https://github.com/alibaba/druid?tab=readme-ov-file
Druid是什么
Druid是Java语言中最好的数据库连接池。Druid能够提供强大的监控和扩展功能。
下载
https://mvnrepository.com/
1 2 3 4 5 | <dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid-version}</version>
</dependency>
|
1)使用方法一
第一、引入jar包
第二、创建ComboPooledDataSource对象
1 | private static DruidDataSource dataSource = new DruidDataSource(); |
第三、设置必须的属性
1 2 3 4 | dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/jdbc_test?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai");
dataSource.setUsername("root");
dataSource.setPassword("root");
|
第四、根据情况,设置可选的属性
第五、获取连接对象
1 | Connection conn = dataSource.getConnection() ; |
第六、关闭连接对象
1 | conn.close() ; |
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | package org.zing.util;
import com.alibaba.druid.pool.DruidDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
/**
* @author zqx
* @date 2025-02-24
*/
public class DruidHelper {
/**
* 数据源对象
*/
private static DruidDataSource dataSource;
/**
* 获取数据源对象
*/
public static DataSource getDataSource() {
if (dataSource == null) {
initDataSource();
}
return dataSource;
}
/**
* 初始化数据源
*/
private static void initDataSource() {
try {
// 实例化数据源对象
dataSource = new DruidDataSource();
// 基础配置
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/jdbc_test?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai");
dataSource.setUsername("root");
dataSource.setPassword("root");
// 初始化连接数量
dataSource.setInitialSize(10);
// 最小连接数
dataSource.setMinIdle(10);
// 最大等待时间
dataSource.setMaxWait(6000);
// 最大连接数
dataSource.setMaxActive(100);
// 连接有效性检测SQL
dataSource.setValidationQuery("SELECT 1");
// 空闲时检测连接有效性
dataSource.setTestWhileIdle(true);
} catch (Exception e) {
throw new RuntimeException("初始化Druid连接池失败", e);
}
}
/**
* 从连接池获取连接对象
*/
public static Connection getConnection() {
try {
return getDataSource().getConnection();
} catch (SQLException e) {
throw new RuntimeException("获取连接对象失败", e);
}
}
/**
* 关闭连接,回收到连接池
*
* @param conn 连接对象
*/
public static void close(Connection conn) {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
System.err.println("关闭数据库连接时发生异常");
}
}
}
}
|
2)使用方法二
第一、引入jar包
第二、创建配置文件 druid.properties 文件(存放在构建路径)
1 2 3 4 5 6 7 8 9 10 11 12 | driverClassName=com.mysql.cj.jdbc.Driver url=jdbc:mysql://127.0.0.1:3306/jdbc_test?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username=root password=root # 初始化连接数量 initialSize=10 # 最小连接数 minIdle=10 # 最大连接数 maxActive=100 # 最大等待时间 maxWait=6000 |
第三、创建 DataSource 对象,并指定配置参数
1 2 3 | Properties prop = new Properties();
prop.load(DruidUtil.class.getClassLoader().getResourceAsStream("druid.properties"));
DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
|
第四、获取连接对象
1 | Connection conn = dataSource.getConnection() ; |
第五、关闭连接对象
1 | conn.close() ; |
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | package org.zing.util;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
/**
* @author zqx
* @date 2025-02-24
*/
public class DruidUtil {
/**
* 数据源对象
*/
private static DataSource dataSource;
/**
* 获取数据源对象
*
* @return 数据源对象
*/
public static DataSource getDataSource() {
if (dataSource == null) {
initDataSource();
}
return dataSource;
}
/**
* 初始化数据源对象
*/
private static void initDataSource() {
Properties prop = new Properties();
try {
prop.load(DruidUtil.class.getClassLoader().getResourceAsStream("druid.properties"));
dataSource = DruidDataSourceFactory.createDataSource(prop);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 从连接池获取连接对象
*
* @return 连接对象
*/
public static Connection getConnection() {
try {
return getDataSource().getConnection();
} catch (SQLException e) {
throw new RuntimeException("获取连接对象失败", e);
}
}
/**
* 关闭连接,回收到连接池
*
* @param conn 连接对象
*/
public static void close(Connection conn) {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
System.err.println("关闭数据库连接时发生异常");
}
}
}
}
|
七、DbUtils 增、删、改、查操作
第一:通过C3P0/DBCP获取数据源对象
告知DbUtils操作哪个服务中,哪个端口号的哪个数据库 - 连接池
第二:创建QueryRunner对象
1 | QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()) ; |
第三:定义要操作的SQL语句
1 | String sql = "select id,name,age,phone,pos,add_time as addTime from staffs" ; |
注意:使用别名解决实体对象属性名和数据表字段名不一致的问题
内省
第四:定义SQL语句需要的具体数据
1 | Object[] params = new Object[]{数据1,数据2,...} ;
|
第五:执行SQL语句 —— 反射、封装处理
- 查询操作:query(…)
- 更新操作:update(…)
1 2 | List<Staff> list = qr.query(sql, new BeanListHandler<Staff>(Staff.class)); Staff staff = qr.query(sql,new BeanHandler<>(Staff.class),id); |
八、日期时间问题
1、JDBC 类型映射机制
数据库中的时间类型(如 DATETIME、TIMESTAMP)通过 JDBC 驱动转换为 Java 类型时,不同版本的驱动处理方式不同:
- MySQL 5.x 驱动:默认将
DATETIME映射为java.sql.Timestamp(继承自java.util.Date) - MySQL 8.x 驱动:支持 JDBC 4.2+,可能直接映射为
java.time.LocalDateTime
当使用 MySQL 8.x 驱动时:
- 数据库
DATETIME→ JDBC 返回LocalDateTime BeanProcessor尝试将LocalDateTime赋值给java.util.Date类型属性- 由于无默认转换器,抛出类型不匹配异常
2、解决
1)方案一
使用驱动统一的 java.time 类型
1 2 3 4 5 | public class Staff {
// 使用 java.time 类型
private LocalDateTime createTime;
// getter/setter
}
|
2)方案二
第一:自定义 BeanProcessor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | package org.zing.dao;
import org.apache.commons.dbutils.BeanProcessor;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
/**
* 自定义 BeanProcessor - 实现 LocalDateTime 转换 java.util.Date
*
* @author zqx
* @date 2025-02-25
*/
public class CustomBeanProcessor extends BeanProcessor {
@Override
protected Object processColumn(ResultSet resultSet, int index, Class<?> propType)
throws SQLException {
// 获取某字段的数据
Object value = super.processColumn(resultSet, index, propType);
// 处理 LocalDateTime → Date 的转换
if (value instanceof LocalDateTime localDateTime && propType == Date.class) {
return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
}
// 其它类型的转换...
return value;
}
}
|
第二:通过 RowProcessor 注入自定义处理器
1 2 3 4 5 6 7 8 | // 创建自定义 RowProcessor
BasicRowProcessor rowProcessor = new BasicRowProcessor(new CustomBeanProcessor());
// 创建 BeanListHandler 时传入自定义处理器
BeanListHandler<Staff> handler = new BeanListHandler<>(Staff.class, rowProcessor);
// 执行查询
List<Staff> list = runner.query("select * from staffs", handler);
|
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | public List<Staff> selectByPage(int limit, int count) {
String sql = "select id,name,age,phone,sta_pos as staPos,add_time as addTime,update_time as updateTime from staffs limit ?,?";
List<Staff> list = null;
try {
// ResultSetHandler VS BeanListHandler
// ResultSetHandler 结果集处理器,通过实现此接口来对结果集数据进行处理
// BeanListHandler 是 ResultSetHandler 接口的其中一个实现类,返回实体对象的List集合
Object[] params = {limit, count};
// 创建自定义 RowProcessor
BasicRowProcessor rowProcessor = new BasicRowProcessor(new CustomBeanProcessor());
// 创建 BeanListHandler 时传入自定义处理器
BeanListHandler<Staff> handler = new BeanListHandler<>(Staff.class, rowProcessor);
// 执行查询
list = qr.query(sql, handler, params);
} catch (SQLException e) {
throw new RuntimeException(e);
}
return list;
} |
