001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 019 package org.apache.commons.beanutils; 020 021 022 import java.io.Serializable; 023 import java.sql.ResultSet; 024 import java.sql.SQLException; 025 import java.util.ArrayList; 026 import java.util.List; 027 028 029 /** 030 * <p>Implementation of {@link DynaClass} that creates an in-memory collection 031 * of {@link DynaBean}s representing the results of an SQL query. Once the 032 * {@link DynaClass} instance has been created, the JDBC <code>ResultSet</code> 033 * and <code>Statement</code> on which it is based can be closed, and the 034 * underlying <code>Connection</code> can be returned to its connection pool 035 * (if you are using one).</p> 036 * 037 * <p>The normal usage pattern is something like:</p> 038 * <pre> 039 * Connection conn = ...; // Acquire connection from pool 040 * Statement stmt = conn.createStatement(); 041 * ResultSet rs = stmt.executeQuery("SELECT ..."); 042 * RowSetDynaClass rsdc = new RowSetDynaClass(rs); 043 * rs.close(); 044 * stmt.close(); 045 * ...; // Return connection to pool 046 * List rows = rsdc.getRows(); 047 * ...; // Process the rows as desired 048 * </pre> 049 * 050 * <p>Each column in the result set will be represented as a {@link DynaBean} 051 * property of the corresponding name (optionally forced to lower case 052 * for portability). There will be one {@link DynaBean} in the 053 * <code>List</code> returned by <code>getRows()</code> for each 054 * row in the original <code>ResultSet</code>.</p> 055 * 056 * <p>In general, instances of {@link RowSetDynaClass} can be serialized 057 * and deserialized, which will automatically include the list of 058 * {@link DynaBean}s representing the data content. The only exception 059 * to this rule would be when the underlying property values that were 060 * copied from the <code>ResultSet</code> originally cannot themselves 061 * be serialized. Therefore, a {@link RowSetDynaClass} makes a very 062 * convenient mechanism for transporting data sets to remote Java-based 063 * application components.</p> 064 * 065 * @author Craig R. McClanahan 066 * @version $Revision: 926685 $ $Date: 2010-03-23 17:59:08 +0000 (Tue, 23 Mar 2010) $ 067 */ 068 069 public class RowSetDynaClass extends JDBCDynaClass implements DynaClass, Serializable { 070 071 072 // ----------------------------------------------------- Instance variables 073 074 /** 075 * <p>Limits the size of the returned list. The call to 076 * <code>getRows()</code> will return at most limit number of rows. 077 * If less than or equal to 0, does not limit the size of the result. 078 */ 079 protected int limit = -1; 080 081 /** 082 * <p>The list of {@link DynaBean}s representing the contents of 083 * the original <code>ResultSet</code> on which this 084 * {@link RowSetDynaClass} was based.</p> 085 */ 086 protected List rows = new ArrayList(); 087 088 // ----------------------------------------------------------- Constructors 089 090 091 /** 092 * <p>Construct a new {@link RowSetDynaClass} for the specified 093 * <code>ResultSet</code>. The property names corresponding 094 * to column names in the result set will be lower cased.</p> 095 * 096 * @param resultSet The result set to be wrapped 097 * 098 * @exception NullPointerException if <code>resultSet</code> 099 * is <code>null</code> 100 * @exception SQLException if the metadata for this result set 101 * cannot be introspected 102 */ 103 public RowSetDynaClass(ResultSet resultSet) throws SQLException { 104 105 this(resultSet, true, -1); 106 107 } 108 109 /** 110 * <p>Construct a new {@link RowSetDynaClass} for the specified 111 * <code>ResultSet</code>. The property names corresponding 112 * to column names in the result set will be lower cased.</p> 113 * 114 * If <code>limit</code> is not less than 0, max <code>limit</code> 115 * number of rows will be copied into the list. 116 * 117 * @param resultSet The result set to be wrapped 118 * @param limit The maximum for the size of the result. 119 * 120 * @exception NullPointerException if <code>resultSet</code> 121 * is <code>null</code> 122 * @exception SQLException if the metadata for this result set 123 * cannot be introspected 124 */ 125 public RowSetDynaClass(ResultSet resultSet, int limit) throws SQLException { 126 127 this(resultSet, true, limit); 128 129 } 130 131 132 /** 133 * <p>Construct a new {@link RowSetDynaClass} for the specified 134 * <code>ResultSet</code>. The property names corresponding 135 * to the column names in the result set will be lower cased or not, 136 * depending on the specified <code>lowerCase</code> value.</p> 137 * 138 * If <code>limit</code> is not less than 0, max <code>limit</code> 139 * number of rows will be copied into the resultset. 140 * 141 * 142 * @param resultSet The result set to be wrapped 143 * @param lowerCase Should property names be lower cased? 144 * 145 * @exception NullPointerException if <code>resultSet</code> 146 * is <code>null</code> 147 * @exception SQLException if the metadata for this result set 148 * cannot be introspected 149 */ 150 public RowSetDynaClass(ResultSet resultSet, boolean lowerCase) 151 throws SQLException { 152 this(resultSet, lowerCase, -1); 153 154 } 155 156 /** 157 * <p>Construct a new {@link RowSetDynaClass} for the specified 158 * <code>ResultSet</code>. The property names corresponding 159 * to the column names in the result set will be lower cased or not, 160 * depending on the specified <code>lowerCase</code> value.</p> 161 * 162 * <p><strong>WARNING</strong> - If you specify <code>false</code> 163 * for <code>lowerCase</code>, the returned property names will 164 * exactly match the column names returned by your JDBC driver. 165 * Because different drivers might return column names in different 166 * cases, the property names seen by your application will vary 167 * depending on which JDBC driver you are using.</p> 168 * 169 * @param resultSet The result set to be wrapped 170 * @param lowerCase Should property names be lower cased? 171 * @param limit Maximum limit for the <code>List</code> of {@link DynaBean} 172 * 173 * @exception NullPointerException if <code>resultSet</code> 174 * is <code>null</code> 175 * @exception SQLException if the metadata for this result set 176 * cannot be introspected 177 */ 178 public RowSetDynaClass(ResultSet resultSet, boolean lowerCase, int limit) 179 throws SQLException { 180 181 this(resultSet, lowerCase, limit, false); 182 183 } 184 185 /** 186 * <p>Construct a new {@link RowSetDynaClass} for the specified 187 * <code>ResultSet</code>. The property names corresponding 188 * to the column names in the result set will be lower cased or not, 189 * depending on the specified <code>lowerCase</code> value.</p> 190 * 191 * <p><strong>WARNING</strong> - If you specify <code>false</code> 192 * for <code>lowerCase</code>, the returned property names will 193 * exactly match the column names returned by your JDBC driver. 194 * Because different drivers might return column names in different 195 * cases, the property names seen by your application will vary 196 * depending on which JDBC driver you are using.</p> 197 * 198 * @param resultSet The result set to be wrapped 199 * @param lowerCase Should property names be lower cased? 200 * @param useColumnLabel true if the column label should be used, otherwise false 201 * 202 * @exception NullPointerException if <code>resultSet</code> 203 * is <code>null</code> 204 * @exception SQLException if the metadata for this result set 205 * cannot be introspected 206 * @since 1.8.3 207 */ 208 public RowSetDynaClass(ResultSet resultSet, boolean lowerCase, boolean useColumnLabel) 209 throws SQLException { 210 this(resultSet, lowerCase, -1, useColumnLabel); 211 212 } 213 214 /** 215 * <p>Construct a new {@link RowSetDynaClass} for the specified 216 * <code>ResultSet</code>. The property names corresponding 217 * to the column names in the result set will be lower cased or not, 218 * depending on the specified <code>lowerCase</code> value.</p> 219 * 220 * <p><strong>WARNING</strong> - If you specify <code>false</code> 221 * for <code>lowerCase</code>, the returned property names will 222 * exactly match the column names returned by your JDBC driver. 223 * Because different drivers might return column names in different 224 * cases, the property names seen by your application will vary 225 * depending on which JDBC driver you are using.</p> 226 * 227 * @param resultSet The result set to be wrapped 228 * @param lowerCase Should property names be lower cased? 229 * @param limit Maximum limit for the <code>List</code> of {@link DynaBean} 230 * @param useColumnLabel true if the column label should be used, otherwise false 231 * 232 * @exception NullPointerException if <code>resultSet</code> 233 * is <code>null</code> 234 * @exception SQLException if the metadata for this result set 235 * cannot be introspected 236 * @since 1.8.3 237 */ 238 public RowSetDynaClass(ResultSet resultSet, boolean lowerCase, int limit, boolean useColumnLabel) 239 throws SQLException { 240 241 if (resultSet == null) { 242 throw new NullPointerException(); 243 } 244 this.lowerCase = lowerCase; 245 this.limit = limit; 246 setUseColumnLabel(useColumnLabel); 247 introspect(resultSet); 248 copy(resultSet); 249 250 } 251 252 /** 253 * <p>Return a <code>List</code> containing the {@link DynaBean}s that 254 * represent the contents of each <code>Row</code> from the 255 * <code>ResultSet</code> that was the basis of this 256 * {@link RowSetDynaClass} instance. These {@link DynaBean}s are 257 * disconnected from the database itself, so there is no problem with 258 * modifying the contents of the list, or the values of the properties 259 * of these {@link DynaBean}s. However, it is the application's 260 * responsibility to persist any such changes back to the database, 261 * if it so desires.</p> 262 * 263 * @return A <code>List</code> of {@link DynaBean} instances 264 */ 265 public List getRows() { 266 267 return (this.rows); 268 269 } 270 271 272 // ------------------------------------------------------ Protected Methods 273 274 275 /** 276 * <p>Copy the column values for each row in the specified 277 * <code>ResultSet</code> into a newly created {@link DynaBean}, and add 278 * this bean to the list of {@link DynaBean}s that will later by 279 * returned by a call to <code>getRows()</code>.</p> 280 * 281 * @param resultSet The <code>ResultSet</code> whose data is to be 282 * copied 283 * 284 * @exception SQLException if an error is encountered copying the data 285 */ 286 protected void copy(ResultSet resultSet) throws SQLException { 287 288 int cnt = 0; 289 while (resultSet.next() && (limit < 0 || cnt++ < limit) ) { 290 DynaBean bean = createDynaBean(); 291 for (int i = 0; i < properties.length; i++) { 292 String name = properties[i].getName(); 293 Object value = getObject(resultSet, name); 294 bean.set(name, value); 295 } 296 rows.add(bean); 297 } 298 299 } 300 301 302 /** 303 * <p>Create and return a new {@link DynaBean} instance to be used for 304 * representing a row in the underlying result set.</p> 305 * 306 * @return A new <code>DynaBean</code> instance 307 */ 308 protected DynaBean createDynaBean() { 309 310 return (new BasicDynaBean(this)); 311 312 } 313 314 315 }