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 018package org.apache.commons.beanutils; 019 020import java.sql.ResultSet; 021import java.sql.SQLException; 022import java.util.ArrayList; 023import java.util.List; 024import java.util.Objects; 025 026/** 027 * <p>Implements {@link DynaClass} to create an in-memory collection 028 * of {@link DynaBean}s representing the results of an SQL query. Once the 029 * {@link DynaClass} instance has been created, the JDBC <code>ResultSet</code> 030 * and <code>Statement</code> on which it is based can be closed, and the 031 * underlying <code>Connection</code> can be returned to its connection pool 032 * (if you are using one).</p> 033 * 034 * <p>The normal usage pattern is something like:</p> 035 * <pre> 036 * Connection conn = ...; // Acquire connection from pool 037 * Statement stmt = conn.createStatement(); 038 * ResultSet rs = stmt.executeQuery("SELECT ..."); 039 * RowSetDynaClass rsdc = new RowSetDynaClass(rs); 040 * rs.close(); 041 * stmt.close(); 042 * ...; // Return connection to pool 043 * List rows = rsdc.getRows(); 044 * ...; // Process the rows as desired 045 * </pre> 046 * 047 * <p>Each column in the result set will be represented as a {@link DynaBean} 048 * property of the corresponding name (optionally forced to lower case 049 * for portability). There will be one {@link DynaBean} in the 050 * <code>List</code> returned by <code>getRows()</code> for each 051 * row in the original <code>ResultSet</code>.</p> 052 * 053 * <p>In general, instances of {@link RowSetDynaClass} can be serialized 054 * and deserialized, which will automatically include the list of 055 * {@link DynaBean}s representing the data content. The only exception 056 * to this rule would be when the underlying property values that were 057 * copied from the <code>ResultSet</code> originally cannot themselves 058 * be serialized. Therefore, a {@link RowSetDynaClass} makes a very 059 * convenient mechanism for transporting data sets to remote Java-based 060 * application components.</p> 061 * 062 */ 063 064public class RowSetDynaClass extends JDBCDynaClass { 065 066 private static final long serialVersionUID = 1L; 067 068 /** 069 * <p>Limits the size of the returned list. The call to 070 * <code>getRows()</code> will return at most limit number of rows. 071 * If less than or equal to 0, does not limit the size of the result. 072 */ 073 protected int limit = -1; 074 075 /** 076 * <p>The list of {@link DynaBean}s representing the contents of 077 * the original <code>ResultSet</code> on which this 078 * {@link RowSetDynaClass} was based.</p> 079 */ 080 protected List<DynaBean> rows = new ArrayList<>(); 081 082 /** 083 * <p>Construct a new {@link RowSetDynaClass} for the specified 084 * <code>ResultSet</code>. The property names corresponding 085 * to column names in the result set will be lower cased.</p> 086 * 087 * @param resultSet The result set to be wrapped 088 * @throws NullPointerException if <code>resultSet</code> 089 * is <code>null</code> 090 * @throws SQLException if the metadata for this result set 091 * cannot be introspected 092 */ 093 public RowSetDynaClass(final ResultSet resultSet) throws SQLException { 094 095 this(resultSet, true, -1); 096 097 } 098 099 /** 100 * <p>Construct a new {@link RowSetDynaClass} for the specified 101 * <code>ResultSet</code>. The property names corresponding 102 * to the column names in the result set will be lower cased or not, 103 * depending on the specified <code>lowerCase</code> value.</p> 104 * 105 * If <code>limit</code> is not less than 0, max <code>limit</code> 106 * number of rows will be copied into the resultset. 107 * 108 * 109 * @param resultSet The result set to be wrapped 110 * @param lowerCase Should property names be lower cased? 111 * @throws NullPointerException if <code>resultSet</code> 112 * is <code>null</code> 113 * @throws SQLException if the metadata for this result set 114 * cannot be introspected 115 */ 116 public RowSetDynaClass(final ResultSet resultSet, final boolean lowerCase) 117 throws SQLException { 118 this(resultSet, lowerCase, -1); 119 120 } 121 122 /** 123 * <p>Construct a new {@link RowSetDynaClass} for the specified 124 * <code>ResultSet</code>. The property names corresponding 125 * to the column names in the result set will be lower cased or not, 126 * depending on the specified <code>lowerCase</code> value.</p> 127 * 128 * <p><strong>WARNING</strong> - If you specify <code>false</code> 129 * for <code>lowerCase</code>, the returned property names will 130 * exactly match the column names returned by your JDBC driver. 131 * Because different drivers might return column names in different 132 * cases, the property names seen by your application will vary 133 * depending on which JDBC driver you are using.</p> 134 * 135 * @param resultSet The result set to be wrapped 136 * @param lowerCase Should property names be lower cased? 137 * @param useColumnLabel true if the column label should be used, otherwise false 138 * @throws NullPointerException if <code>resultSet</code> 139 * is <code>null</code> 140 * @throws SQLException if the metadata for this result set 141 * cannot be introspected 142 * @since 1.8.3 143 */ 144 public RowSetDynaClass(final ResultSet resultSet, final boolean lowerCase, final boolean useColumnLabel) 145 throws SQLException { 146 this(resultSet, lowerCase, -1, useColumnLabel); 147 148 } 149 150 /** 151 * <p>Construct a new {@link RowSetDynaClass} for the specified 152 * <code>ResultSet</code>. The property names corresponding 153 * to the column names in the result set will be lower cased or not, 154 * depending on the specified <code>lowerCase</code> value.</p> 155 * 156 * <p><strong>WARNING</strong> - If you specify <code>false</code> 157 * for <code>lowerCase</code>, the returned property names will 158 * exactly match the column names returned by your JDBC driver. 159 * Because different drivers might return column names in different 160 * cases, the property names seen by your application will vary 161 * depending on which JDBC driver you are using.</p> 162 * 163 * @param resultSet The result set to be wrapped 164 * @param lowerCase Should property names be lower cased? 165 * @param limit Maximum limit for the <code>List</code> of {@link DynaBean} 166 * @throws NullPointerException if <code>resultSet</code> 167 * is <code>null</code> 168 * @throws SQLException if the metadata for this result set 169 * cannot be introspected 170 */ 171 public RowSetDynaClass(final ResultSet resultSet, final boolean lowerCase, final int limit) 172 throws SQLException { 173 174 this(resultSet, lowerCase, limit, false); 175 176 } 177 178 /** 179 * <p>Construct a new {@link RowSetDynaClass} for the specified 180 * <code>ResultSet</code>. The property names corresponding 181 * to the column names in the result set will be lower cased or not, 182 * depending on the specified <code>lowerCase</code> value.</p> 183 * 184 * <p><strong>WARNING</strong> - If you specify <code>false</code> 185 * for <code>lowerCase</code>, the returned property names will 186 * exactly match the column names returned by your JDBC driver. 187 * Because different drivers might return column names in different 188 * cases, the property names seen by your application will vary 189 * depending on which JDBC driver you are using.</p> 190 * 191 * @param resultSet The result set to be wrapped 192 * @param lowerCase Should property names be lower cased? 193 * @param limit Maximum limit for the <code>List</code> of {@link DynaBean} 194 * @param useColumnLabel true if the column label should be used, otherwise false 195 * @throws NullPointerException if <code>resultSet</code> 196 * is <code>null</code> 197 * @throws SQLException if the metadata for this result set 198 * cannot be introspected 199 * @since 1.8.3 200 */ 201 public RowSetDynaClass(final ResultSet resultSet, final boolean lowerCase, final int limit, final boolean useColumnLabel) 202 throws SQLException { 203 Objects.requireNonNull(resultSet, "resultSet"); 204 this.lowerCase = lowerCase; 205 this.limit = limit; 206 setUseColumnLabel(useColumnLabel); 207 introspect(resultSet); 208 copy(resultSet); 209 } 210 211 /** 212 * <p>Construct a new {@link RowSetDynaClass} for the specified 213 * <code>ResultSet</code>. The property names corresponding 214 * to column names in the result set will be lower cased.</p> 215 * 216 * If <code>limit</code> is not less than 0, max <code>limit</code> 217 * number of rows will be copied into the list. 218 * 219 * @param resultSet The result set to be wrapped 220 * @param limit The maximum for the size of the result. 221 * @throws NullPointerException if <code>resultSet</code> 222 * is <code>null</code> 223 * @throws SQLException if the metadata for this result set 224 * cannot be introspected 225 */ 226 public RowSetDynaClass(final ResultSet resultSet, final int limit) throws SQLException { 227 228 this(resultSet, true, limit); 229 230 } 231 232 /** 233 * <p>Copy the column values for each row in the specified 234 * <code>ResultSet</code> into a newly created {@link DynaBean}, and add 235 * this bean to the list of {@link DynaBean}s that will later by 236 * returned by a call to <code>getRows()</code>.</p> 237 * 238 * @param resultSet The <code>ResultSet</code> whose data is to be 239 * copied 240 * 241 * @throws SQLException if an error is encountered copying the data 242 */ 243 protected void copy(final ResultSet resultSet) throws SQLException { 244 245 int cnt = 0; 246 while (resultSet.next() && (limit < 0 || cnt++ < limit) ) { 247 final DynaBean bean = createDynaBean(); 248 for (final DynaProperty propertie : properties) { 249 final String name = propertie.getName(); 250 final Object value = getObject(resultSet, name); 251 bean.set(name, value); 252 } 253 rows.add(bean); 254 } 255 256 } 257 258 /** 259 * <p>Create and return a new {@link DynaBean} instance to be used for 260 * representing a row in the underlying result set.</p> 261 * 262 * @return A new <code>DynaBean</code> instance 263 */ 264 protected DynaBean createDynaBean() { 265 266 return new BasicDynaBean(this); 267 268 } 269 270 /** 271 * <p>Return a <code>List</code> containing the {@link DynaBean}s that 272 * represent the contents of each <code>Row</code> from the 273 * <code>ResultSet</code> that was the basis of this 274 * {@link RowSetDynaClass} instance. These {@link DynaBean}s are 275 * disconnected from the database itself, so there is no problem with 276 * modifying the contents of the list, or the values of the properties 277 * of these {@link DynaBean}s. However, it is the application's 278 * responsibility to persist any such changes back to the database, 279 * if it so desires.</p> 280 * 281 * @return A <code>List</code> of {@link DynaBean} instances 282 */ 283 public List<DynaBean> getRows() { 284 285 return this.rows; 286 287 } 288 289}