news 2026/4/18 17:45:07

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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),提升数据库连接性能
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 17:32:33

ApkShellext2:让Windows文件管理器智能识别应用图标的创新工具

ApkShellext2:让Windows文件管理器智能识别应用图标的创新工具 【免费下载链接】apkshellext Show app icons in windows explorer 项目地址: https://gitcode.com/gh_mirrors/ap/apkshellext 在Windows系统中管理Android应用包文件时,你是否曾为…

作者头像 李华
网站建设 2026/4/18 17:30:18

EV录屏文件损坏的深度修复指南:从原因分析到工具实战

1. EV录屏文件损坏的常见原因分析 每次遇到EV录屏文件损坏的情况,我都恨不得把键盘砸了——辛辛苦苦录了几个小时的教程,结果打开一看全是马赛克或者直接打不开。经过多年踩坑,我发现文件损坏主要集中在这几个场景: 最典型的就是长…

作者头像 李华
网站建设 2026/4/18 17:30:17

【S32K开发指南】SIUL2:从引脚管理到中断响应的实战解析

1. SIUL2模块初探:S32K的引脚管家 第一次拿到S32K开发板时,看着密密麻麻的引脚定义图,我整个人都是懵的——这些引脚既能当GPIO用,又能做串口通信,还能触发中断,到底该怎么管理?直到遇见了SIUL2…

作者头像 李华