`
dean_liu
  • 浏览: 75172 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

扩展ApacheCommos的DbUtils

    博客分类:
  • Java
阅读更多
    ApacheCommos的DbUtils是一个简单好用的轻量级的数据库操作工具,该项目的主页是:http://commons.apache.org/dbutils/,关于它的信息可以从那里获取.
    请看代码:
QueryRunner run = new QueryRunner(dataSource);

// Use the BeanListHandler implementation to convert all
// ResultSet rows into a List of Person JavaBeans.
ResultSetHandler<List<Person>> h = new BeanListHandler(Person.class);

// Execute the SQL statement and return the results in a List of
// Person objects generated by the BeanListHandler.
List<Person> persons = run.query("SELECT * FROM Person", h);

    从以上代码可看出dbutils可以把查询出来的结果集映射成Bean的List,这是个很有用的功能,不过有一个很大限制,规定数据库字段名必须跟Bean的属性名一致,例如字段名为"my_name",那么属性名也必须为"my_name".
    这个限制的影响还是比较大的,这样好像BeanListHandler就没有什么作用了?而我希望的是该工具可以定制各种各样的结果集与Bean的映射规则.比如,可以使用"驼峰规则"来匹配,字段"MY_NAME"对应的属性为"myName",或者是直接指定一个二维数组定义字段与属性之间的对应关系.
    说到这里,我们的扩展目标也出来.现在,看一下dbutils的源代码是否有这方面的扩展点.先看一下BeanListHandler的相关代码.
...
public class BeanListHandler<T> implements ResultSetHandler<List<T>> {

    /**
     * The Class of beans produced by this handler.
     */
    private final Class<T> type;

    /**
     * The RowProcessor implementation to use when converting rows 
     * into beans.
     */
    private final RowProcessor convert;

    public BeanListHandler(Class<T> type) {
        this(type, ArrayHandler.ROW_PROCESSOR);
    }

    public BeanListHandler(Class<T> type, RowProcessor convert) {
        this.type = type;
        this.convert = convert;
    }

    public List<T> handle(ResultSet rs) throws SQLException {
        return this.convert.toBeanList(rs, type);
    }
}

   
    根据上面的代码片断可以看出,BeanListHandler的功能是由convert(RowProcessor)提供的,而convert的默认实现为ArrayHandler.ROW_PROCESSOR.再看一下ArrayHandler.ROW_PROCESSOR的相关代码.
public class ArrayHandler implements ResultSetHandler<Object[]> {

    ...

    static final RowProcessor ROW_PROCESSOR = new BasicRowProcessor();

    ...
}


    很明显BeanListHandler是依赖BasicRowProcessor来完成相应的功能的.不过,这不是终点,继续看一下,会发现,真实完成字段与属性映射功能的是类BeanProcessor,再看一下BeanProcessor的源码.
...
public class BeanProcessor {

    ...

    public <T> List<T> toBeanList(ResultSet rs, Class<T> type) throws SQLException {
        List<T> results = new ArrayList<T>();

        if (!rs.next()) {
            return results;
        }

        PropertyDescriptor[] props = this.propertyDescriptors(type);
        ResultSetMetaData rsmd = rs.getMetaData();
        int[] columnToProperty = this.mapColumnsToProperties(rsmd, props);

        do {
            results.add(this.createBean(rs, type, props, columnToProperty));
        } while (rs.next());

        return results;
    }

    ...

    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;
    }

    ...
}

    阅读以上代码可知,方法toBeanList提供将结果集映射成包括Bean的List的功能,方法内部使用数组columnToProperty来存放各字段对应属性的索引位置,而此数组是由方法mapColumnsToProperties来提供的,而且此方法是通过columnName.equalsIgnoreCase(props[i].getName())来确定指定字段对应的属性的,就是这里造成了上面提到的限制.那么很明显这里就是要找的扩展点了.只要重写此方法就可以实现了,比如文章开头提到的各种需求.不过我觉得还有更好的扩展方法,例如可以把匹配字段名与属性名这个逻辑抽象出来作为一种策略,并且提供常用的策略以供选择,而且有新的需求同样可以通过提供新的策略来实现.这样做还有另一个好处,就是把扩展时的关注点范围缩小,集中到"匹配"这一点上来,而不去关心其它问题.这样的好处可想而知,可以很容易的写出实现与明确的单元测试.
    好了,以下为我的扩展的实现,首先是StrategyBeanProcessor:
package com.yang.commons.dbutils;

import java.beans.PropertyDescriptor;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Arrays;

import org.apache.commons.dbutils.BeanProcessor;

/**
 * 策略模式的BeanProcessor
 * @author 刘晓阳
 * @since 2010-02-05
 */
public class StrategyBeanProcessor extends BeanProcessor {
	
	private Matcher matcher;
	
	public StrategyBeanProcessor(){
		// 默认Matcher
		matcher = new EqualsIgnoreCaseMatcher();
	}
	
	public StrategyBeanProcessor(Matcher matcher){
		this.matcher = matcher;
	}
	
	public Matcher getMatcher() {
		return matcher;
	}

	public void setMatcher(Matcher matcher) {
		this.matcher = matcher;
	}

	/**
	 * 重写BeanProcessor的实现,使用策略模式
	 */
	protected int[] mapColumnsToProperties(ResultSetMetaData rsmd,
            PropertyDescriptor[] props) throws SQLException {
		if (matcher == null)
			throw new IllegalStateException("Matcher must be setted!");
		
        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 (matcher.match(columnName, props[i].getName())) {//与BeanProcessor不同的地方
                    columnToProperty[col] = i;
                    break;
                }
            }
        }

        return columnToProperty;
    }
}


    StrategyBeanProcessor重写了BeanProcessor的mapColumnsToProperties方法,把原先写死的字段名与属性名的匹配逻辑交由Matcher来实现.Matcher是一个接口,它是"字段名与属性名是否匹配"的抽象.继续,下面是接口Matcher:
package com.yang.commons.dbutils;

/**
 * 字段名与属性名的匹配器
 * @author 刘晓阳
 * @since 2010-02-05
 */
public interface Matcher {
	
	/**
	 * 是否匹配
	 * @param columnName 字段名
	 * @param propertyName 属性名
	 * @return 匹配结果
	 */
	boolean match(String columnName, String propertyName);
}


    接口Matcher非常简单,只有一个方法match.再看一下两个常用实现,分别是MappingMatcher与HumpMatcher:
package com.yang.commons.dbutils;

import java.util.HashMap;
import java.util.Map;

/**
 * 二维数组映射的匹配器
 * @author 刘晓阳
 * @since 2010-02-05
 */
public class MappingMatcher implements Matcher {
	
	private Map<String, String> map = null;
	
	public MappingMatcher(String [][] mapping){
		if (mapping == null)
			throw new IllegalArgumentException();
		
		map = new HashMap<String, String>();
		for (int i = 0; i < mapping.length; i++){
			String columnName = mapping[i][0];
			if (columnName != null)
				map.put(columnName.toUpperCase(), mapping[i][1]);
		}
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.yang.commons.dbutils.Matcher#match(java.lang.String, java.lang.String)
	 */
	public boolean match(String columnName, String propertyName) {
		if (columnName == null)
			return false;
		String pName = map.get(columnName.toUpperCase());
		if (pName == null)
			return false;
		else {
			return pName.equals(propertyName);
		}
	}
}

package com.yang.commons.dbutils;

/**
 * 驼峰转换的匹配器
 * 
 * @author 刘晓阳
 * @since 2010-02-05
 */
public class HumpMatcher implements Matcher {

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.yang.commons.dbutils.Matcher#match(java.lang.String,
	 * java.lang.String)
	 */
	public boolean match(String columnName, String propertyName) {
		if (columnName == null)
			return false;

		columnName = columnName.toLowerCase();
		String[] _ary = columnName.split("_");
		StringBuilder strBuilder = new StringBuilder();
		for (int i = 0; i < _ary.length; i++) {
			String str = _ary[i];
			if (!"".equals(str) && i > 0) {
				StringBuilder _builder = new StringBuilder();
				str = _builder.append(str.substring(0, 1).toUpperCase()).append(str.substring(1)).toString();
			}
			strBuilder.append(str);
		}
		return strBuilder.toString().equals(propertyName);
	}

}

    有了这样的扩展,以后就可以这样写代码了:
QueryRunner run = new QueryRunner(dataSource);

ResultSetHandler<List<Person>> h = new BeanListHandler(Person.class, new BasicRowProcessor(new StrategyBeanProcessor(new HumpMatcher())));

List<Person> persons = run.query("SELECT * FROM Person", h);

    现在可以满足文章前面提出的需求了,并且在有新的需求出现的情况下,也可以很容易的满足.

    注:附件提供了源代码
   
3
0
分享到:
评论
3 楼 zhaoyangjay 2010-03-10  
谢谢   
2 楼 zhaoyangjay 2010-03-10  
佩服,拿来用了谢谢
1 楼 dean_liu 2010-02-06  
补充一下,请看下面的代码:
...   
public class BeanProcessor {   

    ...   
  
    protected int[] mapColumnsToProperties(ResultSetMetaData rsmd,   
            PropertyDescriptor[] props) throws SQLException {   
  
        ...
  
        for (int col = 1; col <= cols; col++) {   
            String columnName = rsmd.getColumnLabel(col);   
            ...
        }   
  
        ...
    }   
  
    ...   
}  

因为字段名是通过方法rsmd.getColumnLabel(col)来取得的,所以还可以把字段名与属性名的映射关系写在SQL语句里面,如:
SELECT ID as id, MY_NAME as myName FROM MY_TABLE

相关推荐

    dbutils dbutils dbutils dbutils

    dbutils dbutils dbutils dbutils

    DbUtils扩展源码

    ApacheCommos的DbUtils是一个简单好用的轻量级的数据库操作工具,该项目的主页是:http://commons.apache.org/dbutils/,关于它的信息可以从那里获取. dbutils可以把查询出来的结果集映射成Bean的List,这是个很有用的...

    python类DBUtils安装包

    DBUtils 是一套允许线程化 Python 程序可以安全和有效的访问数据库的模块。DBUtils已经作为 Webware for Python 一部分用来结合 PyGreSQL 访问 PostgreSQL 数据库,当然他也可以用在其他Python应用程序中来访问 DB-...

    commons-dbutils-1.7-API文档-中文版.zip

    赠送jar包:commons-dbutils-1.7.jar; 赠送原API文档:commons-dbutils-1.7-javadoc.jar; 赠送源代码:commons-dbutils-1.7-sources.jar; 赠送Maven依赖信息文件:commons-dbutils-1.7.pom; 包含翻译后的API文档...

    模仿DBUtils(自己模仿DBUtils写的简易DBUtils)

    模仿DBUtils(自己模仿Commons DBUtils写的简易DBUtils),欢迎大家下载学习,这不是Apache旗下的DBUtils哦~

    DBUtils操作数据库以及事物的管理

    DBUtils操作数据库以及事物的管理

    commons-dbutils-1.7.jar

    commons-dbutils-1.7.jarcommons-dbutils-1.7.jarcommons-dbutils-1.7.jar

    commons-dbutils-1.7-API文档-中英对照版.zip

    赠送jar包:commons-dbutils-1.7.jar; 赠送原API文档:commons-dbutils-1.7-javadoc.jar; 赠送源代码:commons-dbutils-1.7-sources.jar; 赠送Maven依赖信息文件:commons-dbutils-1.7.pom; 包含翻译后的API文档...

    commons-dbutils-1.3.zip

    这里分享的是commons-dbutils-1.3,解压打开导入commons-dbutils-1.3.jar即可

    commons-dbutils-1.7.zip

    dbutils

    commons-dbutils-1.6.jar包

    包org.apache.commons.dbutils DbUtils是一个为简化JDBC操作的小类库. 接口摘要 ResultSetHandler 将ResultSet转换为别的对象的工具. RowProcessor 将ResultSet行转换为别的对象的工具. 类摘要 BasicRowProcessor ...

    python-dbutils 简单封装

    python dbutils 简单封装

    dbutils api

    dbutils api

    commons-dbutils.jar.rar

    commons-dbutils.jar jdbc工具类jar包 1.3、1.6、1.7三个版本

    commons-dbutils-1.4.jar

    commons-dbutils-1.4.jar

    C3P0和DButils

    c3p0-0.9.1.2.jar、commons-dbutils-1.4.jar、mysql-connector-java-5.1.39-bin.jar

    Dbutils项目实例

    DBUtils包括3个包: org apache commons dbutils org apache commons dbutils handlers org apache commons dbutils wrappers DBUtils封装了对JDBC的操作 简化了JDBC操作 可以少写代码 org apache commons dbutils ...

    commons-dbutils-1.7.rar

    commons-dbutils-1.7.jar,commons-dbutils-1.7-javadoc.jar,commons-dbutils-1.7-sources.jar,commons-dbutils-1.7-tests.jar,commons-dbutils-1.7-test-sources.jar

Global site tag (gtag.js) - Google Analytics