1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 19 package org.apache.commons.beanutils; 20 21 22 import java.io.Serializable; 23 import java.sql.ResultSet; 24 import java.sql.SQLException; 25 import java.util.ArrayList; 26 import java.util.List; 27 28 29 /** 30 * <p>Implementation of {@link DynaClass} that creates an in-memory collection 31 * of {@link DynaBean}s representing the results of an SQL query. Once the 32 * {@link DynaClass} instance has been created, the JDBC <code>ResultSet</code> 33 * and <code>Statement</code> on which it is based can be closed, and the 34 * underlying <code>Connection</code> can be returned to its connection pool 35 * (if you are using one).</p> 36 * 37 * <p>The normal usage pattern is something like:</p> 38 * <pre> 39 * Connection conn = ...; // Acquire connection from pool 40 * Statement stmt = conn.createStatement(); 41 * ResultSet rs = stmt.executeQuery("SELECT ..."); 42 * RowSetDynaClass rsdc = new RowSetDynaClass(rs); 43 * rs.close(); 44 * stmt.close(); 45 * ...; // Return connection to pool 46 * List rows = rsdc.getRows(); 47 * ...; // Process the rows as desired 48 * </pre> 49 * 50 * <p>Each column in the result set will be represented as a {@link DynaBean} 51 * property of the corresponding name (optionally forced to lower case 52 * for portability). There will be one {@link DynaBean} in the 53 * <code>List</code> returned by <code>getRows()</code> for each 54 * row in the original <code>ResultSet</code>.</p> 55 * 56 * <p>In general, instances of {@link RowSetDynaClass} can be serialized 57 * and deserialized, which will automatically include the list of 58 * {@link DynaBean}s representing the data content. The only exception 59 * to this rule would be when the underlying property values that were 60 * copied from the <code>ResultSet</code> originally cannot themselves 61 * be serialized. Therefore, a {@link RowSetDynaClass} makes a very 62 * convenient mechanism for transporting data sets to remote Java-based 63 * application components.</p> 64 * 65 * @version $Id$ 66 */ 67 68 public class RowSetDynaClass extends JDBCDynaClass implements DynaClass, Serializable { 69 70 71 // ----------------------------------------------------- Instance variables 72 73 /** 74 * <p>Limits the size of the returned list. The call to 75 * <code>getRows()</code> will return at most limit number of rows. 76 * If less than or equal to 0, does not limit the size of the result. 77 */ 78 protected int limit = -1; 79 80 /** 81 * <p>The list of {@link DynaBean}s representing the contents of 82 * the original <code>ResultSet</code> on which this 83 * {@link RowSetDynaClass} was based.</p> 84 */ 85 protected List<DynaBean> rows = new ArrayList<DynaBean>(); 86 87 // ----------------------------------------------------------- Constructors 88 89 90 /** 91 * <p>Construct a new {@link RowSetDynaClass} for the specified 92 * <code>ResultSet</code>. The property names corresponding 93 * to column names in the result set will be lower cased.</p> 94 * 95 * @param resultSet The result set to be wrapped 96 * 97 * @throws NullPointerException if <code>resultSet</code> 98 * is <code>null</code> 99 * @throws SQLException if the metadata for this result set 100 * cannot be introspected 101 */ 102 public RowSetDynaClass(final ResultSet resultSet) throws SQLException { 103 104 this(resultSet, true, -1); 105 106 } 107 108 /** 109 * <p>Construct a new {@link RowSetDynaClass} for the specified 110 * <code>ResultSet</code>. The property names corresponding 111 * to column names in the result set will be lower cased.</p> 112 * 113 * If <code>limit</code> is not less than 0, max <code>limit</code> 114 * number of rows will be copied into the list. 115 * 116 * @param resultSet The result set to be wrapped 117 * @param limit The maximum for the size of the result. 118 * 119 * @throws NullPointerException if <code>resultSet</code> 120 * is <code>null</code> 121 * @throws SQLException if the metadata for this result set 122 * cannot be introspected 123 */ 124 public RowSetDynaClass(final ResultSet resultSet, final int limit) throws SQLException { 125 126 this(resultSet, true, limit); 127 128 } 129 130 131 /** 132 * <p>Construct a new {@link RowSetDynaClass} for the specified 133 * <code>ResultSet</code>. The property names corresponding 134 * to the column names in the result set will be lower cased or not, 135 * depending on the specified <code>lowerCase</code> value.</p> 136 * 137 * If <code>limit</code> is not less than 0, max <code>limit</code> 138 * number of rows will be copied into the resultset. 139 * 140 * 141 * @param resultSet The result set to be wrapped 142 * @param lowerCase Should property names be lower cased? 143 * 144 * @throws NullPointerException if <code>resultSet</code> 145 * is <code>null</code> 146 * @throws SQLException if the metadata for this result set 147 * cannot be introspected 148 */ 149 public RowSetDynaClass(final ResultSet resultSet, final boolean lowerCase) 150 throws SQLException { 151 this(resultSet, lowerCase, -1); 152 153 } 154 155 /** 156 * <p>Construct a new {@link RowSetDynaClass} for the specified 157 * <code>ResultSet</code>. The property names corresponding 158 * to the column names in the result set will be lower cased or not, 159 * depending on the specified <code>lowerCase</code> value.</p> 160 * 161 * <p><strong>WARNING</strong> - If you specify <code>false</code> 162 * for <code>lowerCase</code>, the returned property names will 163 * exactly match the column names returned by your JDBC driver. 164 * Because different drivers might return column names in different 165 * cases, the property names seen by your application will vary 166 * depending on which JDBC driver you are using.</p> 167 * 168 * @param resultSet The result set to be wrapped 169 * @param lowerCase Should property names be lower cased? 170 * @param limit Maximum limit for the <code>List</code> of {@link DynaBean} 171 * 172 * @throws NullPointerException if <code>resultSet</code> 173 * is <code>null</code> 174 * @throws SQLException if the metadata for this result set 175 * cannot be introspected 176 */ 177 public RowSetDynaClass(final ResultSet resultSet, final boolean lowerCase, final int limit) 178 throws SQLException { 179 180 this(resultSet, lowerCase, limit, false); 181 182 } 183 184 /** 185 * <p>Construct a new {@link RowSetDynaClass} for the specified 186 * <code>ResultSet</code>. The property names corresponding 187 * to the column names in the result set will be lower cased or not, 188 * depending on the specified <code>lowerCase</code> value.</p> 189 * 190 * <p><strong>WARNING</strong> - If you specify <code>false</code> 191 * for <code>lowerCase</code>, the returned property names will 192 * exactly match the column names returned by your JDBC driver. 193 * Because different drivers might return column names in different 194 * cases, the property names seen by your application will vary 195 * depending on which JDBC driver you are using.</p> 196 * 197 * @param resultSet The result set to be wrapped 198 * @param lowerCase Should property names be lower cased? 199 * @param useColumnLabel true if the column label should be used, otherwise false 200 * 201 * @throws NullPointerException if <code>resultSet</code> 202 * is <code>null</code> 203 * @throws SQLException if the metadata for this result set 204 * cannot be introspected 205 * @since 1.8.3 206 */ 207 public RowSetDynaClass(final ResultSet resultSet, final boolean lowerCase, final boolean useColumnLabel) 208 throws SQLException { 209 this(resultSet, lowerCase, -1, useColumnLabel); 210 211 } 212 213 /** 214 * <p>Construct a new {@link RowSetDynaClass} for the specified 215 * <code>ResultSet</code>. The property names corresponding 216 * to the column names in the result set will be lower cased or not, 217 * depending on the specified <code>lowerCase</code> value.</p> 218 * 219 * <p><strong>WARNING</strong> - If you specify <code>false</code> 220 * for <code>lowerCase</code>, the returned property names will 221 * exactly match the column names returned by your JDBC driver. 222 * Because different drivers might return column names in different 223 * cases, the property names seen by your application will vary 224 * depending on which JDBC driver you are using.</p> 225 * 226 * @param resultSet The result set to be wrapped 227 * @param lowerCase Should property names be lower cased? 228 * @param limit Maximum limit for the <code>List</code> of {@link DynaBean} 229 * @param useColumnLabel true if the column label should be used, otherwise false 230 * 231 * @throws NullPointerException if <code>resultSet</code> 232 * is <code>null</code> 233 * @throws SQLException if the metadata for this result set 234 * cannot be introspected 235 * @since 1.8.3 236 */ 237 public RowSetDynaClass(final ResultSet resultSet, final boolean lowerCase, final int limit, final boolean useColumnLabel) 238 throws SQLException { 239 240 if (resultSet == null) { 241 throw new NullPointerException(); 242 } 243 this.lowerCase = lowerCase; 244 this.limit = limit; 245 setUseColumnLabel(useColumnLabel); 246 introspect(resultSet); 247 copy(resultSet); 248 249 } 250 251 /** 252 * <p>Return a <code>List</code> containing the {@link DynaBean}s that 253 * represent the contents of each <code>Row</code> from the 254 * <code>ResultSet</code> that was the basis of this 255 * {@link RowSetDynaClass} instance. These {@link DynaBean}s are 256 * disconnected from the database itself, so there is no problem with 257 * modifying the contents of the list, or the values of the properties 258 * of these {@link DynaBean}s. However, it is the application's 259 * responsibility to persist any such changes back to the database, 260 * if it so desires.</p> 261 * 262 * @return A <code>List</code> of {@link DynaBean} instances 263 */ 264 public List<DynaBean> getRows() { 265 266 return (this.rows); 267 268 } 269 270 271 // ------------------------------------------------------ Protected Methods 272 273 274 /** 275 * <p>Copy the column values for each row in the specified 276 * <code>ResultSet</code> into a newly created {@link DynaBean}, and add 277 * this bean to the list of {@link DynaBean}s that will later by 278 * returned by a call to <code>getRows()</code>.</p> 279 * 280 * @param resultSet The <code>ResultSet</code> whose data is to be 281 * copied 282 * 283 * @throws SQLException if an error is encountered copying the data 284 */ 285 protected void copy(final ResultSet resultSet) throws SQLException { 286 287 int cnt = 0; 288 while (resultSet.next() && (limit < 0 || cnt++ < limit) ) { 289 final DynaBean bean = createDynaBean(); 290 for (DynaProperty propertie : properties) { 291 final String name = propertie.getName(); 292 final Object value = getObject(resultSet, name); 293 bean.set(name, value); 294 } 295 rows.add(bean); 296 } 297 298 } 299 300 301 /** 302 * <p>Create and return a new {@link DynaBean} instance to be used for 303 * representing a row in the underlying result set.</p> 304 * 305 * @return A new <code>DynaBean</code> instance 306 */ 307 protected DynaBean createDynaBean() { 308 309 return (new BasicDynaBean(this)); 310 311 } 312 313 314 }