允许随意转载,但请注明出处及作者。
=========================================
Jakarta-Common-BeanUtils研究心得(2)
SonyMusic
2003.05.13
六、ConstructorUtils补遗
创建对象还有一个方法:invokeExactConstructor,该方法对参数要求
更加严格,传递进去的参数必须严格符合构造方法的参数列表。
例如:
Object[] args={new Integer(1), "Jan"};
Class[] argsType={int.class, String.class};
Object obj;
//下面这句调用将不会成功,因为args[0]的类型为Integer,而不是int
//obj = ConstructorUtils.invokeExactConstructor(Month.class, args);
//这一句就可以,因为argsType指定了类型。
obj = ConstructorUtils.invokeExactConstructor(Month.class, args, argsType);
Month month=(Month)obj;
System.out.println(BeanUtils.getProperty(month,"value"));
七、MethodUtils
与ConstructorUtils类似,不过调用的时候,通常需要再指定一个method name的参数。
八、DynaClass/DynaBean
这似乎是BeanUtils中最有趣的部分之一了,很简单,简单到光看这两个接口中的方法会不明白
为什么要设计这两个接口。不过看到ResultSetDynaClass后,就明白了。下面是java doc中的代码:
ResultSet rs = ...;
ResultSetDynaClass rsdc = new ResultSetDynaClass(rs);
Iterator rows = rsdc.iterator();
while (rows.hasNext()) {
DynaBean row = (DynaBean) rows.next();
... process this row ...
}
rs.close();
原来这是一个ResultSet的包装器,ResultSetDynaClass实现了DynaClass,它的iterator方法返回一个
ResultSetIterator,则是实现了DynaBean接口。
在获得一个DynaBean之后,我们就可以用
DynaBean row = (DynaBean) rows.next();
System.out.println(row.get("field1")); //field1是其中一个字段的名字
再看另一个类RowSetDynaClass的用法,代码如下:
String driver="com.mysql.jdbc.Driver";
String url="jdbc:mysql://localhost/2hu?useUnicode=true&characterEncoding=GBK";
String username="root";
String password="";
java.sql.Connection con=null;
PreparedStatement ps=null;
ResultSet rs=null;
try {
Class.forName(driver).newInstance();
con = DriverManager.getConnection(url);
ps=con.prepareStatement("select * from forumlist");
rs=ps.executeQuery();
//先打印一下,用于检验后面的结果。
while(rs.next()){
System.out.println(rs.getString("name"));
}
rs.beforeFirst();//这里必须用beforeFirst,因为RowSetDynaClass只从当前位置向前滚动
RowSetDynaClass rsdc = new RowSetDynaClass(rs);
rs.close();
ps.close();
List rows = rsdc.getRows();//返回一个标准的List,存放的是DynaBean
for (int i = 0; i DynaBean b=(DynaBean)rows.get(i); System.out.println(b.get("name")); } } catch (Exception e) { e.printStackTrace(); } finally{ try { con.close(); } catch (Exception e) { } } 是不是很有趣?封装了ResultSet的数据,代价是占用内存。如果一个表有10万条记录,rsdc.getRows() 就会返回10万个记录。@_@ 需要注意的是ResultSetDynaClass和RowSetDynaClass的不同之处: 1,ResultSetDynaClass是基于Iterator的,一次只返回一条记录,而RowSetDynaClass是基于 List的,一次性返回全部记录。直接影响是在数据比较多时ResultSetDynaClass会比较的快速, 而RowSetDynaClass需要将ResultSet中的全部数据都读出来(并存储在其内部),会占用过多的 内存,并且速度也会比较慢。 2,ResultSetDynaClass一次只处理一条记录,在处理完成之前,ResultSet不可以关闭。 3,ResultSetIterator的next()方法返回的DynaBean其实是指向其内部的一个固定 对象,在每次next()之后,内部的值都会被改变。这样做的目的是节约内存,如果你需要保存每 次生成的DynaBean,就需要创建另一个DynaBean,并将数据复制过去,下面也是java doc中的代码: ArrayList results = new ArrayList(); // To hold copied list ResultSetDynaClass rsdc = ...; DynaProperty properties[] = rsdc.getDynaProperties(); BasicDynaClass bdc = new BasicDynaClass("foo", BasicDynaBean.class, rsdc.getDynaProperties()); Iterator rows = rsdc.iterator(); while (rows.hasNext()) { DynaBean oldRow = (DynaBean) rows.next(); DynaBean newRow = bdc.newInstance(); PropertyUtils.copyProperties(newRow, oldRow); results.add(newRow); } 事实上DynaClass/DynaBean可以用于很多地方,存储各种类型的数据。自己想吧。嘿嘿。 九、自定义的CustomRowSetDynaClass 两年前写过一个与RowSetDynaClass目标相同的类,不过多一个功能,就是分页,只取需要的数据, 这样内存占用就会减少。 先看一段代码: String driver="com.mysql.jdbc.Driver"; String url="jdbc:mysql://localhost/2hu?useUnicode=true&characterEncoding=GBK"; String username="root"; String password=""; java.sql.Connection con=null; PreparedStatement ps=null; ResultSet rs=null; try { Class.forName(driver).newInstance(); con = DriverManager.getConnection(url); ps=con.prepareStatement("select * from forumlist order by name"); rs=ps.executeQuery(); /* while(rs.next()){ System.out.println(rs.getString("name")); } rs.beforeFirst(); */ //第二个参数表示第几页,第三个参数表示页的大小 CustomRowSetDynaClass rsdc = new CustomRowSetDynaClass(rs, 2, 5); //RowSetDynaClass rsdc = new RowSetDynaClass(rs); rs.close(); ps.close(); List rows = rsdc.getRows(); for (int i = 0; i DynaBean b=(DynaBean)rows.get(i); System.out.println(b.get("name")); } } catch (Exception e) { e.printStackTrace(); } finally{ try { con.close(); } catch (Exception e) { } } 在这里用到了一个CustomRowSetDynaClass类,构造方法中增加了page和pageSize两个参数, 这样,不管数据库里有多少条记录,最多只取pageSize条记录,若pageSize==-1,则功能和 RowSetDynaClass一样。这在大多数情况下是适用的。该类的代码如下: package test.jakarta.commons.beanutils; import java.io.*; import java.sql.*; import java.util.*; import org.apache.commons.beanutils.*; /** * @author SonyMusic * * To change this generated comment edit the template variable "typecomment": * Window>Preferences>Java>Templates. * To enable and disable the creation of type comments go to * Window>Preferences>Java>Code Generation. */ public class CustomRowSetDynaClass implements DynaClass, Serializable { // ----------------------------------------------------------- Constructors /** * Construct a new {@link RowSetDynaClass} for the specified * * to column names in the result set will be lower cased. * * @param resultSet The result set to be wrapped * * @exception NullPointerException if * is * @exception SQLException if the metadata for this result set * cannot be introspected */ public CustomRowSetDynaClass(ResultSet resultSet) throws SQLException { this(resultSet, true); } /** * Construct a new {@link RowSetDynaClass} for the specified * * to the column names in the result set will be lower cased or not, * depending on the specified ResultSet. The property names correspondingresultSetnullResultSet. The property names correspondinglowerCase value.
*
*
WARNING - If you specify false
* for lowerCase, the returned property names will
* exactly match the column names returned by your JDBC driver.
* Because different drivers might return column names in different
* cases, the property names seen by your application will vary
* depending on which JDBC driver you are using.
*
* @param resultSet The result set to be wrapped
* @param lowerCase Should property names be lower cased?
*
* @exception NullPointerException if resultSet
* is null
* @exception SQLException if the metadata for this result set
* cannot be introspected
*/
public CustomRowSetDynaClass(ResultSet resultSet, boolean lowerCase)
throws SQLException {
this(resultSet, 1, -1, lowerCase);
}
public CustomRowSetDynaClass(
ResultSet resultSet,
int page,
int pageSize,
boolean lowerCase)
throws SQLException {
if (resultSet == null) {
throw new NullPointerException();
}
this.lowerCase = lowerCase;
this.page = page;
this.pageSize = pageSize;
introspect(resultSet);
copy(resultSet);
}
public CustomRowSetDynaClass(ResultSet resultSet, int page, int pageSize)
throws SQLException {
this(resultSet, page, pageSize, true);
}
// ----------------------------------------------------- Instance Variables
/**
*
Flag defining whether column names should be lower cased when
* converted to property names.
*/
protected boolean lowerCase = true;
protected int page = 1;
protected int pageSize = -1;
/**
*
The set of dynamic properties that are part of this
* {@link DynaClass}.
*/
protected DynaProperty properties[] = null;
/**
*
The set of dynamic properties that are part of this
* {@link DynaClass}, keyed by the property name. Individual descriptor
* instances will be the same instances as those in the
* properties list.
*/
protected Map propertiesMap = new HashMap();
/**
*
The list of {@link DynaBean}s representing the contents of
* the original ResultSet on which this
* {@link RowSetDynaClass} was based.
*/
protected List rows = new ArrayList();
// ------------------------------------------------------ DynaClass Methods
/**
*
Return the name of this DynaClass (analogous to the
* getName() method of java.lang.Class
* allows the same DynaClass implementation class to support
* different dynamic classes, with different sets of properties.
*/
public String getName() {
return (this.getClass().getName());
}
/**
*
Return a property descriptor for the specified property, if it
* exists; otherwise, return null.
*
* @param name Name of the dynamic property for which a descriptor
* is requested
*
* @exception IllegalArgumentException if no property name is specified
*/
public DynaProperty getDynaProperty(String name) {
if (name == null) {
throw new IllegalArgumentException("No property name specified");
}
return ((DynaProperty) propertiesMap.get(name));
}
/**
*
Return an array of ProperyDescriptors for the properties
* currently defined in this DynaClass. If no properties are defined, a
* zero-length array will be returned.
*/
public DynaProperty[] getDynaProperties() {
return (properties);
}
/**
*
Instantiate and return a new DynaBean instance, associated
* with this DynaClass. NOTE - This operation is not
* supported, and throws an exception.
*
* @exception IllegalAccessException if the Class or the appropriate
* constructor is not accessible
* @exception InstantiationException if this Class represents an abstract
* class, an array class, a primitive type, or void; or if instantiation
* fails for some other reason
*/
public DynaBean newInstance()
throws IllegalAccessException, InstantiationException {
throw new UnsupportedOperationException("newInstance() not supported");
}
// --------------------------------------------------------- Public Methods
/**
*
Return a List containing the {@link DynaBean}s that
* represent the contents of each Row from the
* ResultSet that was the basis of this
* {@link RowSetDynaClass} instance. These {@link DynaBean}s are
* disconnected from the database itself, so there is no problem with
* modifying the contents of the list, or the values of the properties
* of these {@link DynaBean}s. However, it is the application's
* responsibility to persist any such changes back to the database,
* if it so desires.
*/
public List getRows() {
return (this.rows);
}
// ------------------------------------------------------ Protected Methods
/**
*
Copy the column values for each row in the specified
* ResultSet into a newly created {@link DynaBean}, and add
* this bean to the list of {@link DynaBean}s that will later by
* returned by a call to getRows().
*
* @param resultSet The ResultSet whose data is to be
* copied
*
* @exception SQLException if an error is encountered copying the data
*/
protected void copy(ResultSet resultSet) throws SQLException {
int abs = 0;
int rowsCount = 0;
int currentPageRows = 0;
resultSet.last();
rowsCount = resultSet.getRow();
if (pageSize != -1) {
int totalPages = (int) Math.ceil(((double) rowsCount) / pageSize);
if (page > totalPages)
page = totalPages;
if (page < 1)
page = 1;
abs = (page - 1) * pageSize;
//currentPageRows=(page==totalPages?rowsCount-pageSize*(totalPages-1):pageSize);
} else
pageSize = rowsCount;
if (abs == 0)
resultSet.beforeFirst();
else
resultSet.absolute(abs);
//int
while (resultSet.next() && ++currentPageRows <= pageSize) {
DynaBean bean = new BasicDynaBean(this);
for (int i = 0; i < properties.length; i++) {
String name = properties[i].getName();
bean.set(name, resultSet.getObject(name));
}
rows.add(bean);
}
}
/**
*
Introspect the metadata associated with our result set, and populate
* the properties and propertiesMap instance
* variables.
*
* @param resultSet The resultSet whose metadata is to
* be introspected
*
* @exception SQLException if an error is encountered processing the
* result set metadata
*/
protected void introspect(ResultSet resultSet) throws SQLException {
// Accumulate an ordered list of DynaProperties
ArrayList list = new ArrayList();
ResultSetMetaData metadata = resultSet.getMetaData();
int n = metadata.getColumnCount();
for (int i = 1; i <= n; i++) { // JDBC is one-relative!
DynaProperty dynaProperty = createDynaProperty(metadata, i);
if (dynaProperty != null) {
list.add(dynaProperty);
}
}
// Convert this list into the internal data structures we need
properties =
(DynaProperty[]) list.toArray(new DynaProperty[list.size()]);
for (int i = 0; i < properties.length; i++) {
propertiesMap.put(properties[i].getName(), properties[i]);
}
}
/**
*
Factory method to create a new DynaProperty for the given index
* into the result set metadata.
*
* @param metadata is the result set metadata
* @param i is the column index in the metadata
* @return the newly created DynaProperty instance
*/
protected DynaProperty createDynaProperty(
ResultSetMetaData metadata,
int i)
throws SQLException {
String name = null;
if (lowerCase) {
name = metadata.getColumnName(i).toLowerCase();
} else {
name = metadata.getColumnName(i);
}
String className = null;
try {
className = metadata.getColumnClassName(i);
} catch (SQLException e) {
// this is a patch for HsqlDb to ignore exceptions
// thrown by its metadata implementation
}
// Default to Object type if no class name could be retrieved
// from the metadata
Class clazz = Object.class;
if (className != null) {
clazz = loadClass(className);
}
return new DynaProperty(name, clazz);
}
/**
*
Loads and returns the Class of the given name.
* By default, a load from the thread context class loader is attempted.
* If there is no such class loader, the class loader used to load this
* class will be utilized.
*
* @exception SQLException if an exception was thrown trying to load
* the specified class
*/
protected Class loadClass(String className) throws SQLException {
try {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (cl == null) {
cl = this.getClass().getClassLoader();
}
return (cl.loadClass(className));
} catch (Exception e) {
throw new SQLException(
"Cannot load column class '" + className + "': " + e);
}
}
}
大部分代码从BeanUtils的源码中取得,只做了简单的修改,没有加多余的注释。如果要正式使用,
需要再做精加工。
========================================
关于这个包,只准备测试到这里了,不过已经有了大概的印象了,至少,知道这个包可以做些什么。
其实这个笔记也只是起到这个作用。@_@

