? BeanUtils:?威力和代價(jià)(轉(zhuǎn)載綜合) | |
Apache Jakarta Commons項(xiàng)目非常有用。我曾在許多不同的項(xiàng)目上或直接或間接地使用各種流行的commons組件。其中的一個(gè)強(qiáng)大的組件就是BeanUtils。我將說明如何使用BeanUtils將local實(shí)體bean轉(zhuǎn)換為對(duì)應(yīng)的value 對(duì)象:
上面的代碼從aLocal對(duì)象復(fù)制屬性到aValue對(duì)象。它相當(dāng)簡(jiǎn)單!它不管local(或?qū)?yīng)的value)對(duì)象有多少個(gè)屬性,只管進(jìn)行復(fù)制。我們假設(shè)local對(duì)象有100個(gè)屬性。上面的代碼使我們可以無需鍵入至少100行的冗長、容易出錯(cuò)和反復(fù)的get和set方法調(diào)用。這太棒了!太強(qiáng)大了!太有用了! 現(xiàn)在,還有一個(gè)壞消息:使用BeanUtils的成本驚人地昂貴!我做了一個(gè)簡(jiǎn)單的測(cè)試,BeanUtils所花費(fèi)的時(shí)間要超過取數(shù)據(jù)、將其復(fù)制到對(duì)應(yīng)的value對(duì)象(通過手動(dòng)調(diào)用get和set方法),以及通過串行化將其返回到遠(yuǎn)程的客戶機(jī)的時(shí)間總和。所以要小心使用這種威力!
Beanutils用了魔術(shù)般的反射技術(shù),實(shí)現(xiàn)了很多夸張有用的功能,都是C/C++時(shí)代不敢想的。無論誰的項(xiàng)目,始終一天都會(huì)用得上它。我算是后知后覺了,第一回看到它的時(shí)候居然錯(cuò)過。 1.屬性的動(dòng)態(tài)getter,setter 在這框架滿天飛的年代,不能事事都保證執(zhí)行g(shù)etter,setter函數(shù)了,有時(shí)候?qū)傩允且枰鶕?jù)名字動(dòng)態(tài)取得的,就像這樣: BeanUtils.getProperty(myBean,"code"); 而BeanUtils更強(qiáng)的功能是直接訪問內(nèi)嵌對(duì)象的屬性,只要使用點(diǎn)號(hào)分隔。 BeanUtils.getProperty(orderBean, "address.city"); 相比之下其他類庫的BeanUtils通常都很簡(jiǎn)單,不能訪問內(nèi)嵌的對(duì)象,所以經(jīng)常要用Commons BeanUtils替換它們。 BeanUtils還支持List和Map類型的屬性。如下面的語法即可取得顧客列表中第一個(gè)顧客的名字 BeanUtils.getProperty(orderBean, "customers[1].name"); 其中BeanUtils會(huì)使用ConvertUtils類把字符串轉(zhuǎn)為Bean屬性的真正類型,方便從HttpServletRequest等對(duì)象中提取bean,或者把bean輸出到頁面。 而PropertyUtils就會(huì)原色的保留Bean原來的類型。 2.beanCompartor 動(dòng)態(tài)排序 還是通過反射,動(dòng)態(tài)設(shè)定Bean按照哪個(gè)屬性來排序,而不再需要在bean的Compare接口進(jìn)行復(fù)雜的條件判斷。 List peoples = ...; // Person對(duì)象的列表 Collections.sort(peoples, new BeanComparator("age")); 如果要支持多個(gè)屬性的復(fù)合排序,如"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包。 ?? Comparator mycmp = ComparableComparator.getInstance(); 3.Converter 把Request或ResultSet中的字符串綁定到對(duì)象的屬性 ?? 經(jīng)常要從request,resultSet等對(duì)象取出值來賦入bean中,下面的代碼誰都寫膩了,如果不用MVC框架的綁定功能的話。 String a = request.getParameter("a"); bean.setA(a); String b = .... 不妨寫一個(gè)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方法其實(shí)都會(huì)調(diào)用convert進(jìn)行轉(zhuǎn)換。 ConvertUtilsBean convertUtils = new ConvertUtilsBean(); 4 其他功能 4.1 PropertyUtils,當(dāng)屬性為Collection,Map時(shí)的動(dòng)態(tài)讀取: ? 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,動(dòng)態(tài)創(chuàng)建對(duì)象 ????? public static Object invokeConstructor(Class klass, Object arg) 4.4 MethodUtils,動(dòng)態(tài)調(diào)用方法 MethodUtils.invokeMethod(bean, methodName, parameter);4.5 動(dòng)態(tài)Bean 見用DynaBean減除不必要的VO和FormBean? 一、概述 第一次看到BeanUtils包,是在Struts項(xiàng)目中,作為Struts一個(gè)工具來使用的, 估計(jì)功能越弄越強(qiáng),就移到Common項(xiàng)目中了吧。 BeanUtils一共有四個(gè)package: org.apache.commons.beanutils org.apache.commons.beanutils.converters org.apache.commons.beanutils.locale org.apache.commons.beanutils.locale.converters 后三個(gè)包主要是用于數(shù)據(jù)的轉(zhuǎn)換,圍繞著一個(gè)Converter接口,該接口只有一個(gè)方法: java.lang.Object convert(java.lang.Class type, java.lang.Object value) , 用于將一個(gè)value轉(zhuǎn)換成另一個(gè)類型為type的Object。在一些自動(dòng)化的應(yīng)用中應(yīng)該會(huì)有用。 這里不作評(píng)論,以后有興趣了,或者覺得有用了,再行研究。 這里只講第一個(gè)包。 二、測(cè)試用的Bean 在開始所有的測(cè)試之前,我寫了一個(gè)簡(jiǎn)單的Bean,以便于測(cè)試,代碼如下: 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 這是一個(gè)主要應(yīng)用于Bean的Util(呵呵,這個(gè)解釋很絕吧),以下是其中幾個(gè)方法的例子 //static java.util.Map describe(java.lang.Object bean) //這個(gè)方法返回一個(gè)Object中所有的可讀屬性,并將屬性名/屬性值放入一個(gè)Map中,另外還有 //一個(gè)名為class的屬性,屬性值是Object的類名,事實(shí)上class是java.lang.Object的一個(gè)屬性 ?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中實(shí)際的值是多少。 與此對(duì)應(yīng)的還有static void populate(java.lang.Object bean, java.util.Map properties) 用于將剛才describe出的Map再裝配成一個(gè)對(duì)象。 再看這樣一段代碼 曹曉鋼也許還記得,為了取一個(gè)不確定對(duì)象的property,著實(shí)花了不少時(shí)間, 難度不大,但要做到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類似的還有g(shù)etIndexedProperty, 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(); ?} 這兩個(gè)調(diào)用是相同的。 BeanUtils中還有一個(gè)方法: static void copyProperties(java.lang.Object dest, java.lang.Object orig) 它真是太有用,還記得struts中滿天飛的都是copyProperties,我甚至懷疑整個(gè)BeanUtils最初 是不是因?yàn)檫@個(gè)方法的需求才寫出來的。 它將對(duì)象orig中的屬性復(fù)制到dest中去。 四、PropertyUtils 這個(gè)類和BeanUtils類很多的方法在參數(shù)上都是相同的,但返回值不同。 BeanUtils著重于"Bean",返回值通常是String,而PropertyUtils著重于屬性, 它的返回值通常是Object。 五、ConstructorUtils 這個(gè)類中的方法主要分成兩種,一種是得到構(gòu)造方法,一種是創(chuàng)建對(duì)象。 事實(shí)上多數(shù)時(shí)候得到構(gòu)造方法的目的就是創(chuàng)建對(duì)象,這里只介紹一下創(chuàng)建對(duì)象。 //static java.lang.Object ConstructorUtils.invokeConstructor //(java.lang.Class klass, java.lang.Object[] args) //根據(jù)一個(gè)java.lang.Class以及相應(yīng)的構(gòu)造方法的參數(shù),創(chuàng)建一個(gè)對(duì)象。 ?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(); ?} 輸出證明,構(gòu)造方法的調(diào)用是成功的。 如果需要強(qiáng)制指定構(gòu)造方法的參數(shù)類型,可以這樣調(diào)用: ? ?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指定了參數(shù)的類型。 ? 六、ConstructorUtils補(bǔ)遺
創(chuàng)建對(duì)象還有一個(gè)方法:invokeExactConstructor,該方法對(duì)參數(shù)要求 更加嚴(yán)格,傳遞進(jìn)去的參數(shù)必須嚴(yán)格符合構(gòu)造方法的參數(shù)列表。 例如: Object[] args={new Integer(1), "Jan"}; Class[] argsType={int.class, String.class}; Object obj; //下面這句調(diào)用將不會(huì)成功,因?yàn)閍rgs[0]的類型為Integer,而不是int //obj = ConstructorUtils.invokeExactConstructor(Month.class, args); //這一句就可以,因?yàn)閍rgsType指定了類型。 obj = ConstructorUtils.invokeExactConstructor(Month.class, args, argsType); Month month=(Month)obj; System.out.println(BeanUtils.getProperty(month,"value")); 七、MethodUtils 與ConstructorUtils類似,不過調(diào)用的時(shí)候,通常需要再指定一個(gè)method name的參數(shù)。 八、DynaClass/DynaBean 這似乎是BeanUtils中最有趣的部分之一了,很簡(jiǎn)單,簡(jiǎn)單到光看這兩個(gè)接口中的方法會(huì)不明白 為什么要設(shè)計(jì)這兩個(gè)接口。不過看到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(); 原來這是一個(gè)ResultSet的包裝器,ResultSetDynaClass實(shí)現(xiàn)了DynaClass,它的iterator方法返回一個(gè) ResultSetIterator,則是實(shí)現(xiàn)了DynaBean接口。 在獲得一個(gè)DynaBean之后,我們就可以用 ?????DynaBean row = (DynaBean) rows.next(); ?????System.out.println(row.get("field1")); //field1是其中一個(gè)字段的名字 再看另一個(gè)類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(); //先打印一下,用于檢驗(yàn)后面的結(jié)果。 while(rs.next()){ System.out.println(rs.getString("name")); } rs.beforeFirst();//這里必須用beforeFirst,因?yàn)镽owSetDynaClass只從當(dāng)前位置向前滾動(dòng) RowSetDynaClass rsdc = new RowSetDynaClass(rs); rs.close(); ps.close(); List rows = rsdc.getRows();//返回一個(gè)標(biāo)準(zhǔn)的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的數(shù)據(jù),代價(jià)是占用內(nèi)存。如果一個(gè)表有10萬條記錄,rsdc.getRows() 就會(huì)返回10萬個(gè)記錄。@_@ 需要注意的是ResultSetDynaClass和RowSetDynaClass的不同之處: 1,ResultSetDynaClass是基于Iterator的,一次只返回一條記錄,而RowSetDynaClass是基于 List的,一次性返回全部記錄。直接影響是在數(shù)據(jù)比較多時(shí)ResultSetDynaClass會(huì)比較的快速, 而RowSetDynaClass需要將ResultSet中的全部數(shù)據(jù)都讀出來(并存儲(chǔ)在其內(nèi)部),會(huì)占用過多的 內(nèi)存,并且速度也會(huì)比較慢。 2,ResultSetDynaClass一次只處理一條記錄,在處理完成之前,ResultSet不可以關(guān)閉。 3,ResultSetIterator的next()方法返回的DynaBean其實(shí)是指向其內(nèi)部的一個(gè)固定 對(duì)象,在每次next()之后,內(nèi)部的值都會(huì)被改變。這樣做的目的是節(jié)約內(nèi)存,如果你需要保存每 次生成的DynaBean,就需要?jiǎng)?chuàng)建另一個(gè)DynaBean,并將數(shù)據(jù)復(fù)制過去,下面也是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); ???} 事實(shí)上DynaClass/DynaBean可以用于很多地方,存儲(chǔ)各種類型的數(shù)據(jù)。自己想吧。嘿嘿。 九、自定義的CustomRowSetDynaClass 兩年前寫過一個(gè)與RowSetDynaClass目標(biāo)相同的類,不過多一個(gè)功能,就是分頁,只取需要的數(shù)據(jù), 這樣內(nèi)存占用就會(huì)減少。 先看一段代碼: 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(); */ //第二個(gè)參數(shù)表示第幾頁,第三個(gè)參數(shù)表示頁的大小 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) { } } 在這里用到了一個(gè)CustomRowSetDynaClass類,構(gòu)造方法中增加了page和pageSize兩個(gè)參數(shù), 這樣,不管數(shù)據(jù)庫里有多少條記錄,最多只取pageSize條記錄,若pageSize==-1,則功能和 RowSetDynaClass一樣。這在大多數(shù)情況下是適用的。該類的代碼如下: 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的源碼中取得,只做了簡(jiǎn)單的修改,沒有加多余的注釋。如果要正式使用, 需要再做精加工。 |