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   * @author Craig R. McClanahan
66   * @version $Revision: 556233 $ $Date: 2007-07-14 07:37:06 +0100 (Sat, 14 Jul 2007) $
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         if (resultSet == null) {
182             throw new NullPointerException();
183         }
184         this.lowerCase = lowerCase;
185         this.limit = limit;
186         introspect(resultSet);
187         copy(resultSet);
188 
189     }
190 
191     /***
192      * <p>Return a <code>List</code> containing the {@link DynaBean}s that
193      * represent the contents of each <code>Row</code> from the
194      * <code>ResultSet</code> that was the basis of this
195      * {@link RowSetDynaClass} instance.  These {@link DynaBean}s are
196      * disconnected from the database itself, so there is no problem with
197      * modifying the contents of the list, or the values of the properties
198      * of these {@link DynaBean}s.  However, it is the application's
199      * responsibility to persist any such changes back to the database,
200      * if it so desires.</p>
201      *
202      * @return A <code>List</code> of {@link DynaBean} instances
203      */
204     public List getRows() {
205 
206         return (this.rows);
207 
208     }
209 
210 
211     // ------------------------------------------------------ Protected Methods
212 
213 
214     /***
215      * <p>Copy the column values for each row in the specified
216      * <code>ResultSet</code> into a newly created {@link DynaBean}, and add
217      * this bean to the list of {@link DynaBean}s that will later by
218      * returned by a call to <code>getRows()</code>.</p>
219      *
220      * @param resultSet The <code>ResultSet</code> whose data is to be
221      *  copied
222      *
223      * @exception SQLException if an error is encountered copying the data
224      */
225     protected void copy(ResultSet resultSet) throws SQLException {
226 
227         int cnt = 0;
228         while (resultSet.next() && (limit < 0  || cnt++ < limit) ) {
229             DynaBean bean = createDynaBean();
230             for (int i = 0; i < properties.length; i++) {
231                 String name = properties[i].getName();
232                 Object value = getObject(resultSet, name);
233                 bean.set(name, value);
234             }
235             rows.add(bean);
236         }
237 
238     }
239 
240 
241     /***
242      * <p>Create and return a new {@link DynaBean} instance to be used for
243      * representing a row in the underlying result set.</p>
244      *
245      * @return A new <code>DynaBean</code> instance
246      */
247     protected DynaBean createDynaBean() {
248 
249         return (new BasicDynaBean(this));
250 
251     }
252 
253 
254 }