利用反射实现JDBC的数据库通用查询方法

/**
     * 通用的查询方法
     * 这里的SQL语句作为一个变量参数传递进来
     * 如果是根据id或者name等模糊查询还需要将具体的参数传递进来
     * 但是这里是通用的方法,我们不知道具体传入的是emp还是dept
     * 所以直接利用一个对象数组的这样一个参数传递进来
     */
    //返回一个结果集
    public List<E> query(String sql,Object[] parames,Class<?> cla){
        //这里还需要一个Class<?>属性来通过方法的名字以及方法的参数属性
        //来得到一个方法,这里利用了反射的原理
        List<E> list = new ArrayList<E>();
        //获取连接
        con = JDBCUtil.getConnection();
        //预处理
        try {
            ps = con.prepareStatement(sql);
            //由于这里的SQL语句是一个变量
            //不知道需要查询哪些变量和哪些字段
            //以及对应的字段名是什么
            //预处理之后需要设置通过SQL语句传入进来的值
            //比如通过id进行查询,and name like%S%等
            //那么就需要先将这些问号设置具体的值,而这些值是存放在
            //一个对象数组里面的,我们需要通过遍历拿到对应的值,才能进行
            //具体的设置
            for (int i = 0; i < parames.length; i++) {
                ps.setObject(i+1, parames[i]);
            }
            //做好SQL语句的准备工作之后就是执行SQL语句了
            ResultSet rs = ps.executeQuery();
            //接下来的工作就是将查询的结果从数据库中返回给用户
            //同样利用rs的游标性质,通过while循环取出来
            ResultSetMetaData data = rs.getMetaData();
            //变化结果集的列数
            //通过变化结果集来获取列数
            int count = data.getColumnCount();
            E o = null;
            while(rs.next()){

                //通过rs先通过字段类型和字段名称来取出每一行对应的字段的值
                //但是现在我们不知道具体是什么类型和字段
                //这里我们通过结果集的metadata的属性来获取
                //返回的数据类型为结果集变化数据
                //然后我们从变化的数据中(metadata)获取字段名称也就是列名
                //这里的参数需要传入第几列data.getColumnName(column)
                //所以我们还需要拿到变化结果集中由多少列
                //遍历data中的列数,依次获取对应的列名
                for (int i = 1; i <=count ; i++) {
                    /*
                     * 从结果集中获取数据
                     */
                    //之前的做法是通过rs.getstring("addr")
                    //但是这里我不知道取出的是什么类型的的字段名称
                    //所以直接用getObject来表示
                    //然后具体的字段名称直接再用for循环遍历列名的时候
                    //用i来取值
                    Object val = rs.getObject(i);
                    //获取到数据之后就是开始new一个student或者emp将从结果集中取出的一行行的数据
                    //封装成一个对象,然后添加到结果集中
                    //但是这里我们不能通过new一个对象来获取
                    //因为可能有的实体类没有构建构造方法
                    //并且构造器需要严格要求字段的顺序保持一致
                    //所以这里我们实例化对象的时候采用set+具体的字段名称
                    //所以这里我们需要先获取到具体的字段名称,

                    String columnName = data.getColumnName(i);
                    //获取到实体的具体的字段名称之后就需要构造一个和实体类里面
                    //定义的set方法一样的格式来调用set函数
                    String relMethodName = "set"+columnName.substring(0,1).toUpperCase()+columnName.substring(1).toLowerCase();
                    /*
                     *  public void setName(String name) {
                                this.name = name;
                            }
                        到这里我们已经拿到了setName(),下面还需要拿到字段的属性
                        同样是利用结果变化数据来拿到columnclassname
                     */
                    String columnClassName = data.getColumnClassName(i);
                    //但是这里我们拿到的只是字段的属性名称,是一个字符串类型的
                    //我们需要通过这个名称来转为具体的属性
                    //就需要通过forname的反射来实现
                    try {
                        Class<?> columnType = Class.forName(columnClassName);
                        //由于这里返回的class属性太多我们不知道具体是哪个,比如可能有string double int 或者public void 函数等
                        //所以这里我们利用Class<?>的泛化来表示 通过属性名称反射的属性
                        /**
                         * 同时需要注意的是虽然我们已经得到了具体的字段属性
                         * 但是Java的数据类型表示方法和数据库的数据类型表示方法不一致
                         * 所以我们还需要将Java中数据类型转为数据库的类型
                         * 主要需要转换的就是decimal类型
                         */
                        //所以这里需要一个if判断得到的column type属性是否有decimal属性,有的话
                        //就将对应的属性转为int类型
                        //同时还需要将里面的值也要强制转为int类型
                        if(java.math.BigDecimal.class.equals(columnType)){
                            columnType = int.class;
                            //这里的val是通过getobject属性获取的,是object类型,所以需要先强制转换为
                            //bigdecimal类型,然后转为int类型
                            val = ((java.math.BigDecimal)val).intValue();
                        }
                        try {
                            //到这里我们已经拿到了方法名字如:setName 
                            //以及需要设置的字段的类型 如 string
                            //那么就相当于我们可以创建一个public void setName(String name) {}
                            //这样的函数方法了
                            //要拿到一个实体的set方法那么我们需要先得到这个属性的class属性
                            //但是这个实体是一个变化的可能是Emp,或者Dept,
                            //所以这里也需要通过一个参数传递进来
                            Method method = cla.getMethod(relMethodName, columnType);
                            try {
                                //现在我们已经得到了方法
                                /**
                                 *  public void setName(String name) {
                                    this.name = name;
                                }
                                 */
                                //然后就是需要找到this  也就是调用方法的执行体
                                //而这里的this指的就是当前的对象
                                //但是在这个通用的查询方法中我们并不知道
                                //传入进来的对象到底是什么,所以我们初始化一个对象
                                //利用E o = null,来实例化一个对象泛指所有的对象
                                //然后利用方法的invoke属性来执行将取出的val设置给对象O
                                method.invoke(o, val);
                            } catch (IllegalAccessException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            } catch (IllegalArgumentException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            } catch (InvocationTargetException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }
                        } catch (NoSuchMethodException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        } catch (SecurityException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    } catch (ClassNotFoundException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                //封装好对象之后就需要添加到集合中
                //遍历一张表就添加一次
                //for循环完了就添加一个对象
                list.add(o);
            }
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return list;
    }

然后再写一个具体的子类继承上面的通用方法
StuDAO

public class StuDAO extends BaseDAO<Student> {
public List<Student> findAll(Object[] parames){
        String sql = "select * from student";
        //调用父类的方法query
        return this.query(sql, parames, Student.class);
        //返回当前方法的query方法 
        //方法中调用方法
        //子类中的方法调用父类中的方法
        }

这里还需要一个中转作用的StuService,起一个在数据库和用户中间的参数传递的桥梁

public class Stuservice {
/**
    * findAll()
    */
   public List<Student> findAll(){
       //查询所有的时候不需要参数
       //这里定义需要传入的参数为空的集合
       Object[] parames ={};
       //调用StuDAO的方法findAll
       return sd.findAll(parames);
   }
   }

在测试类中进行测试

public class TestInfo {

    private StudentDAO sd = new StudentDAO();
    private Stuservice ss  = new Stuservice();
    /**
     * 查询所有
     */
    @Test
    public void testFindAll(){
        //调用StuService的方法findAll,返回一个结果集

        List<Student> list = ss.findAll();
        for (Student student : list) {
            System.out.println(student);
        }
    }


}
posted @ 2017-08-15 21:18  crr121  阅读(219)  评论(0编辑  收藏  举报