JAVA jdbc封装 DbUtilsBean-查询方法支持指定封装类(内省)

说明

类似 Mybatis,支持将查询结果封装到 Bean 中

继承

需要继承 某个 DbUtils 类 或 子类即可

注意

  1. MySQL 中的 decimal 类型,JDBC 查询是 BigDecimal 类型,需要做判断,转换成 double、float 类型

  2. bean中有某成员变量,但查询表时,没有查询该列,就不能取该数据

代码

定义内省工具类

package top.malaoshi.jdbc;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class IntrospectorUtils {

    /**
     * 获取setter方法
     * @param bean
     * @return 返回Map,属性名作为key,setter方法作为value
     */
    public static Map<String, Method> getSetMethods(Class bean) throws IntrospectionException {
        // 使用内省,目的:是反射的简化写法
        BeanInfo bi= Introspector.getBeanInfo(bean); // 返回 BeanInfo,bean的信息
        // 将信息取出,放入到数组里
        PropertyDescriptor[] pds=bi.getPropertyDescriptors();
        // 将属性 和 相关的set方法,放入到map中
        Map<String,Method> propertyAndSetterMap = new HashMap();
        for( PropertyDescriptor pd : pds){
            //System.out.println("属性名:"+pd.getName());
            String propertyName = pd.getName();
            // 获取 set方法 的信息
            Method method = pd.getWriteMethod();
            // 如果有 set 方法,就放入到map中
            if(method!=null) {
                propertyAndSetterMap.put(propertyName,method);
            }
        }
        return propertyAndSetterMap;
    }

}

定义 DbUtilsBean 类

/**
     * 指定封装类,将查询的结果封装到该类中
     * 注意:列名要与类成员变量名相同,才能封装
     *
     * @param sql
     * @param params
     * @param clazz 指定封装类
     * @return
     * @throws SQLException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     */
    public <E> List<E> query(Class clazz,String sql,Object ... params) throws SQLException, IntrospectionException, IllegalAccessException, InstantiationException, InvocationTargetException {
        Map<String,Method> propertyAndSetterMap = IntrospectorUtils.getSetMethods(clazz);
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        // 先循环绑定参数
        for (int i=0   ;i< params.length   ;i++   ){
            // ? 的索引,从1开始的
            // 数组的索引,从0开始的
            preparedStatement.setObject(i+1 , params[i]  );
        }
        List retList = new ArrayList();
        // 然后执行查询
        ResultSet resultSet = preparedStatement.executeQuery();
        ResultSetMetaData rsmd = resultSet.getMetaData();
        // 返回查询结果列的总数
        int columnNum = rsmd.getColumnCount();
        while (resultSet.next()){
            // 通过反射方式实例化
            Object bean = clazz.newInstance();
            // 将查询列的值,放入到 javabean(模型类)的 属性中
            for( int i = 1 ;i<=columnNum ;i++){

                // 取出查询的列名
                String columnLabel = rsmd.getColumnLabel(i);
                // 取出与该列名相应的值
                Object value = resultSet.getObject(i);
                // 根据列名,从map中取出与列名相同的属性的set方法
                Method setMethod = propertyAndSetterMap.get(columnLabel);
                // 如果方法为null,表示没有对应的方法,就不再往下执行
                if(setMethod == null) {
                    continue;
                }
                invokeSetMethod(bean,setMethod,value);
            }
            retList.add(bean);
        }
        resultSet.close();
        preparedStatement.close();
        return retList;
    }

    /**
     * 调用 setter 方法
     * @param bean
     * @param setMethod
     * @param value
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    private void invokeSetMethod(Object bean, Method setMethod, Object value ) throws InvocationTargetException, IllegalAccessException {
        // set方法一定有一个参数
        Class paraType = setMethod.getParameterTypes()[0];
        Object dataTemp=null;
        if ( value instanceof BigDecimal){ // mysql数据库的 decimal 类型,jdbc查询出来的类型是 BigDecimal,需要做额外处理
            BigDecimal temp=(BigDecimal)value;
            if (paraType == double.class){
                dataTemp = temp.doubleValue();
            }else if(paraType == float.class){
                dataTemp = temp.floatValue();
            }
        }else{
            dataTemp=value;
        }
        // 动态调用 setXXX 方法,并且传值
        try {
            setMethod.invoke(bean,dataTemp);
        }catch (java.lang.IllegalArgumentException e){
            String dataType = "null";
            if(dataTemp!=null){
                dataType = dataTemp.getClass().getName();
            }
            String msg = String.format("%s() 形参类型是:%s,从数据库查询出来的数据:%s,类型是:%s",
                    setMethod.getName(),
                    setMethod.getParameterTypes()[0],
                    dataTemp,
                    dataType);

            throw new IllegalArgumentException(msg);
        }
    }

测试

依赖

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.18</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>top.malaoshi</groupId>
    <artifactId>util</artifactId>
    <version>1.4.5</version>
    <scope>test</scope>
</dependency>

定义封装类 Emp

public class Emp {
    private int empno;
    private String ename;
    private String job;
    private Integer mgr;

    public int getEmpno() {
        return empno;
    }

    public void setEmpno(int empno) {
        this.empno = empno;
    }

    public String getEname() {
        return ename;
    }

    public void setEname(String ename) {
        this.ename = ename;
    }

    public String getJob() {
        return job;
    }

    public void setJob(String job) {
        this.job = job;
    }

    public Integer getMgr() {
        return mgr;
    }

    public void setMgr(Integer mgr) {
        this.mgr = mgr;
    }

    @Override
    public String toString() {
        return "Emp{" +
                "empno=" + empno +
                ", ename='" + ename + '\'' +
                ", job='" + job + '\'' +
                ", mgr=" + mgr +
                '}';
    }
}

定义测试类

import top.malaoshi.jdbc.ConnectionFactory;
import top.malaoshi.jdbc.DB;
import top.malaoshi.util.Out;

import java.beans.IntrospectionException;
import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;

public class Test {

    public static void main(String[] args) throws SQLException, ClassNotFoundException {
        //东8时区
        String url="jdbc:mysql://localhost:3308/scott?useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai";

        Connection conn = ConnectionFactory.connect("com.mysql.cj.jdbc.Driver"
                , url
                ,"root"
                ,"");

        DB db = new DB(conn);
        String sql = "select * from emp";
        try {
            List<Emp> list = db.query(Emp.class, sql);

            Out.elementSep(",\n").printList(list);
        } catch (IntrospectionException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }

    }
}

原文出处:/show_1IX3vG3MUZJl.html