当前位置: 首页 > news >正文

JDBC基础(2)

JDBC基础(2)

作者:没有四次元口袋的蓝胖
日期:2026-06-29
标签:Java, JDBC, 工具类


一、为什么要封装工具类?

原生 JDBC 代码有很多重复:

  • 每次都要写注册驱动、获取连接
  • 每次都要写繁琐的释放资源
  • 连接参数硬编码在代码里,修改不方便

封装 JDBCUtils 的目的:减少重复代码,提高开发效率。


二、JDBCUtils 工具类的编写

2.1 配置文件(jdbc.properties)

把连接参数放到配置文件里,方便修改:

# jdbc.properties driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/db_name?useSSL=false&serverTimezone=UTC&characterEncoding=utf8 username=root password=123456

2.2 工具类代码

importjava.io.IOException;importjava.io.InputStream;importjava.sql.*;importjava.util.Properties;/** * JDBC 工具类 * 功能:获取连接、释放资源 */publicclassJDBCUtils{privatestaticStringdriver;privatestaticStringurl;privatestaticStringusername;privatestaticStringpassword;// 静态代码块:类加载时读取配置文件、注册驱动,只执行一次static{try{// 1. 读取配置文件Propertiesprop=newProperties();// 使用类加载器读取 src 下的配置文件InputStreamis=JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");prop.load(is);// 2. 获取参数driver=prop.getProperty("driver");url=prop.getProperty("url");username=prop.getProperty("username");password=prop.getProperty("password");// 3. 注册驱动Class.forName(driver);}catch(IOException|ClassNotFoundExceptione){thrownewRuntimeException("JDBC 配置文件加载失败",e);}}/** * 获取连接 */publicstaticConnectiongetConnection()throwsSQLException{returnDriverManager.getConnection(url,username,password);}/** * 释放资源(增删改用,无 ResultSet) */publicstaticvoidclose(Connectionconn,Statementstmt){close(null,stmt,conn);}/** * 释放资源(查询用,有 ResultSet) */publicstaticvoidclose(ResultSetrs,Statementstmt,Connectionconn){if(rs!=null){try{rs.close();}catch(SQLExceptione){e.printStackTrace();}}if(stmt!=null){try{stmt.close();}catch(SQLExceptione){e.printStackTrace();}}if(conn!=null){try{conn.close();}catch(SQLExceptione){e.printStackTrace();}}}}

2.3 使用工具类

// 查询示例publicStudentfindById(intid){Connectionconn=null;PreparedStatementpstmt=null;ResultSetrs=null;try{conn=JDBCUtils.getConnection();Stringsql="SELECT * FROM student WHERE id = ?";pstmt=conn.prepareStatement(sql);pstmt.setInt(1,id);rs=pstmt.executeQuery();Studentstu=null;if(rs.next()){stu=newStudent();stu.setId(rs.getInt("id"));stu.setName(rs.getString("name"));stu.setAge(rs.getInt("age"));}returnstu;}catch(SQLExceptione){e.printStackTrace();returnnull;}finally{JDBCUtils.close(rs,pstmt,conn);}}

工具类的好处:

  • 连接参数统一管理,修改方便
  • 获取连接和释放资源不用重复写
  • 代码更简洁

2.4 通用增删改方法

可以进一步封装通用的增删改方法:

/** * 通用增删改方法 * @param sql SQL 语句 * @param args 参数(可变参数,对应 ? 的值) * @return 影响的行数 */publicstaticintupdate(Stringsql,Object...args){Connectionconn=null;PreparedStatementpstmt=null;try{conn=getConnection();pstmt=conn.prepareStatement(sql);// 设置参数for(inti=0;i<args.length;i++){pstmt.setObject(i+1,args[i]);}returnpstmt.executeUpdate();}catch(SQLExceptione){e.printStackTrace();return0;}finally{close(null,pstmt,conn);}}

使用:

// 增JDBCUtils.update("INSERT INTO student(name, age) VALUES(?, ?)","王五",23);// 删JDBCUtils.update("DELETE FROM student WHERE id = ?",3);// 改JDBCUtils.update("UPDATE student SET age = ? WHERE name = ?",25,"王五");

2.5 通用查询方法(返回集合)

/** * 通用查询方法 * @param sql SQL 语句 * @param handler 结果集处理器(自己定义接口) * @param args 参数 */publicstatic<T>Tquery(Stringsql,ResultSetHandler<T>handler,Object...args){Connectionconn=null;PreparedStatementpstmt=null;ResultSetrs=null;try{conn=getConnection();pstmt=conn.prepareStatement(sql);for(inti=0;i<args.length;i++){pstmt.setObject(i+1,args[i]);}rs=pstmt.executeQuery();returnhandler.handle(rs);// 交给处理器处理结果集}catch(SQLExceptione){e.printStackTrace();returnnull;}finally{close(rs,pstmt,conn);}}// 结果集处理器接口publicinterfaceResultSetHandler<T>{Thandle(ResultSetrs)throwsSQLException;}// 把结果集封装成 List<Student> 的处理器publicclassBeanListHandler<T>implementsResultSetHandler<List<T>>{privateClass<T>clazz;publicBeanListHandler(Class<T>clazz){this.clazz=clazz;}@OverridepublicList<T>handle(ResultSetrs)throwsSQLException{List<T>list=newArrayList<>();ResultSetMetaDatametaData=rs.getMetaData();intcolumnCount=metaData.getColumnCount();while(rs.next()){try{Tobj=clazz.newInstance();for(inti=1;i<=columnCount;i++){StringcolumnName=metaData.getColumnName(i);Objectvalue=rs.getObject(i);// 反射给字段赋值Fieldfield=clazz.getDeclaredField(columnName);field.setAccessible(true);field.set(obj,value);}list.add(obj);}catch(Exceptione){e.printStackTrace();}}returnlist;}}

使用:

List<Student>list=JDBCUtils.query("SELECT * FROM student WHERE age > ?",newBeanListHandler<>(Student.class),18);

三、Apache DbUtils 组件

手写通用查询比较麻烦,Apache 提供了成熟的工具类commons-dbutils,它已经帮我们封装好了通用的增删改查和结果集处理器。

3.1 引入依赖

Maven 项目在pom.xml中添加:

<dependency><groupId>commons-dbutils</groupId><artifactId>commons-dbutils</artifactId><version>1.7</version></dependency>

普通 Java 项目直接导入 jar 包即可:commons-dbutils-1.7.jar

3.2 QueryRunner —— 核心类

QueryRunner 是 DbUtils 的核心操作类,它提供了增删改查的方法。

importorg.apache.commons.dbutils.QueryRunner;importorg.apache.commons.dbutils.handlers.*;// 创建 QueryRunner 对象QueryRunnerqr=newQueryRunner();

3.3 增删改(update)

// 增StringinsertSql="INSERT INTO student(name, age) VALUES(?, ?)";introws=qr.update(conn,insertSql,"赵六",24);// 删StringdeleteSql="DELETE FROM student WHERE id = ?";introws2=qr.update(conn,deleteSql,1);// 改StringupdateSql="UPDATE student SET age = ? WHERE name = ?";introws3=qr.update(conn,updateSql,26,"赵六");

如果传入DataSource(连接池)给 QueryRunner 构造方法,就不用每次传 Connection 了。

3.4 查询 —— ResultSetHandler 八大实现类

实现类作用
BeanHandler<T>将结果集第一行封装为一个 JavaBean 对象
BeanListHandler<T>将结果集所有行封装为 List
ScalarHandler将结果集第一行第一列封装为 Object(用于统计查询 count/sum 等)
MapHandler将结果集第一行封装为 Map(key=列名, value=值)
MapListHandler将结果集所有行封装为 List
ArrayHandler将结果集第一行封装为 Object[]
ArrayListHandler将结果集所有行封装为 List<Object[]>
ColumnListHandler将结果集某一列封装为 List

3.5 查询一个对象(BeanHandler)

Stringsql="SELECT * FROM student WHERE id = ?";Studentstu=qr.query(conn,sql,newBeanHandler<>(Student.class),1);System.out.println(stu);

3.6 查询集合(BeanListHandler)

Stringsql="SELECT * FROM student WHERE age > ?";List<Student>list=qr.query(conn,sql,newBeanListHandler<>(Student.class),18);for(Students:list){System.out.println(s);}

3.7 统计查询(ScalarHandler)

Stringsql="SELECT COUNT(*) FROM student";Longcount=qr.query(conn,sql,newScalarHandler<>());System.out.println("总记录数:"+count);

注意:SELECT COUNT(*)返回的是Long类型,不是int,要用Long接收。

3.8 查询 Map 集合

// 查询一条 → MapMap<String,Object>map=qr.query(conn,"SELECT * FROM student WHERE id = ?",newMapHandler(),1);// 查询多条 → List<Map>List<Map<String,Object>>mapList=qr.query(conn,"SELECT * FROM student",newMapListHandler());

3.9 自定义 ResultSetHandler

如果内置的处理器满足不了需求,可以自定义:

// 自定义:只取姓名列List<String>names=qr.query(conn,"SELECT name FROM student",newResultSetHandler<List<String>>(){@OverridepublicList<String>handle(ResultSetrs)throwsSQLException{List<String>list=newArrayList<>();while(rs.next()){list.add(rs.getString("name"));}returnlist;}});

3.10 DbUtils 工具类

DbUtils 还提供了一个DbUtils类,用于释放资源,比自己写的更简洁:

importorg.apache.commons.dbutils.DbUtils;// 关闭连接DbUtils.close(conn);DbUtils.closeQuietly(conn);// 安静关闭,不抛异常// 关闭多个DbUtils.closeQuietly(conn,stmt,rs);

四、封装实体类的注意事项

使用 BeanHandler / BeanListHandler 时,JavaBean 必须满足:

  1. 私有字段:成员变量私有
  2. 无参构造:必须提供无参构造方法(反射要用到)
  3. getter/setter:提供所有字段的 getter 和 setter 方法
  4. 字段名对应:数据库列名要和 JavaBean 属性名一致(或用别名对应)
publicclassStudent{privateintid;privateStringname;privateintage;// 必须有无参构造publicStudent(){}// getter 和 setterpublicintgetId(){returnid;}publicvoidsetId(intid){this.id=id;}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicintgetAge(){returnage;}publicvoidsetAge(intage){this.age=age;}}

列名和属性名不一致怎么办?用 SQL 别名:

SELECTuser_idASuserId,user_nameASuserNameFROMuser

五、思维导图速览

JDBC 进阶 ├── JDBCUtils 工具类 │ ├── 配置文件 jdbc.properties │ ├── 静态代码块加载配置、注册驱动 │ ├── getConnection() 获取连接 │ ├── close() 释放资源(方法重载) │ ├── 通用 update(增删改) │ └── 通用 query + ResultSetHandler ├── Apache DbUtils ✅ │ ├── QueryRunner(核心操作类) │ │ ├── update(增删改) │ │ └── query(查询) │ ├── ResultSetHandler 八大实现 │ │ ├── BeanHandler → 一个对象 │ │ ├── BeanListHandler → List集合 │ │ ├── ScalarHandler → 单个值(统计) │ │ ├── MapHandler / MapListHandler │ │ ├── ArrayHandler / ArrayListHandler │ │ └── ColumnListHandler │ └── DbUtils 类(释放资源) └── JavaBean 要求 ├── 私有字段 ├── 无参构造 ├── getter/setter └── 列名与属性名对应

六、写在最后

  1. JDBCUtils 必须自己手写一遍:理解工具类的封装思想,这是面试常考的"手写 JDBC 工具类"
  2. DbUtils 要会用:实际开发用它比自己写通用方法方便得多
  3. BeanHandler / BeanListHandler 是重点:最常用的两个结果集处理器
  4. ScalarHandler 注意返回类型:count 返回 Long,别用 int 接

常见面试点

  • 手写 JDBC 工具类
  • PreparedStatement 和 Statement 的区别(SQL 注入)
  • ResultSetHandler 的实现类有哪些
  • JavaBean 封装的要求
http://www.gsyq.cn/news/1610673.html

相关文章:

  • 想提升用户体验?快把HTML5视频播放器代码嵌入你的网站
  • 抖音视频下载神器:轻松保存无水印高清内容
  • PC大型3A 角色扮演游戏(RPG)《怪物猎人物语3:命运双龙》网盘下载 免BIOS 中文版
  • 阿里云图像搜索完整对接指南:从开通到API/SDK深度集成
  • G-Helper:华硕笔记本的轻量级控制中心,三步告别臃肿系统
  • Mac 新手必装工具清单:从效率、安全到清理维护的完整指南(2026 更新版)
  • LabVIEW VISA异步I/O提升吞吐量
  • puzzle(1131)指路罗马
  • 3分钟彻底解决Windows和Office激活难题:KMS_VL_ALL_AIO完整操作指南
  • YOLO26N 姿态估计 RKNN 部署:RK3588 NPU 实战
  • 扣子【Coze】实战:别再花钱买绘本了!用扣子一键生成,孩子天天要看新故事
  • 基于 Simulink 的工业离心机变频调速系统 S-Ramp(S型加减速)曲线规划仿真实战教程。
  • JMeter 实现:上接口失败则不执行下一个接口
  • JavaScript的DOM操作基础
  • docker python images Docker Python镜像别乱拉!容器和镜像傻傻分不清,你还在踩坑?
  • YOLO26N 姿态估计 TensorRT 部署:Jetson 实时推理
  • 经典 CNN 网络 VGG
  • 配置外置与敏感隔离:基于 Django-environ 的多环境配置管理策略
  • 性能测试进阶:从压测工具到容量规划的系统工程实践
  • 学 Simulink — 航空航天 270 V DC 高压直流电源变换器的短路保护仿真
  • 二升三年级暑假特色作业(pdf图文版)
  • 【论文阅读笔记10】小样本充电数据驱动的电池寿命预测——双流ViT与ESA
  • DeepSeek 开始摇人,有点猛啊。
  • Magpie终极指南:15种超分辨率算法重塑Windows窗口放大体验
  • YOLO26N 姿态估计 INT8 量化:低算力设备极致优化
  • 3步掌握Twitch掉落自动获取:终极智能挖矿工具完整指南
  • [hot100]盛最多水的容器
  • 规约驱动开发(SDD)——让规约成为人与 AI 之间的“合同“
  • Pytest+BDD+Playwright:构建现代化Web自动化测试框架的完整指南
  • 6.28[a]