View Javadoc

1   /*
2    * Copyright 2001-2004 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */ 
16  
17  
18  package org.apache.commons.beanutils;
19  
20  
21  import java.io.Serializable;
22  import java.sql.ResultSet;
23  import java.sql.SQLException;
24  import java.util.ArrayList;
25  import java.util.List;
26  
27  
28  /**
29   * <p>Implementation of {@link DynaClass} that creates an in-memory collection
30   * of {@link DynaBean}s representing the results of an SQL query.  Once the
31   * {@link DynaClass} instance has been created, the JDBC <code>ResultSet</code>
32   * and <code>Statement</code> on which it is based can be closed, and the
33   * underlying <code>Connection</code> can be returned to its connection pool
34   * (if you are using one).</p>
35   *
36   * <p>The normal usage pattern is something like:</p>
37   * <pre>
38   *   Connection conn = ...;  // Acquire connection from pool
39   *   Statement stmt = conn.createStatement();
40   *   ResultSet rs = stmt.executeQuery("SELECT ...");
41   *   RowSetDynaClass rsdc = new RowSetDynaClass(rs);
42   *   rs.close();
43   *   stmt.close();
44   *   ...;                    // Return connection to pool
45   *   List rows = rsdc.getRows();
46   *   ...;                   // Process the rows as desired
47   * </pre>
48   *
49   * <p>Each column in the result set will be represented as a {@link DynaBean}
50   * property of the corresponding name (optionally forced to lower case
51   * for portability).  There will be one {@link DynaBean} in the
52   * <code>List</code> returned by <code>getRows()</code> for each
53   * row in the original <code>ResultSet</code>.</p>
54   *
55   * <p>In general, instances of {@link RowSetDynaClass} can be serialized
56   * and deserialized, which will automatically include the list of
57   * {@link DynaBean}s representing the data content.  The only exception
58   * to this rule would be when the underlying property values that were
59   * copied from the <code>ResultSet</code> originally cannot themselves
60   * be serialized.  Therefore, a {@link RowSetDynaClass} makes a very
61   * convenient mechanism for transporting data sets to remote Java-based
62   * application components.</p>
63   *
64   * @author Craig R. McClanahan
65   * @version $Revision: 1.9 $ $Date: 2004/02/28 13:18:34 $
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 rows = new ArrayList();
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       * @exception NullPointerException if <code>resultSet</code>
98       *  is <code>null</code>
99       * @exception SQLException if the metadata for this result set
100      *  cannot be introspected
101      */
102     public RowSetDynaClass(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      * @exception NullPointerException if <code>resultSet</code>
120      *  is <code>null</code>
121      * @exception SQLException if the metadata for this result set
122      *  cannot be introspected
123      */
124     public RowSetDynaClass(ResultSet resultSet, 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      * @exception NullPointerException if <code>resultSet</code>
145      *  is <code>null</code>
146      * @exception SQLException if the metadata for this result set
147      *  cannot be introspected
148      */
149     public RowSetDynaClass(ResultSet resultSet, 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      *
171      * @exception NullPointerException if <code>resultSet</code>
172      *  is <code>null</code>
173      * @exception SQLException if the metadata for this result set
174      *  cannot be introspected
175      */
176     public RowSetDynaClass(ResultSet resultSet, boolean lowerCase, int limit)
177                                                             throws SQLException {
178 
179         if (resultSet == null) {
180             throw new NullPointerException();
181         }
182         this.lowerCase = lowerCase;
183         this.limit = limit;
184         introspect(resultSet);
185         copy(resultSet);
186 
187     }
188 
189     /**
190      * <p>Return a <code>List</code> containing the {@link DynaBean}s that
191      * represent the contents of each <code>Row</code> from the
192      * <code>ResultSet</code> that was the basis of this
193      * {@link RowSetDynaClass} instance.  These {@link DynaBean}s are
194      * disconnected from the database itself, so there is no problem with
195      * modifying the contents of the list, or the values of the properties
196      * of these {@link DynaBean}s.  However, it is the application's
197      * responsibility to persist any such changes back to the database,
198      * if it so desires.</p>
199      */
200     public List getRows() {
201 
202         return (this.rows);
203 
204     }
205 
206 
207     // ------------------------------------------------------ Protected Methods
208 
209 
210     /**
211      * <p>Copy the column values for each row in the specified
212      * <code>ResultSet</code> into a newly created {@link DynaBean}, and add
213      * this bean to the list of {@link DynaBean}s that will later by
214      * returned by a call to <code>getRows()</code>.</p>
215      *
216      * @param resultSet The <code>ResultSet</code> whose data is to be
217      *  copied
218      *
219      * @exception SQLException if an error is encountered copying the data
220      */
221     protected void copy(ResultSet resultSet) throws SQLException {
222 
223         int cnt = 0;
224         while (resultSet.next() && (limit < 0  || cnt++ < limit) ) {	
225             DynaBean bean = createDynaBean();
226             for (int i = 0; i < properties.length; i++) {
227                 String name = properties[i].getName();
228                 bean.set(name, resultSet.getObject(name));
229             }
230             rows.add(bean);
231         }
232 
233     }
234 
235 
236     /**
237      * <p>Create and return a new {@link DynaBean} instance to be used for
238      * representing a row in the underlying result set.</p>
239      */
240     protected DynaBean createDynaBean() {
241 
242         return (new BasicDynaBean(this));
243 
244     }
245 
246 
247 }