更新时间:2022-07-06 来源:黑马程序员 浏览量:
1.1 概述
如果只使用JDBC进行开发,冗余代码过多,为了简化JDBC开发。我们采用apache commons组件一个成员:DBUtils。
DBUtils就是JDBC的简化开发工具包。需要项目导入commons-dbutils-1.6.jar才能够正常使用DBUtils工具。
DBUtils是java编程中的数据库操作实用工具,小巧简单实用。DBUtils封装了对JDBC的操作,简化了JDBC操作,可以少写代码。
本案例就是模拟DBUtils的功能自定义一个简化JDBC开发的工具HMDButils,目的练习反射的内容。
HMDButils三个核心功能介绍
HMQueryRunner中提供对sql语句操作的API.
HMResultSetHandler接口,用于定义select操作后,怎样封装结果集.
HMDbUtils类,它就是一个工具类,定义了关闭资源与事务处理的方法
## 1.2 HMQueryRunner核心类
HMQueryRunner核心类源代码:
  ```java
package com.itheima.dbutils;
import com.itheima.dbutils.inter.HMResultSetHandler;
import com.itheima.dbutils.utils.HMQueryRunnerUtils;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class HMQueryRunner {
    private DataSource ds;
    //空参构造
    public HMQueryRunner() {
    }
    //有参构造
    public HMQueryRunner(DataSource ds) {
        this.ds = ds;
    }
    //执行增删改
    public int update(String sql,Object ... params) throws SQLException {
        return update(ds.getConnection(),sql,params);
    }
    //执行增删改
    public int update(Connection con, String sql,Object ... params) throws SQLException {
        //1.调用方法获取PreparedStatement对象(已经给?完成了赋值)
        PreparedStatement pstmt = HMQueryRunnerUtils.preparedStatement(con, sql, params);
        //5.执行查询
        int result = pstmt.executeUpdate();
        //6.返回结果
        return result;
    }
    //执行查询
    public <T> T query(String sql, HMResultSetHandler<T> hmrsh, Object ... params) throws SQLException {
        return query(ds.getConnection(),sql,hmrsh,params);
    }
    //执行查询
    public <T> T query(Connection con, String sql, HMResultSetHandler<T> hmrsh, Object ... params) throws SQLException {
        //1.调用方法,执行查询
        ResultSet rs = HMQueryRunnerUtils.getResultSet(con, sql, params);
        //2.调用结果集处理器的handler方法,处理结果集
        T t = hmrsh.handler(rs);
        //返回结果对象t
        return t;
    }
}HMQueryRunner核心类中使用的工具类HMQueryRunnerUtils的源代码:
  ```java
package com.itheima.dbutils.utils;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.*;
public class HMQueryRunnerUtils {
    //构造方法private修饰
    private HMQueryRunnerUtils(){}
    //静态方法,获取PreparedStatement对象
    public static PreparedStatement preparedStatement(Connection con, String sql, Object ... params) throws SQLException {
        //1.获取执行sql语句的PreparedStatement对象
        PreparedStatement pstmt = con.prepareStatement(sql);
        //2.获取sql语句参数的ParameterMetaData对象
        ParameterMetaData pmd = pstmt.getParameterMetaData();
        //3.获取sql语句中?的数量
        int count = pmd.getParameterCount();
        //System.out.println(count);
        //4.给?号赋值
        for(int i = 0;i<count;i++) {
            pstmt.setObject((i+1),params[i]);
        }
        //5.返回PreparedStatement对象
        return pstmt;
    }
    //执行查询获取结果集
    public static ResultSet getResultSet(Connection con, String sql, Object ... params) throws SQLException {
        //1.调用方法获取PreparedStatement对象(已经给?完成了赋值)
        PreparedStatement pstmt = preparedStatement(con, sql, params);
        //2.执行查询
        ResultSet rs = pstmt.executeQuery();
        //3.返回结果集
        return rs;
    }
    public static <T> T getInstance(ResultSet rs,Class<T> clazz, ResultSetMetaData rmd, int count) throws InstantiationException, IllegalAccessException, SQLException, NoSuchMethodException, ClassNotFoundException, InvocationTargetException {
        //6.反射创建对象
        T t = clazz.newInstance();
        //7.获取所以字段名,调用set方法完成对象的赋值
        for (int i = 0; i < count; i++) {
            String columnName = rmd.getColumnName(i + 1);
            //8.调用方法,根据字段名获取对应的set方法名称
            String setMethodName = getSetMethodName(columnName);
            //9.获取列类型对应的java类型的全类名
            String columnClassName = rmd.getColumnClassName(i + 1);
            //9.反射获取set方法对象
            Method setMethod = clazz.getMethod(setMethodName, Class.forName(columnClassName));
            //10.执行set方法对象,给属性赋值
            setMethod.invoke(t,rs.getObject(i+1));
        }
        return t;
    }
    //根据字段名称,获取对应的Set方法名称
    private static String getSetMethodName(String fieldName) {
        return "set".concat(fieldName.substring(0,1).toUpperCase().concat(fieldName.substring(1)));
    }
}1.2.1 构造方法使用-提供数据源
构造方法
`HMQueryRunner(DataSource) ` 创建核心类,并提供数据源,内部自己维护Connection
普通方法
`update(String sql , Object ... params) ` 执行DML语句
`query(String sql , HMResultSetHandler , Object ... params) ` 执行DQL语句,并将查询结果封装到对象中。
1.2.2 构造方法使用-不提供数据源
构造方法
`HMQueryRunner()` 创建核心类,**没有**提供数据源,在进行具体操作时,需要手动提供Connection
普通方法
`update(Connection conn , String sql , Object ... params)` 使用提供的Connection,完成DML语句
`query(Connection conn , String sql , HMResultSetHandler , Object ... params) ` 使用提供的Connection,执行DQL语句,并将查询结果封装到对象中。
1.3 结果集处理器接口HMResultSetHandler源代码
  ```java
package com.itheima.dbutils.inter;
import java.sql.ResultSet;
import java.sql.SQLException;
/*
    结果集处理器接口
 */
public interface HMResultSetHandler<T> {
    //抽象方法
    public abstract T handler(ResultSet rs) throws SQLException;
}1.4 结果集处理器接口实现类
  HMBeanHandler:将结果集中第一条记录封装到一个指定的javaBean中。
package com.itheima.dbutils.impl;
import com.itheima.dbutils.inter.HMResultSetHandler;
import com.itheima.dbutils.utils.HMQueryRunnerUtils;
import java.lang.reflect.InvocationTargetException;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
public class HMBeanHandler<T> implements HMResultSetHandler<T> {
    private Class<T> clazz;
    public HMBeanHandler(Class<T> clazz) {
        this.clazz = clazz;
    }
    @Override
    public T handler(ResultSet rs) throws SQLException {
        T t = null;
        try {
            //3.获取结果集元数据
            ResultSetMetaData rmd = rs.getMetaData();
            //4.获取列数量
            int count = rmd.getColumnCount();
            //5.处理结果集
            if(rs.next()) {
                t = HMQueryRunnerUtils.getInstance(rs,clazz, rmd, count);
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return t;
    }
}HMBeanListHandler:将结果集中每一条记录封装到指定的javaBean中,将这些javaBean在封装到List集合中
  ```java
package com.itheima.dbutils.impl;
import com.itheima.dbutils.inter.HMResultSetHandler;
import com.itheima.dbutils.utils.HMQueryRunnerUtils;
import java.lang.reflect.InvocationTargetException;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class HMBeanListHandler<T> implements HMResultSetHandler<List<T>> {
    private Class<T> clazz;
    //构造方法
    public HMBeanListHandler(Class<T> clazz) {
        this.clazz = clazz;
    }
    @Override
    public List<T> handler(ResultSet rs) throws SQLException {
        //2.创建List集合对象
        List<T> list = new ArrayList<>();
        try {
            //3.获取结果集元数据
            ResultSetMetaData rmd = rs.getMetaData();
            //4.获取列数量
            int count = rmd.getColumnCount();
            //5.处理结果集
            while(rs.next()) {
                T t = HMQueryRunnerUtils.getInstance(rs,clazz, rmd, count);
                //11.把对象添加到List集合对象中
                list.add(t);
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return list;
    }
}HMScalarHandler:它是用于单数据。例如select count(*) from 表操作。
  ```java
package com.itheima.dbutils.impl;
import com.itheima.dbutils.inter.HMResultSetHandler;
import java.sql.ResultSet;
import java.sql.SQLException;
public class HMScalarHandler<T> implements HMResultSetHandler<T> {
    private final int columnIndex;
    private final String columnName;
    public HMScalarHandler() {
        this(1, null);
    }
    public HMScalarHandler(int columnIndex) {
        this(columnIndex,null);
    }
    public HMScalarHandler(String columnName) {
        this(1,columnName);
    }
    private HMScalarHandler(int columnIndex, String columnName) {
        this.columnIndex = columnIndex;
        this.columnName = columnName;
    }
    @Override
    public T handler(ResultSet rs) throws SQLException {
        T t = null;
        //5.处理结果集
        if(rs.next()) {
            if(columnName == null) {
                t = (T)rs.getObject(columnIndex);
            } else {
                t = (T)rs.getObject(columnName);
            }
        }
        return t;
    }
}HMColumnListHandler:将结果集中指定的列的字段值,封装到一个List集合中
  ```java
package com.itheima.dbutils.impl;
import com.itheima.dbutils.inter.HMResultSetHandler;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class HMColumnListHandler<T> implements HMResultSetHandler<List<T>> {
    private final int columnIndex;
    private final String columnName;
    public HMColumnListHandler() {
        this(1,null);
    }
    public HMColumnListHandler(int columnIndex) {
        this(columnIndex,null);
    }
    public HMColumnListHandler(String columnName) {
        this(1,columnName);
    }
    private HMColumnListHandler(int columnIndex, String columnName) {
        this.columnIndex = columnIndex;
        this.columnName = columnName;
    }
    @Override
    public List<T> handler(ResultSet rs) throws SQLException {
        //2.创建List集合对象
        List<T> list = new ArrayList<>();
        //3.获取结果集元数据
        ResultSetMetaData rmd = rs.getMetaData();
        //4.获取列数量
        int count = rmd.getColumnCount();
        //5.处理结果集
        while(rs.next()) {
            Object obj = null;
            if(columnName == null) {
                obj = rs.getObject(columnIndex);
            } else {
                obj = rs.getObject(columnName);
            }
            list.add((T)obj);
        }
        return list;
    }
}然后把我们的整个模块打成jar包,就可以通过导入jar包的方式进行使用了(本文档提供打好的jar包)
  关于如何把idea中模块打成jar包,这里就不再详细描述,可以百度搜索: idea中如何将代码打成jar包

总结
1.此篇文章用来模拟DBUtils简化JDBC操作数据库的复杂步骤
2.大量使用反射,让代码更为灵活,使用者更加方便
3.方法参数是接口,调用者可以根据需求传递具体的实现了,如果感觉实现类不合适,可以自己定义实现类,提高了代码的扩展性
1024首播|39岁程序员逆袭记:不被年龄定义,AI浪潮里再迎春天
2025-10-241024程序员节丨10年同行,致敬用代码改变世界的你
2025-10-24【AI设计】北京143期毕业仅36天,全员拿下高薪offer!黑马AI设计连续6期100%高薪就业
2025-09-19【跨境电商运营】深圳跨境电商运营毕业22个工作日,就业率91%+,最高薪资达13500元
2025-09-19【AI运维】郑州运维1期就业班,毕业14个工作日,班级93%同学已拿到Offer, 一线均薪资 1W+
2025-09-19【AI鸿蒙开发】上海校区AI鸿蒙开发4期5期,距离毕业21天,就业率91%,平均薪资14046元
2025-09-19