日期:2014-05-16  浏览次数:20683 次

dbUtils从使用到源码解析

最近工作关系需要对dbutils进行一些了解,来完善公司的测试DAO的框架封装。给本屌丝最大的感慨 麻雀虽小,五脏俱全。

dbutils是Apache组织开发的一个简单,小巧,又具有大部分功能的操作数据库的java组件,dbutis包含三个包,10几个类,够简单的吧!

使用dbutils也非常的简单,只需要初始化QueryRunner类,传入SQL语句,就可以进行增删改查的所有的操作,当然dbutils对查询也做了一些比较简单的查询的封装,主要是对ResultHandle做了一些处理。最终还是通过QueryRunner来进行的操作的。

?

QueryRunner可以直接new进行实例化,也有传入dataSource进行实例化的构造器,前者进行实例化之后,操作时需要传递Connnection参数,方便比较容易获得Connection的应用使用,而使用DataSource进行实例化就可以直接进行数据库操作了。这里特别的提醒一下。如果应用中存在多个数据库源,并且使用了proxool的数据库连接池,那必须设置数据库连接池的别名alias,这是因为数据库连接池在获取连接的时候首先通过别名来获得链接,如果都没有设置别名,将有可能获得错误的数据库连接,后果就严重了!大家可以看看这段代码就明了了。

?

 ConnectionPool cp = null;
        try {
            if (!ConnectionPoolManager.getInstance().isPoolExists(alias)) {
                registerPool();
            }
            cp = ConnectionPoolManager.getInstance().getConnectionPool(alias);
            return cp.getConnection();
        } catch (ProxoolException e) {
            LOG.error("Problem getting connection", e);
            throw new SQLException(e.toString());
        }

?

? ? 如果应用中已经集成了spring,那将dbutils集成进来就非常的简单了,直接通过spring对queryRunner进行管理,这里就不细说了!会用spring的人都知道该怎么做了。至于增删改这些东西都直接通过SQL语句,比较直观,再封装也省不了多少工作量,所以今天主要介绍一下关于将resultSet转化成javaBean方面的转换。

?

实际上,dbutils对一些简单的resultSet-->Bean是有做一个处理器的,主要通过BeanProcessor来对resultSet到Bean的转换,如果不涉及到一些诸如Calendar,Enum,自定义特殊的类型或者说不涉及到一些关联的话,简单的javaBean是可以满足需求的。代码也不复杂:

?

BeanProcessor beanProcessor = new BeanProcessor();
		RowProcessor rowProcessor = new BasicRowProcessor(beanProcessor);
		BeanHandler<T> handle = new BeanHandler<T>(beanClass, rowProcessor);
//进行查询
T t = queryRunner.query(sql, handle);
?

这几行代码应该比较容易看懂了,很简单的几行代码就可以进行Bean的转换了,除了bean的转化,还有toBeanList,toBeanMap的转换,这些都可以轻松的做到。

?

但是往往需求就不是那么简单的, 我们的实体类是hibernate的实体类,里面进行一些实体之间的关联,同时还有我们对枚举类型进行了一些特殊的封装,使hibernate保存在数据库中的数据是我们自定义的一个数字或者是一个标识符,这个大家都肯定都能想到,这些特殊的类型无法直接从数据库保存的值直接转化成我们自己的类型,需要我们对这种特殊类型进行一些改造。在进行改造的时候,我们应该先知道怎么从columnToPerporty这样的一个过程。

?

首先,我们知道hibernate对实体的映射是通过反射来实例化实体类,获取属性来设置值的,dbutils也是一样,他通过一个默认的规则使得列名和属性名对应,然后找到需要设置值的属性名,当在实体中没有找到相关的属性时,就不设置,这样首先要解决的问题是,怎么把他的属性映射的默认规则改成我们自己的规则。对于这种属性和列对应的处理我们通过查阅BeanProcessor的源码来看默认的映射规则。

?

?

protected int[] mapColumnsToProperties(ResultSetMetaData rsmd,
            PropertyDescriptor[] props) throws SQLException {

        int cols = rsmd.getColumnCount();
        int columnToProperty[] = new int[cols + 1];
        Arrays.fill(columnToProperty, PROPERTY_NOT_FOUND);

        for (int col = 1; col <= cols; col++) {
            String columnName = rsmd.getColumnLabel(col);
            if (null == columnName || 0 == columnName.length()) {
              columnName = rsmd.getColumnName(col);
            }
            for (int i = 0; i < props.length; i++) {

                if (columnName.equalsIgnoreCase(props[i].getName())) {
                    columnToProperty[col] = i;
                    break;
                }
            }
        }

        return columnToProperty;
    }
?

找到这个方法,这个方法我一开始看的时候感觉怪怪的,怎么是个嵌套循环呢,不知道当时作者是怎么想的,我想了想,这样的嵌套循环最多的次数可能为 cols.length *?cols.length,当然对于计算机来说这也许算不了什么,但是这样的写法的可读性毕竟是不太好的,所以我在改造的时候对这段代码进行重载,再来看看这段代码里面的关键的一句,让我们知道dbutils的列与属性的对应的默认的规则:columnName.equalsIgnoreCase(props[i].getName(),将列名与属性名称忽略大小写进行比较,如果比较相等,就对应上了。

?

麻烦来了,这样的对应关系跟我们的系统的hibernate的映射规则不匹配,在实体中我们使用驼峰表达式,而在数据库中我们遵循驼峰使用_来分开,还有更麻烦的是我们的hibernate中的映射不全遵循驼峰表达式(例如:phoneOrTel-->phone_or_tel),还有一些关联的实体是直接通过注解写在属性上方来进行列名映射的。如果这个映射规则不改变,那我们就无法使用BeanProcessor来进行记录集与bean的直接转换了。或许可以通过一个结果处理器重新来写,但是这样的话每张表都需要写ResultHandler来处理,而且需要一个

列一列进行转换,这样产生出来的工作量非常大,而且就算