dbutils和反射实现ORM问题整理

原创 2017年09月09日 07:02:29

背景

时间过的真快,用dbutils自定义ORM竟然已经是7月中旬的事情了,本周才真正用到那些代码,当初简单实现的MyBeanHandler类还有一些缺陷,整理修正流程如下。

实现流程

基本思路是这样的:需要将数据库的一条查询结果,转换成Java实体类的实例,自定义一个ResultHandler,接受一个类型Class。

由于select列可能不是Java全部的属性,所以不能遍历Java属性列表,逐个从ResultSet获取该列的值,一旦某一列没有被查出来,ResultSet的getObject(entityField)的时候直接报异常了。所以转换思路,以查询结果列,反着找到该列对应的Java类的属性,再用反射调用属性的setter方法,完成实例的组装。

此外,需要注意Java实体类属性的类型和数据库表列的类型,如果存在不一致的话,需要特殊处理。例如:Java中boolean类型的属性,对应到数据库中是tinyint。还有一个特别重要的问题就是ResultSet中获取的元数据信息中,所有的数值列都是Java的封装类型,即:tiny/int/bigint对应的Class是Integer.class的。

Java的封装类型和基本类型的Class是不同的,int/long/byte/char/float的Class是对应的类型名称.class的,如int.class,long.class。所以调用setter方法触发反射的类型一定要传递Java实体属性的类型。

反射获取类属性

开发过程中有些模块的实体有公共属性,所以有不少实体类是有基础父类的,这些实体类的表字段,除了有自己定义的属性外,还必须包括父类的属性。

反射获取实体字段的方法为:

    /**
     * 判断某个属性是否是简单属性:所有的简单类型和对应的包装类型都是
     * 
     * @param fieldType
     * @return
     */
    public static boolean isSimpleTypeField(Class<?> fieldType) {
        if (fieldType == Integer.class ||fieldType == int.class || fieldType == long.class ||fieldType == Long.class || fieldType == String.class
                || fieldType == Short.class || fieldType == Character.class) {
            return true;
        }

        return false;
    }

    /**
     * 判断某个属性是否是boolean类型,有两种包装类型和原始boolean类型
     * 
     * @param fieldType
     * @return
     */
    public static boolean isBooleanTypeField(Class<?> fieldType) {
        if (fieldType == Boolean.class ||fieldType==boolean.class) {
            return true;
        }

        return false;
    }

/**
     * 获取某一个类的fieleName对应的Field对象,如果当前类没有,从父类获取
     * @comment 异常,不处理
     * @param entityClass
     * @param fieldName
     * @return
     */
    public static Field getFieldOfEntity(Class<?> entityClass,String fieldName){
        if(fieldName==null){
            return null;
        }

        try {
            return entityClass.getDeclaredField(fieldName);
        } catch (NoSuchFieldException e) {
            try {
                return entityClass.getSuperclass().getDeclaredField(fieldName);
            } catch (NoSuchFieldException e1) {

            } catch (SecurityException e1) {

            }
        } catch (SecurityException e) {

        }

        return null;
    }

此外,数据库表列名称和Java属性setter直接的映射关系,可以自定义的,通用的方法是二者名称一致,对应的就是setXXX,getXXX,isXXX(boolean类型)。如果数据库列表是下划线,那么就是另一种映射关系,这些都可以自定义映射规则的。

那么获取列的getter/setter方法为:

/**
     * 获取某个属性的get方法名称
     * 
     * @param name
     * @return
     */
    public static String obtainGetterName(String name,boolean isBooleanType) {
        //特殊处理boolean类型,统一为isXXX
        if(isBooleanType){
            return "is" + name.substring(0, 1).toUpperCase()+name.substring(1);
        }

        return "get" + name.substring(0, 1).toUpperCase()+name.substring(1);
    }

    /**
     * 获取某个属性的set方法名称
     * 
     * @param name
     * @return
     */
    public static String obtainSetterName(String name) {
        return "set" + name.substring(0, 1).toUpperCase()+name.substring(1);
    }

自定义BeanHandler

按上述思路修正原来实现的BeanHandler如下:

public class MyBeanHandler<T> implements ResultSetHandler<T> {

    private Logger logger = Logger.getLogger(MyBeanHandler.class);

    private Class<T> entityClass;

    public MyBeanHandler(Class<T> entityClass) {
        this.entityClass = entityClass;
    }

    @Override
    public T handle(ResultSet resultSet) {

        try {

            while (resultSet.next()) {
                T result = null;
                result = entityClass.newInstance();
                ResultSetMetaData metaData = resultSet.getMetaData();
                int columnCount = metaData.getColumnCount();
                for(int col = 1 ; col <= columnCount ; col++){
                    String name = metaData.getColumnName(col);
                    Object oldValue = resultSet.getObject(col);
                    Object columnValue = oldValue;
                    if(columnValue==null){//空,直接过滤掉。。。。
                        continue;
                    }

                    //获取该字段名称对应的Java实体类字段声明:updated on 2017-09-05
                    Field field = IntrospectUtil.getFieldOfEntity(entityClass,name);
                    if(field==null){
                           continue;
                    }

                    Class<?> columnType = field.getType();
                    if (ArrayList.class == columnType || List.class == columnType) {
                        // 数据库存储的是JSON,回转成List对象
                        List<Object> list = JSONObject.parseArray(columnValue.toString(), Object.class);
                        columnValue = list;
                    } else if (IntrospectUtil.isSimpleTypeField(columnType)) {
                        // non op 普通属性,直接设置值

                    } else if(IntrospectUtil.isBooleanTypeField(columnType)){
                        columnValue = (Integer)oldValue==1 ? Boolean.TRUE:Boolean.FALSE;
                    }else {
                        // 复杂对象,且非List,通过JSON转换为对应类型的对象
                        columnValue = JSONObject.parseObject(columnValue.toString(), columnType);
                    }

                    //找到对应简单字段的原始字段类型,作为setter的方法类型
                    String setter = IntrospectUtil.obtainSetterName(name);
                    Method method = entityClass.getMethod(setter, columnType);
                    method.invoke(result, columnValue);
                }

                return result;
            }
        } catch (Exception e) {
            logger.error("MyBeanHandler error", e);
        }

        return null;
    }
}

启示录

反射应用时,Class类的getDeclaredField能获取当前类的所有属性,静态、非静态的都能获取到。如果插入操作需要根据实体属性集合类定义insert后面的字段,需要排除掉静态成员变量。

此外,父类的成员变量即使定义成protected的,也不能直接被子类用getDeclaredField方法获取到;但是getMethod方法不仅能获取到自己的方法,同时也能获取父类继承来的方法,所以Method不存在需要特别注意的地方。

代码不用就不知道有坑啊,demo编写过程中还是太简单,所以发现不了问题。编程路漫漫呐!

而且生活时不时还给你一点小打击,毕小宝不到三岁半,刚上幼儿园一个月,昨天惊闻幼儿园缴费上的课程跟其他小朋友单独报的课程不一样,突然就感受到了深深的伤害,一整天心情都很沉重。我该怎么跟这个社会对抗,才能全身而退呢?我的孩子又该怎么面对将来的社会环境呢。又是一个大命题!

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

带条件的分页【重点】

带条件的分页属于重点内容,很复杂的逻辑,相互学习下吧~

dbutils工具包分析及应用

背景项目开发过程中,需要更换数据库,DAO层抽象了一个顶层的父类,以此类着手重写一整套操作,可以将换库工作量降低到最少。那么工作量就落在了解决ORM映射问题上,原来使用的BSONObject工具包,直...

ORM底层封装( JDBC \ DBUtils)

.....请大神无视我! 数据库 : JDBC+DBUtils 数据源 : C3P0 oracle驱动(可自扩展mysql驱动) 源代码分享...

Java轻量级ORM工具--DbUtils介绍

一、Apache Commons DbUtils简介 Apache的DbUtils工具是一个轻量级的持久层解决方案,天生为性能而生,它简单的对JDBC进行了必要的操作封装,让开发人员能够...
  • wyc_cs
  • wyc_cs
  • 2016年05月20日 09:35
  • 704

Java轻量级ORM工具--DbUtils使用

一、概述 DbUtil是一个非常经量级的ORM工具,不属于一个ORM框架,只提供数据库操作的简单实现,包含增、删、改、查、批量以及事务等操作。 二、功能介绍 采用配置文件...
  • wyc_cs
  • wyc_cs
  • 2016年05月20日 09:36
  • 6153

反射模拟DbUtils实现ResultSet转成Bean实例

前几天接触到了apache的一个小框架DbUtils,真的被其优雅的设计所震撼到了,尤其是其中的MyBean mybean = QueryRunner.query(sqlConnection,sqlS...

使用Java的反射简单的实现ORM框架(五)

用反射机制能干什么事          刚开始在使用jdbc时侯,在编写访问数据库时写到想吐,有八个表,每个表都有增删改查中操作      那时候还不知道有反射机制这个概念,所以就对不同的表创建不同的...

用反射实现将数据自动填充到对象中,学习orm的半个功能

最近整理以前的资料,想起来了这个小东西,以前感觉反射效率应该不行,后来翻看了nhibernate的源代码彻底改变了我的观点,随后自己动手写了这个小东西玩玩,大牛就不用看了,对反射有意思的可以瞧瞧   ...
  • Asa_Jim
  • Asa_Jim
  • 2015年07月12日 16:42
  • 377

Dbutils Sqlserver Update 占位符问题

使用DButils,修改sqlserver2000数据库,代码如下: public static void updatetest() { Connection conn = getCon...

使用DBUtils连接Sqlserver插入失败的问题

使用DBUtils连接Sqlserver插入失败的问题 帅宏军 一、问题描述: 使用DBUtils对数据库Sqlserver进行插入操作时,失败,提示参数“?”不可识别。代码如下 ...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:dbutils和反射实现ORM问题整理
举报原因:
原因补充:

(最多只允许输入30个字)