首页 > 代码库 > 利用反射和ResultSetMetaData实现DBUtils的基本功能

利用反射和ResultSetMetaData实现DBUtils的基本功能

  DBUtils大大简化了JDBC的书写,极大的提高了开发效率,和数据库连接池一起,简化了JDBC开发的流程.简易的自定义数据库连接池可以通过装饰者设计模式和动态代理模式得到很简单的实现,那么DBUtils应该怎么实现呢?为了了解DBUtils其内部工作的流程,我实现了一个自己的DBUtils工具类,实现一些简单的更新和查询操作.  

  ResultSetMetaData是可以获取ResultSet对象的列类型和属性信息的对象.这个类里面有很多方法,在这个案例中,只用到两个:getColumnCount():获取ResultSet结果集中列的数目.getColumnName(int column):根据指定的列数目获取列名.有了这两个方法就可以自己动手去实现一个简易版的DBUtils啦~下面是我实现的步骤:

  1.编写MyQueryRunner的executeUpdate方法.

  这个方法的编写非常简单,因为可以通过dataSource获取Connection,在方法的内部就是简单的jdbc操作.需要注意的是,需要手动设置传入的参数到PreparedStatement中.代码如下:

public int update(String sql, Object... params) {
        Connection connection=null;
        PreparedStatement preparedStatement=null;
        try {
            connection=dataSource.getConnection();
            preparedStatement=connection.prepareStatement(sql);
            for(int i=0;i<params.length;i++) {
                preparedStatement.setObject((i+1), params[i]);//设置参数.
            }
            int x=preparedStatement.executeUpdate();//执行更新操作.
            return x;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        finally {
            if(preparedStatement!=null) {
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(connection!=null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }

  2.编写MyQueryRunner的executeQuery方法.

  这个方法的实现也不难,因为我们将重要的代码交给传入的ResultSetHandler实现对象来处理.代码如下:

//查询会比较麻烦.
    //作出三个实现BeanHandler BeanListHandler MapListHandler
     public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params) {
         Connection connection=null;
            PreparedStatement preparedStatement=null;
            ResultSet rs=null;
            try {
                connection=dataSource.getConnection();
                preparedStatement=connection.prepareStatement(sql);
                if(params!=null) {
                    for(int i=0;i<params.length;i++) {
                        preparedStatement.setObject((i+1), params[i]);
                    }
                }
                rs=preparedStatement.executeQuery();
                return rsh.handle(rs);//交给处理器处理
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
            finally {
                if(rs!=null) {
                    try {
                        rs.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if(preparedStatement!=null) {
                    try {
                        preparedStatement.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if(connection!=null) {
                    try {
                        connection.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
     }

  3.3个处理器的编写.

  在MyQueryRunner实现自定义查询的实现中,我实现了三个处理器,它们分别是采用了BeanHandler,BeanListHandler,MapListHandler的实现思想,并且给出了最简单易懂(实际上是因为我水平不够= =)的实现.下面说说BeanHandler的实现流程,Handler的实现基本上都是一样的,无非是采用反射获得元素对象,并且将数据封装进去.

  BeanHandler中有两个成员变量,一个T t用来作为要返回的JavaBean,声明在外面供之后封装数据使用,一个Class type对象,用来获取Class,创建JavaBean对象赋值给t,创建Field对象为t的成员变量赋值.在方法的实现中,先通过getColumnCount方法来获取列的数目,遍历每一列,通过getColumnName(int columnCount)方法获取列名,用Class对象的getField方法获取Field对象(这里JavaBean的取值一定要和数据库中相同!否则会报错),再利用Field对象的set方法赋值.当然Field对象对应的成员变量一定是私有的(JavaBean的特性.)因此需要先调用setAccessable方法才可以.具体的代码如下:

public class MyBeanHandler<T> implements ResultSetHandler{
    private Class<T> type;
    T t;//需要封装的JavaBean
    public MyBeanHandler(Class<T> type) {
        this.type=type;
    }
    
    @Override
    public Object handle(ResultSet rs) throws SQLException {
        try {
            t=type.newInstance();
            ResultSetMetaData metaData = rs.getMetaData();
            int count=metaData.getColumnCount();//获取ResultSet中数据的列数
            rs.next();//移动指针
            //遍历获的每一列的列名,采用反射机制设置值
            for(int i=1;i<=count;i++) {
                String name=metaData.getColumnName(i);
                Object obj=rs.getObject(i);
                Field field = type.getDeclaredField(name);
                field.setAccessible(true);
                field.set(t, obj);//封装数据进入JavaBean
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return t;
    }

}

  MyBeanListHandler的实现步骤,基本和BeanHandler一致.唯一的区别就是采用结果集的next方法遍历每一条记录,而将对象的创建移动到了循环里,这里考虑返回的List对象的增删应该比较少因此采用的是ArrayList.具体实现如下:

public class MyBeanListHandler<T> implements ResultSetHandler<List<T>>{
    private Class<T> type;
    List<T> list=new ArrayList<T>();//可能获取比较多.因此采用ArrayList
    
    public MyBeanListHandler(Class<T> type) {
        this.type = type;
    }

    @Override
    public List<T> handle(ResultSet rs) throws SQLException {
        try {
            ResultSetMetaData metaData = rs.getMetaData();
            int count=metaData.getColumnCount();
            while(rs.next()) {
                T t=type.newInstance();
                for(int i=1;i<=count;i++) {
                    String name=metaData.getColumnName(i);//该方法获取列名.获取一系列字段名称.例如name,age...
                    Object obj=rs.getObject(i);//获取字段值
                    Field field = type.getDeclaredField(name);//获取field对象
                    field.setAccessible(true);
                    field.set(t, obj);//设置值
                }
                list.add(t);
            }
            return list;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

  MapList看上去似乎最复杂,但是实际上实现起来却由于没有采用泛型和反射,是最方便的,代码如下:

public class MyMapListHandler implements ResultSetHandler<List<Map<String, Object>>> {

    private List<Map<String,Object>> data=http://www.mamicode.com/new ArrayList<>();

    @Override
    public List<Map<String, Object>> handle(ResultSet rs) throws SQLException {
        ResultSetMetaData metaData = rs.getMetaData();
        int count=metaData.getColumnCount();
        while(rs.next()) {
            Map<String,Object> map=new HashMap<>();
            for(int i=1;i<=count;i++) {
                Object value=rs.getObject(i);
                String name=metaData.getColumnName(i);
                map.put(name, value);
            }
            data.add(map);
        }
        return data;
    }

}

 

利用反射和ResultSetMetaData实现DBUtils的基本功能