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 * @author Craig R. McClanahan
66 * @version $Revision: 926685 $ $Date: 2010-03-23 17:59:08 +0000 (Tue, 23 Mar 2010) $
67 */
68
69 public class RowSetDynaClass extends JDBCDynaClass implements DynaClass, Serializable {
70
71
72 // ----------------------------------------------------- Instance variables
73
74 /**
75 * <p>Limits the size of the returned list. The call to
76 * <code>getRows()</code> will return at most limit number of rows.
77 * If less than or equal to 0, does not limit the size of the result.
78 */
79 protected int limit = -1;
80
81 /**
82 * <p>The list of {@link DynaBean}s representing the contents of
83 * the original <code>ResultSet</code> on which this
84 * {@link RowSetDynaClass} was based.</p>
85 */
86 protected List rows = new ArrayList();
87
88 // ----------------------------------------------------------- Constructors
89
90
91 /**
92 * <p>Construct a new {@link RowSetDynaClass} for the specified
93 * <code>ResultSet</code>. The property names corresponding
94 * to column names in the result set will be lower cased.</p>
95 *
96 * @param resultSet The result set to be wrapped
97 *
98 * @exception NullPointerException if <code>resultSet</code>
99 * 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 }