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

Java JDBC 封装:从原生写法到工具类封装 + 增删改查

在 Java 操作数据库的过程中,原生 JDBC 代码存在大量重复逻辑:加载驱动、获取连接、释放资源…… 这些代码在每个业务中都要写一遍,不仅繁琐,还容易出错。

本文是个人的一些学习笔记,主要内容如下:

  1. 原生 JDBC 写法与封装后写法对比
  2. 配置文件 + 工具类封装
  3. 增删改查 4 个完整测试案例
  4. 项目结构与代码规范说明

一、项目结构说明

当前的项目结构如下,所有代码均放在com.qcby包下,配置文件放在resources目录:

src ├── main │ ├── java │ │ └── com.qcby │ │ ├── JDBCUtils.java // 封装好的工具类 │ │ ├── TestJDBC.java // 原生JDBC查询案例(封装前) │ │ ├── TestJDBCUtils.java // 封装后查询案例 │ │ ├── TestUpdate.java // 封装后增删改案例 │ └── resources │ └── db.properties // 数据库配置文件

二、封装前:原生 JDBC 写法(以查询为例)

1. 原生代码痛点

  • 硬编码数据库连接信息(URL、账号、密码),修改时需要改代码
  • 每次都要手动写加载驱动、获取连接、释放资源的重复代码
  • 资源关闭操作分散,容易遗漏导致连接泄漏

2. 原生查询案例代码(TestJDBC.java)

package com.qcby; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; public class TestJDBC { public static void main(String[] args) throws Exception { // 1. 加载驱动(硬编码驱动类路径) Class.forName("com.mysql.cj.jdbc.Driver"); // 2. 获取连接(硬编码数据库连接信息) String url = "jdbc:mysql:///jdbcdemo?serverTimezone=UTC&useSSL=false"; String user = "root"; String password = "Zhen@777"; Connection conn = DriverManager.getConnection(url, user, password); // 3. 获取执行SQL的对象 Statement stmt = conn.createStatement(); // 4. 执行查询SQL String sql = "select * from t_user"; ResultSet rs = stmt.executeQuery(sql); // 5. 遍历结果集 while (rs.next()) { int id = rs.getInt("id"); String username = rs.getString("username"); String pwd = rs.getString("password"); String email = rs.getString("email"); System.out.println(id + "\t" + username + "\t" + pwd + "\t" + email); } // 6. 手动释放所有资源(顺序:结果集→Statement→连接) rs.close(); stmt.close(); conn.close(); } }

三、封装:配置文件 + JDBC 工具类

1. 数据库配置文件(db.properties)

放在src/main/resources目录下,统一管理数据库连接信息,避免硬编码:

properties:

# 驱动类路径(MySQL8.0+用cj包) driverClass=com.mysql.cj.jdbc.Driver # 数据库连接URL(必须加时区配置) url=jdbc:mysql:///jdbcdemo?serverTimezone=UTC&useSSL=false # 数据库账号 username=root # 数据库密码(修改为你自己的) password=Zhen@777

2. JDBC 工具类(JDBCUtils.java)

封装加载驱动、获取连接、释放资源的通用逻辑,一次编写,到处使用:

package com.qcby; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; import java.util.Properties; /** * JDBC工具类:封装通用的数据库操作逻辑 * 功能:1. 读取配置文件 2. 加载驱动 3. 获取连接 4. 释放资源 */ public class JDBCUtils { // 配置文件对象,静态加载一次 private static Properties props = new Properties(); // 静态代码块:类加载时自动执行,只执行一次 static { try { // 读取resources下的db.properties配置文件 props.load(JDBCUtils.class.getClassLoader().getResourceAsStream("db.properties")); // 加载驱动(从配置文件读取驱动类路径) Class.forName(props.getProperty("driverClass")); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("JDBC工具类初始化失败,请检查配置文件!", e); } } /** * 获取数据库连接 * @return Connection 数据库连接对象 */ public static Connection getConnection() { try { return DriverManager.getConnection( props.getProperty("url"), props.getProperty("username"), props.getProperty("password") ); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 释放资源(查询场景:关闭ResultSet、Statement、Connection) * @param conn 数据库连接对象 * @param stmt Statement对象 * @param rs 结果集对象 */ public static void close(Connection conn, Statement stmt, ResultSet rs) { try { if (rs != null) rs.close(); if (stmt != null) stmt.close(); if (conn != null) conn.close(); } catch (Exception e) { e.printStackTrace(); } } /** * 释放资源(增删改场景:关闭Statement、Connection,无需关闭ResultSet) * @param conn 数据库连接对象 * @param stmt Statement对象 */ public static void close(Connection conn, Statement stmt) { close(conn, stmt, null); } }

四、封装后:增删改查

1. 查询案例(TestJDBCUtils.java)

使用工具类完成查询,代码大幅简化,只关注业务 SQL:

package com.qcby; import java.sql.Connection; import java.sql.ResultSet; import java.sql.Statement; public class TestJDBCUtils { public static void main(String[] args) { Connection conn = null; Statement stmt = null; ResultSet rs = null; try { // 1. 从工具类获取连接(一行代码搞定) conn = JDBCUtils.getConnection(); // 2. 获取执行SQL的对象 stmt = conn.createStatement(); // 3. 编写并执行查询SQL String sql = "select * from t_user"; rs = stmt.executeQuery(sql); // 4. 遍历结果集并输出 while (rs.next()) { System.out.println( rs.getInt("id") + "\t" + rs.getString("username") + "\t" + rs.getString("password") + "\t" + rs.getString("email") ); } } catch (Exception e) { e.printStackTrace(); } finally { // 5. 释放所有资源(调用工具类方法) JDBCUtils.close(conn, stmt, rs); } } }

2. 新增案例(TestUpdate.java 中 insert 部分)

package com.qcby; import java.sql.Connection; import java.sql.Statement; public class TestUpdate { public static void main(String[] args) { Connection conn = null; Statement stmt = null; try { // 1. 获取连接 conn = JDBCUtils.getConnection(); // 2. 获取执行对象 stmt = conn.createStatement(); // 3. 编写新增SQL String sql = "insert into t_user values(null,'封装测试','666','test@qq.com')"; // 4. 执行增删改SQL(executeUpdate返回影响行数) int rows = stmt.executeUpdate(sql); System.out.println("新增数据成功,影响行数:" + rows); } catch (Exception e) { e.printStackTrace(); } finally { // 5. 释放资源(增删改场景,无需ResultSet) JDBCUtils.close(conn, stmt); } } }

3. 修改案例(TestUpdate.java 中 update 部分)

package com.qcby; import java.sql.Connection; import java.sql.Statement; public class TestUpdate { public static void main(String[] args) { Connection conn = null; Statement stmt = null; try { conn = JDBCUtils.getConnection(); stmt = conn.createStatement(); // 修改SQL:将用户名为“封装测试”的密码改为888888 String sql = "update t_user set password='888888' where username='封装测试'"; int rows = stmt.executeUpdate(sql); System.out.println("修改数据成功,影响行数:" + rows); } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtils.close(conn, stmt); } } }

4. 删除案例(TestUpdate.java 中 delete 部分)

package com.qcby; import java.sql.Connection; import java.sql.Statement; public class TestUpdate { public static void main(String[] args) { Connection conn = null; Statement stmt = null; try { conn = JDBCUtils.getConnection(); stmt = conn.createStatement(); // 删除SQL:删除用户名为“封装测试”的记录 String sql = "delete from t_user where username='封装测试'"; int rows = stmt.executeUpdate(sql); System.out.println("删除数据成功,影响行数:" + rows); } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtils.close(conn, stmt); } } }

五、封装前后对比总结

对比维度原生 JDBC 写法工具类封装写法
代码量每个业务都要写连接、释放资源,代码冗余仅关注业务 SQL,代码量减少 60% 以上
可维护性数据库信息硬编码,修改需改动多处代码配置文件统一管理,修改仅需改动配置文件
错误率手动关闭资源易遗漏,导致连接泄漏工具类统一处理,避免资源泄漏问题
复用性无复用性,每个业务重复造轮子工具类可在所有业务中直接调用

六、运行与验证步骤

  1. 检查配置文件:确认db.properties中的账号、密码、URL 配置正确
  2. 运行查询案例:执行TestJDBCUtils.java,控制台输出t_user表所有数据
  3. 运行新增案例:执行TestUpdate.java中的新增代码,控制台输出影响行数:1,数据库新增一条数据
  4. 运行修改案例:执行修改代码,数据库中对应记录的密码被更新
  5. 运行删除案例:执行删除代码,数据库中新增的测试数据被删除

七、后续优化方向(其他博客有介绍)

  • 引入PreparedStatement防止 SQL 注入
  • 封装事务管理方法,实现业务操作的原子性
  • 增加连接池(如 Druid),提升数据库连接性能
http://www.jsqmd.com/news/661917/

相关文章:

  • 如何快速掌握免费开源动画工具:MTB Nodes终极指南
  • 2026年天津遗产继承律所权威测评!千案经验+透明收费,破解继承纠纷难题 - 速递信息
  • G-Helper终极指南:如何快速解决华硕ROG笔记本性能与显示问题
  • 别再傻傻地手动算时间了!C++11 std::chrono::duration_cast 保姆级使用指南(附完整代码)
  • 3分钟掌握ChampR:英雄联盟电竞助手的终极配置方案
  • C++枚举类型最佳实践
  • SAP ECC6 EC-CS 标准报表项目(FS Item)× SAP 标准总账科目对照版
  • 2026年植草砖及PC砖厂家推荐:透水PC砖/导水槽/护坡砖/路面砖专业供应商选型指南 - 品牌推荐官
  • SITS2026现场直击:AGI如何在37分钟内重构量子化学模拟流程(附可复现代码路径)
  • 如何高效获取B站完整评论数据:BilibiliCommentScraper终极指南
  • Vivado综合实战:从代码风格到资源映射,精准控制BRAM与LUTRAM
  • 电商价格系统怎么设计?一次讲清一口价、活动价、券后价、价格快照与改价留痕
  • Git合并策略实战:从merge、rebase到squash的进阶指南
  • 今天不看就晚了:AGI创造性能力评估标准即将升级,3大新增硬性阈值倒计时披露
  • Open Images Dataset V6 + Extensions:一站式获取与实战转换目标检测数据集
  • K8s访问控制
  • 2026天津家暴离婚律所专项测评!人身保护令+损害赔偿实战指南 - 速递信息
  • 3步终极清理方案:彻底解决Visual Studio卸载残留问题
  • Android应用卡顿?从SurfaceFlinger的VSYNC信号与缓冲区管理说起
  • VSCode + Mermaid本地画图最强组合:无需插件,一个HTML文件搞定所有图表
  • K8s控制平面升级
  • 树莓派直连巴法云:TCP与MQTT双协议实战指南
  • STM32CubeMX实战:ADC采集光敏电阻数据实现环境光照监测
  • 高通Camera驱动(4)-- 从configure_streams到Usecase的创建与匹配
  • 余杭永鸿再生资源:杭州市废旧金属回收推荐哪几家 - LYL仔仔
  • STM32H743实战(三)-- 时钟树配置与性能调优实战
  • 5款AI工具大测评,助你轻松实现低查重的AI教材生成梦想!
  • 别再死记硬背了!用H模型和Π模型,手把手教你搞定三极管高频电路设计
  • 从光场相机到手机摄影:聊聊那些让你‘先拍照后对焦’的黑科技是怎么实现的
  • 漂浮式半潜风机(二)环境荷载:从理论谱分析到工程实践的关键考量