Java JDBC 封装:从原生写法到工具类封装 + 增删改查
在 Java 操作数据库的过程中,原生 JDBC 代码存在大量重复逻辑:加载驱动、获取连接、释放资源…… 这些代码在每个业务中都要写一遍,不仅繁琐,还容易出错。
本文是个人的一些学习笔记,主要内容如下:
- 原生 JDBC 写法与封装后写法对比
- 配置文件 + 工具类封装
- 增删改查 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@7772. 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% 以上 |
| 可维护性 | 数据库信息硬编码,修改需改动多处代码 | 配置文件统一管理,修改仅需改动配置文件 |
| 错误率 | 手动关闭资源易遗漏,导致连接泄漏 | 工具类统一处理,避免资源泄漏问题 |
| 复用性 | 无复用性,每个业务重复造轮子 | 工具类可在所有业务中直接调用 |
六、运行与验证步骤
- 检查配置文件:确认
db.properties中的账号、密码、URL 配置正确 - 运行查询案例:执行
TestJDBCUtils.java,控制台输出t_user表所有数据 - 运行新增案例:执行
TestUpdate.java中的新增代码,控制台输出影响行数:1,数据库新增一条数据 - 运行修改案例:执行修改代码,数据库中对应记录的密码被更新
- 运行删除案例:执行删除代码,数据库中新增的测试数据被删除
七、后续优化方向(其他博客有介绍)
- 引入
PreparedStatement防止 SQL 注入 - 封装事务管理方法,实现业务操作的原子性
- 增加连接池(如 Druid),提升数据库连接性能
