1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
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
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
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 }