Jakarta-Common-BeanUtils研究心得(2)

作者: sonymusic
标题: Jakarta-Common-BeanUtils研究心得(2)
关键字: Jakarta Common BeanUtils
分类: 研究心得
密级: 公开
允许随意转载,但请注明出处及作者。

=========================================

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 <rows.size(); 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 <rows.size(); 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

 /**
  * <p>Construct a new {@link RowSetDynaClass} for the specified
  * <code>ResultSet</code>.  The property names corresponding
  * to column names in the result set will be lower cased.</p>
  *
  * @param resultSet The result set to be wrapped
  *
  * @exception NullPointerException if <code>resultSet</code>
  *  is <code>null</code>
  * @exception SQLException if the metadata for this result set
  *  cannot be introspected
  */
 public CustomRowSetDynaClass(ResultSet resultSet) throws SQLException {

   this(resultSet, true);

 }

 /**
  * <p>Construct a new {@link RowSetDynaClass} for the specified
  * <code>ResultSet</code>.  The property names corresponding
  * to the column names in the result set will be lower cased or not,
  * depending on the specified <code>lowerCase</code> value.</p>
  *
  * <p><strong>WARNING</strong> - If you specify <code>false</code>
  * for <code>lowerCase</code>, 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.</p>
  *
  * @param resultSet The result set to be wrapped
  * @param lowerCase Should property names be lower cased?
  *
  * @exception NullPointerException if <code>resultSet</code>
  *  is <code>null</code>
  * @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

 /**
  * <p>Flag defining whether column names should be lower cased when
  * converted to property names.</p>
  */
 protected boolean lowerCase = true;

 protected int page = 1;
 protected int pageSize = -1;

 /**
  * <p>The set of dynamic properties that are part of this
  * {@link DynaClass}.</p>
  */
 protected DynaProperty properties[] = null;

 /**
  * <p>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
  * <code>properties</code> list.</p>
  */
 protected Map propertiesMap = new HashMap();

 /**
  * <p>The list of {@link DynaBean}s representing the contents of
  * the original <code>ResultSet</code> on which this
  * {@link RowSetDynaClass} was based.</p>
  */
 protected List rows = new ArrayList();

 // ------------------------------------------------------ DynaClass Methods

 /**
  * <p>Return the name of this DynaClass (analogous to the
  * <code>getName()</code> method of <code>java.lang.Class</code), which
  * allows the same <code>DynaClass</code> implementation class to support
  * different dynamic classes, with different sets of properties.</p>
  */
 public String getName() {

   return (this.getClass().getName());

 }

 /**
  * <p>Return a property descriptor for the specified property, if it
  * exists; otherwise, return <code>null</code>.</p>
  *
  * @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));

 }

 /**
  * <p>Return an array of <code>ProperyDescriptors</code> for the properties
  * currently defined in this DynaClass.  If no properties are defined, a
  * zero-length array will be returned.</p>
  */
 public DynaProperty[] getDynaProperties() {

   return (properties);

 }

 /**
  * <p>Instantiate and return a new DynaBean instance, associated
  * with this DynaClass.  <strong>NOTE</strong> - This operation is not
  * supported, and throws an exception.</p>
  *
  * @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

 /**
  * <p>Return a <code>List</code> containing the {@link DynaBean}s that
  * represent the contents of each <code>Row</code> from the
  * <code>ResultSet</code> 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.</p>
  */
 public List getRows() {

   return (this.rows);

 }

 // ------------------------------------------------------ Protected Methods

 /**
  * <p>Copy the column values for each row in the specified
  * <code>ResultSet</code> 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 <code>getRows()</code>.</p>
  *
  * @param resultSet The <code>ResultSet</code> 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);
   }

 }

 /**
  * <p>Introspect the metadata associated with our result set, and populate
  * the <code>properties</code> and <code>propertiesMap</code> instance
  * variables.</p>
  *
  * @param resultSet The <code>resultSet</code> 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]);
   }

 }

 /**
  * <p>Factory method to create a new DynaProperty for the given index
  * into the result set metadata.</p>
  *
  * @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);

 }

 /**
  * <p>Loads and returns the <code>Class</code> 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.</p>
  *
  * @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的源码中取得,只做了简单的修改,没有加多余的注释。如果要正式使用,
需要再做精加工。

========================================
关于这个包,只准备测试到这里了,不过已经有了大概的印象了,至少,知道这个包可以做些什么。
其实这个笔记也只是起到这个作用。@_@

posted on 2004-12-14 22:47  笨笨  阅读(1262)  评论(0编辑  收藏  举报

导航