JDBC基础(2) JDBC基础(2)作者没有四次元口袋的蓝胖日期2026-06-29标签Java, JDBC, 工具类一、为什么要封装工具类原生 JDBC 代码有很多重复每次都要写注册驱动、获取连接每次都要写繁琐的释放资源连接参数硬编码在代码里修改不方便封装 JDBCUtils 的目的减少重复代码提高开发效率。二、JDBCUtils 工具类的编写2.1 配置文件jdbc.properties把连接参数放到配置文件里方便修改# jdbc.properties drivercom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/db_name?useSSLfalseserverTimezoneUTCcharacterEncodingutf8 usernameroot password1234562.2 工具类代码importjava.io.IOException;importjava.io.InputStream;importjava.sql.*;importjava.util.Properties;/** * JDBC 工具类 * 功能获取连接、释放资源 */publicclassJDBCUtils{privatestaticStringdriver;privatestaticStringurl;privatestaticStringusername;privatestaticStringpassword;// 静态代码块类加载时读取配置文件、注册驱动只执行一次static{try{// 1. 读取配置文件PropertiespropnewProperties();// 使用类加载器读取 src 下的配置文件InputStreamisJDBCUtils.class.getClassLoader().getResourceAsStream(jdbc.properties);prop.load(is);// 2. 获取参数driverprop.getProperty(driver);urlprop.getProperty(url);usernameprop.getProperty(username);passwordprop.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){Connectionconnnull;PreparedStatementpstmtnull;ResultSetrsnull;try{connJDBCUtils.getConnection();StringsqlSELECT * FROM student WHERE id ?;pstmtconn.prepareStatement(sql);pstmt.setInt(1,id);rspstmt.executeQuery();Studentstunull;if(rs.next()){stunewStudent();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){Connectionconnnull;PreparedStatementpstmtnull;try{conngetConnection();pstmtconn.prepareStatement(sql);// 设置参数for(inti0;iargs.length;i){pstmt.setObject(i1,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 参数 */publicstaticTTquery(Stringsql,ResultSetHandlerThandler,Object...args){Connectionconnnull;PreparedStatementpstmtnull;ResultSetrsnull;try{conngetConnection();pstmtconn.prepareStatement(sql);for(inti0;iargs.length;i){pstmt.setObject(i1,args[i]);}rspstmt.executeQuery();returnhandler.handle(rs);// 交给处理器处理结果集}catch(SQLExceptione){e.printStackTrace();returnnull;}finally{close(rs,pstmt,conn);}}// 结果集处理器接口publicinterfaceResultSetHandlerT{Thandle(ResultSetrs)throwsSQLException;}// 把结果集封装成 ListStudent 的处理器publicclassBeanListHandlerTimplementsResultSetHandlerListT{privateClassTclazz;publicBeanListHandler(ClassTclazz){this.clazzclazz;}OverridepublicListThandle(ResultSetrs)throwsSQLException{ListTlistnewArrayList();ResultSetMetaDatametaDatars.getMetaData();intcolumnCountmetaData.getColumnCount();while(rs.next()){try{Tobjclazz.newInstance();for(inti1;icolumnCount;i){StringcolumnNamemetaData.getColumnName(i);Objectvaluers.getObject(i);// 反射给字段赋值Fieldfieldclazz.getDeclaredField(columnName);field.setAccessible(true);field.set(obj,value);}list.add(obj);}catch(Exceptione){e.printStackTrace();}}returnlist;}}使用ListStudentlistJDBCUtils.query(SELECT * FROM student WHERE age ?,newBeanListHandler(Student.class),18);三、Apache DbUtils 组件手写通用查询比较麻烦Apache 提供了成熟的工具类commons-dbutils它已经帮我们封装好了通用的增删改查和结果集处理器。3.1 引入依赖Maven 项目在pom.xml中添加dependencygroupIdcommons-dbutils/groupIdartifactIdcommons-dbutils/artifactIdversion1.7/version/dependency普通 Java 项目直接导入 jar 包即可commons-dbutils-1.7.jar3.2 QueryRunner —— 核心类QueryRunner 是 DbUtils 的核心操作类它提供了增删改查的方法。importorg.apache.commons.dbutils.QueryRunner;importorg.apache.commons.dbutils.handlers.*;// 创建 QueryRunner 对象QueryRunnerqrnewQueryRunner();3.3 增删改update// 增StringinsertSqlINSERT INTO student(name, age) VALUES(?, ?);introwsqr.update(conn,insertSql,赵六,24);// 删StringdeleteSqlDELETE FROM student WHERE id ?;introws2qr.update(conn,deleteSql,1);// 改StringupdateSqlUPDATE student SET age ? WHERE name ?;introws3qr.update(conn,updateSql,26,赵六);如果传入DataSource连接池给 QueryRunner 构造方法就不用每次传 Connection 了。3.4 查询 —— ResultSetHandler 八大实现类实现类作用BeanHandlerT将结果集第一行封装为一个 JavaBean 对象BeanListHandlerT将结果集所有行封装为 ListScalarHandler将结果集第一行第一列封装为 Object用于统计查询 count/sum 等MapHandler将结果集第一行封装为 Mapkey列名, value值MapListHandler将结果集所有行封装为 ListArrayHandler将结果集第一行封装为 Object[]ArrayListHandler将结果集所有行封装为 ListObject[]ColumnListHandler将结果集某一列封装为 List3.5 查询一个对象BeanHandlerStringsqlSELECT * FROM student WHERE id ?;Studentstuqr.query(conn,sql,newBeanHandler(Student.class),1);System.out.println(stu);3.6 查询集合BeanListHandlerStringsqlSELECT * FROM student WHERE age ?;ListStudentlistqr.query(conn,sql,newBeanListHandler(Student.class),18);for(Students:list){System.out.println(s);}3.7 统计查询ScalarHandlerStringsqlSELECT COUNT(*) FROM student;Longcountqr.query(conn,sql,newScalarHandler());System.out.println(总记录数count);注意SELECT COUNT(*)返回的是Long类型不是int要用Long接收。3.8 查询 Map 集合// 查询一条 → MapMapString,Objectmapqr.query(conn,SELECT * FROM student WHERE id ?,newMapHandler(),1);// 查询多条 → ListMapListMapString,ObjectmapListqr.query(conn,SELECT * FROM student,newMapListHandler());3.9 自定义 ResultSetHandler如果内置的处理器满足不了需求可以自定义// 自定义只取姓名列ListStringnamesqr.query(conn,SELECT name FROM student,newResultSetHandlerListString(){OverridepublicListStringhandle(ResultSetrs)throwsSQLException{ListStringlistnewArrayList();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 必须满足私有字段成员变量私有无参构造必须提供无参构造方法反射要用到getter/setter提供所有字段的 getter 和 setter 方法字段名对应数据库列名要和 JavaBean 属性名一致或用别名对应publicclassStudent{privateintid;privateStringname;privateintage;// 必须有无参构造publicStudent(){}// getter 和 setterpublicintgetId(){returnid;}publicvoidsetId(intid){this.idid;}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.namename;}publicintgetAge(){returnage;}publicvoidsetAge(intage){this.ageage;}}列名和属性名不一致怎么办用 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 └── 列名与属性名对应六、写在最后JDBCUtils 必须自己手写一遍理解工具类的封装思想这是面试常考的手写 JDBC 工具类DbUtils 要会用实际开发用它比自己写通用方法方便得多BeanHandler / BeanListHandler 是重点最常用的两个结果集处理器ScalarHandler 注意返回类型count 返回 Long别用 int 接常见面试点手写 JDBC 工具类PreparedStatement 和 Statement 的区别SQL 注入ResultSetHandler 的实现类有哪些JavaBean 封装的要求