View Javadoc
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 }