?
Apache Jakarta Commons項目非常有用。我曾在許多不同的項目上或直接或間接地使用各種流行的commons組件。其中的一個強大的組件就是BeanUtils。我將說明如何使用BeanUtils將local實體bean轉換為對應的value 對象:
BeanUtils.copyProperties(aValue, aLocal)
上面的代碼從aLocal對象復制屬性到aValue對象。它相當簡單!它不管local(或對應的value)對象有多少個屬性,只管進行復制。我們假設local對象有100個屬性。上面的代碼使我們可以無需鍵入至少100行的冗長、容易出錯和反復的get和set方法調用。這太棒了!太強大了!太有用了!
現在,還有一個壞消息:使用BeanUtils的成本驚人地昂貴!我做了一個簡單的測試,BeanUtils所花費的時間要超過取數據、將其復制到對應的value對象(通過手動調用get和set方法),以及通過串行化將其返回到遠程的客戶機的時間總和。所以要小心使用這種威力!
- 如果您有BeanUtils和類似的實用程序方面的體驗,請與我交流分享。
+prakash
Beanutils用了魔術般的反射技術,實現了很多夸張有用的功能,都是C/C++時代不敢想的。無論誰的項目,始終一天都會用得上它。我算是后知后覺了,第一回看到它的時候居然錯過。
1.屬性的動態getter,setter
在這框架滿天飛的年代,不能事事都保證執行getter,setter函數了,有時候屬性是要需要根據名字動態取得的,就像這樣:
BeanUtils.getProperty(myBean,"code");
而BeanUtils更強的功能是直接訪問內嵌對象的屬性,只要使用點號分隔。
BeanUtils.getProperty(orderBean, "address.city");
相比之下其他類庫的BeanUtils通常都很簡單,不能訪問內嵌的對象,所以經常要用Commons BeanUtils替換它們。
BeanUtils還支持List和Map類型的屬性。如下面的語法即可取得顧客列表中第一個顧客的名字
BeanUtils.getProperty(orderBean, "customers[1].name");
其中BeanUtils會使用ConvertUtils類把字符串轉為Bean屬性的真正類型,方便從HttpServletRequest等對象中提取bean,或者把bean輸出到頁面。
而PropertyUtils就會原色的保留Bean原來的類型。
2.beanCompartor 動態排序
還是通過反射,動態設定Bean按照哪個屬性來排序,而不再需要在bean的Compare接口進行復雜的條件判斷。
List peoples = ...; // Person對象的列表
Collections.sort(peoples, new BeanComparator("age"));
如果要支持多個屬性的復合排序,如"Order By lastName,firstName"
ArrayList sortFields = new ArrayList();
sortFields.add(new BeanComparator("lastName"));
sortFields.add(new BeanComparator("firstName"));
ComparatorChain multiSort = new ComparatorChain(sortFields);
Collections.sort(rows,multiSort);
其中ComparatorChain屬于jakata commons-collections包。
如果age屬性不是普通類型,構造函數需要再傳入一個comparator對象為age變量排序。
另外, BeanCompartor本身的ComparebleComparator, 遇到屬性為null就會拋出異常, 也不能設定升序還是降序。
這個時候又要借助commons-collections包的ComparatorUtils.
?? Comparator mycmp = ComparableComparator.getInstance();
???mycmp = ComparatorUtils.nullLowComparator(mycmp);? //允許null
?? mycmp = ComparatorUtils.reversedComparator(mycmp); //逆序
?? Comparator cmp = new BeanComparator(sortColumn, mycmp);
3.Converter 把Request或ResultSet中的字符串綁定到對象的屬性
?? 經常要從request,resultSet等對象取出值來賦入bean中,下面的代碼誰都寫膩了,如果不用MVC框架的綁定功能的話。
String a = request.getParameter("a");
bean.setA(a);
String b = ....
不妨寫一個Binder:
MyBean bean = ...;
HashMap map = new HashMap();
Enumeration names = request.getParameterNames();
while (names.hasMoreElements())
{
String name = (String) names.nextElement();
map.put(name, request.getParameterValues(name));
}
BeanUtils.populate(bean, map);
??? 其中BeanUtils的populate方法或者getProperty,setProperty方法其實都會調用convert進行轉換。
????但Converter只支持一些基本的類型,甚至連java.util.Date類型也不支持。而且它比較笨的一個地方是當遇到不認識的類型時,居然會拋出異常來。
??? 對于Date類型,我參考它的sqldate類型實現了一個Converter,而且添加了一個設置日期格式的函數。
要把這個Converter注冊,需要如下語句:
ConvertUtilsBean convertUtils = new ConvertUtilsBean();
?? DateConverter dateConverter = new DateConverter();
?? convertUtils.register(dateConverter,Date.class);
//因為要注冊converter,所以不能再使用BeanUtils的靜態方法了,必須創建BeanUtilsBean實例
BeanUtilsBean beanUtils = new BeanUtilsBean(convertUtils,new PropertyUtilsBean());
beanUtils.setProperty(bean, name, value);
4 其他功能
4.1 PropertyUtils,當屬性為Collection,Map時的動態讀取:
?
Collection: 提供index
?? BeanUtils.getIndexedProperty(orderBean,"items",1);
或者
? BeanUtils.getIndexedProperty(orderBean,"items[1]");
Map: 提供Key Value
? BeanUtils.getMappedProperty(orderBean, "items","111");//key-value goods_no=111
或者
? BeanUtils.getMappedProperty(orderBean, "items(111)")
?
4.2 PropertyUtils,獲取屬性的Class類型
???? public static Class getPropertyType(Object bean, String name)
?
4.3 ConstructorUtils,動態創建對象
????? public static Object invokeConstructor(Class klass, Object arg)
4.4 MethodUtils,動態調用方法
MethodUtils.invokeMethod(bean, methodName, parameter);
4.5 動態Bean 見
用DynaBean減除不必要的VO和FormBean?一、概述
第一次看到BeanUtils包,是在Struts項目中,作為Struts一個工具來使用的,
估計功能越弄越強,就移到Common項目中了吧。
BeanUtils一共有四個package:
org.apache.commons.beanutils
org.apache.commons.beanutils.converters
org.apache.commons.beanutils.locale
org.apache.commons.beanutils.locale.converters
后三個包主要是用于數據的轉換,圍繞著一個Converter接口,該接口只有一個方法:
java.lang.Object convert(java.lang.Class type, java.lang.Object value) ,
用于將一個value轉換成另一個類型為type的Object。在一些自動化的應用中應該會有用。
這里不作評論,以后有興趣了,或者覺得有用了,再行研究。
這里只講第一個包。
二、測試用的Bean
在開始所有的測試之前,我寫了一個簡單的Bean,以便于測試,代碼如下:
package test.jakarta.commons.beanutils;
/**
?* @author SonyMusic
?*
?*/
public class Month {
?private int value;
?private String name;
?private int[] days={11,22,33,44,55};
?public Month(int v, String n){
? ?value=v;
? ?name=n;
?}
?
?/**
? * Returns the name.
? * @return String
? */
?public String getName() {
? ?return name;
?}
?/**
? * Returns the value.
? * @return int
? */
?public int getValue() {
? ?return value;
?}
?/**
? * Sets the name.
? * @param name The name to set
? */
?public void setName(String name) {
? ?this.name = name;
?}
?/**
? * Sets the value.
? * @param value The value to set
? */
?public void setValue(int value) {
? ?this.value = value;
?}
?/**
? * @see java.lang.Object#toString()
? */
?public String toString() {
? ?return value+"("+name+")";
?}
?public int[] getDays() {
? ?return days;
?}
?public void setDays(int[] is) {
? ?days = is;
?}
}
三、BeanUtils
這是一個主要應用于Bean的Util(呵呵,這個解釋很絕吧),以下是其中幾個方法的例子
//static java.util.Map describe(java.lang.Object bean)
//這個方法返回一個Object中所有的可讀屬性,并將屬性名/屬性值放入一個Map中,另外還有
//一個名為class的屬性,屬性值是Object的類名,事實上class是java.lang.Object的一個屬性
?Month month=new Month(1, "Jan");
?
?try {
? ?Map map=BeanUtils.describe(month);
? ?Set keySet=map.keySet();
? ?for (Iterator iter = keySet.iterator(); iter.hasNext();) {
? ? ?Object element = (Object) iter.next();
? ? ?System.out.println("KeyClass:"+element.getClass().getName());
? ? ?System.out.println("ValueClass:"+map.get(element).getClass().getName());
? ? ?System.out.print(element+"\t");
? ? ?System.out.print(map.get(element));
? ? ?System.out.println();
? ?}
?} catch (IllegalAccessException e) {
? ?e.printStackTrace();
?} catch (InvocationTargetException e) {
? ?e.printStackTrace();
?} catch (NoSuchMethodException e) {
? ?e.printStackTrace();
?}
輸出為:
KeyClass:java.lang.String
ValueClass:java.lang.String
value ?1
KeyClass:java.lang.String
ValueClass:java.lang.String
class ?class test.jakarta.commons.beanutils.Month
KeyClass:java.lang.String
ValueClass:java.lang.String
name ?Jan
注意到所有Map中的key/value都是String,而不管object中實際的值是多少。
與此對應的還有static void populate(java.lang.Object bean, java.util.Map properties)
用于將剛才describe出的Map再裝配成一個對象。
再看這樣一段代碼
曹曉鋼也許還記得,為了取一個不確定對象的property,著實花了不少時間,
難度不大,但要做到100%的正確,仍然需要付出很大的精力。
//static java.lang.String getProperty(java.lang.Object bean, java.lang.String name)
?Month month=new Month(1, "Jan");
?
?try {
? ?System.out.println(BeanUtils.getProperty(month,"value"));
?} catch (Exception e) {
? ?e.printStackTrace();
?}
//輸出是:1
與getProperty類似的還有getIndexedProperty, getMappedProperty,
以getIndexedProperty為例:
?Month month=new Month(1, "Jan");
?
?try {
? ?System.out.println(BeanUtils.getIndexedProperty(month,"days",1));
? ?System.out.println(BeanUtils.getIndexedProperty(month,"days[1]"));
?} catch (Exception e) {
? ?e.printStackTrace();
?}
這兩個調用是相同的。
BeanUtils中還有一個方法:
static void copyProperties(java.lang.Object dest, java.lang.Object orig)
它真是太有用,還記得struts中滿天飛的都是copyProperties,我甚至懷疑整個BeanUtils最初
是不是因為這個方法的需求才寫出來的。
它將對象orig中的屬性復制到dest中去。
四、PropertyUtils
這個類和BeanUtils類很多的方法在參數上都是相同的,但返回值不同。
BeanUtils著重于"Bean",返回值通常是String,而PropertyUtils著重于屬性,
它的返回值通常是Object。
五、ConstructorUtils
這個類中的方法主要分成兩種,一種是得到構造方法,一種是創建對象。
事實上多數時候得到構造方法的目的就是創建對象,這里只介紹一下創建對象。
//static java.lang.Object ConstructorUtils.invokeConstructor
//(java.lang.Class klass, java.lang.Object[] args)
//根據一個java.lang.Class以及相應的構造方法的參數,創建一個對象。
?Object obj=ConstructorUtils.invokeConstructor(Month.class, {new Integer(1), "Jan"});
?Month month=(Month)obj;
?try {
? ?System.out.println(BeanUtils.getProperty(month,"value"));
?} catch (Exception e) {
? ?e.printStackTrace();
?}
輸出證明,構造方法的調用是成功的。
如果需要強制指定構造方法的參數類型,可以這樣調用:
? ?Object[] args={new Integer(1), "Jan"};
? ?Class[] argsType={int.class, String.class};
? ?Object obj;
? ?obj = ConstructorUtils.invokeExactConstructor(Month.class, args, argsType);
? ?Month month=(Month)obj;
? ?System.out.println(BeanUtils.getProperty(month,"value"));
argsType指定了參數的類型。
?
六、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的源碼中取得,只做了簡單的修改,沒有加多余的注釋。如果要正式使用,
需要再做精加工。