1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 package net.mlw.vlh.adapter.jdbc.dynabean.fix;
64
65
66 import java.sql.ResultSet;
67 import java.sql.SQLException;
68 import java.util.HashMap;
69 import java.util.Iterator;
70 import java.util.Map;
71
72 import org.apache.commons.beanutils.DynaBean;
73 import org.apache.commons.beanutils.DynaClass;
74 import org.apache.commons.beanutils.DynaProperty;
75
76
77 /***
78 * <p>Implementation of <code>DynaClass</code> for DynaBeans that wrap the
79 * <code>java.sql.Row</code> objects of a <code>java.sql.ResultSet</code>.
80 * The normal usage pattern is something like:</p>
81 * <pre>
82 * ResultSet rs = ...;
83 * ResultSetDynaClass rsdc = new ResultSetDynaClass(rs);
84 * Iterator rows = rsdc.iterator();
85 * while (rows.hasNext()) {
86 * DynaBean row = (DynaBean) rows.next();
87 * ... process this row ...
88 * }
89 * rs.close();
90 * </pre>
91 *
92 * <p>Each column in the result set will be represented as a DynaBean
93 * property of the corresponding name (optionally forced to lower case
94 * for portability).</p>
95 *
96 * <p><strong>WARNING</strong> - Any {@link DynaBean} instance returned by
97 * this class, or from the <code>Iterator</code> returned by the
98 * <code>iterator()</code> method, is directly linked to the row that the
99 * underlying result set is currently positioned at. This has the following
100 * implications:</p>
101 * <ul>
102 * <li>Once you retrieve a different {@link DynaBean} instance, you should
103 * no longer use any previous instance.</li>
104 * <li>Changing the position of the underlying result set will change the
105 * data that the {@link DynaBean} references.</li>
106 * <li>Once the underlying result set is closed, the {@link DynaBean}
107 * instance may no longer be used.</li>
108 * </ul>
109 *
110 * <p>Any database data that you wish to utilize outside the context of the
111 * current row of an open result set must be copied. For example, you could
112 * use the following code to create standalone copies of the information in
113 * a result set:</p>
114 * <pre>
115 * ArrayList results = new ArrayList(); // To hold copied list
116 * ResultSetDynaClass rsdc = ...;
117 * DynaProperty properties[] = rsdc.getDynaProperties();
118 * BasicDynaClass bdc =
119 * new BasicDynaClass("foo", BasicDynaBean.class,
120 * rsdc.getDynaProperties());
121 * Iterator rows = rsdc.iterator();
122 * while (rows.hasNext()) {
123 * DynaBean oldRow = (DynaBean) rows.next();
124 * DynaBean newRow = bdc.newInstance();
125 * PropertyUtils.copyProperties(newRow, oldRow);
126 * results.add(newRow);
127 * }
128 * </pre>
129 *
130 * @author Craig R. McClanahan
131 * @version $Revision: 1.3 $ $Date: 2005/08/19 16:06:29 $
132 */
133
134 public class ResultSetDynaClass extends JDBCDynaClass implements DynaClass {
135
136
137
138
139
140 /***
141 * <p>Construct a new ResultSetDynaClass for the specified
142 * <code>ResultSet</code>. The property names corresponding
143 * to column names in the result set will be lower cased.</p>
144 *
145 * @param resultSet The result set to be wrapped
146 *
147 * @exception NullPointerException if <code>resultSet</code>
148 * is <code>null</code>
149 * @exception SQLException if the metadata for this result set
150 * cannot be introspected
151 */
152 public ResultSetDynaClass(ResultSet resultSet) throws SQLException {
153
154 this(resultSet, true, false);
155
156 }
157
158
159 /***
160 * <p>Construct a new ResultSetDynaClass for the specified
161 * <code>ResultSet</code>. The property names corresponding
162 * to the column names in the result set will be lower cased or not,
163 * depending on the specified <code>lowerCase</code> value.</p>
164 *
165 * <p><strong>WARNING</strong> - If you specify <code>false</code>
166 * for <code>lowerCase</code>, the returned property names will
167 * exactly match the column names returned by your JDBC driver.
168 * Because different drivers might return column names in different
169 * cases, the property names seen by your application will vary
170 * depending on which JDBC driver you are using.</p>
171 *
172 * @param resultSet The result set to be wrapped
173 * @param lowerCase Should property names be lower cased?
174 * @param useName Should property names b ethe column name of the
175 * label name?
176 * @exception NullPointerException if <code>resultSet</code>
177 * is <code>null</code>
178 * @exception SQLException if the metadata for this result set
179 * cannot be introspected
180 */
181 public ResultSetDynaClass(ResultSet resultSet, boolean lowerCase, boolean useName)
182 throws SQLException {
183
184 if (resultSet == null) {
185 throw new NullPointerException();
186 }
187 this.resultSet = resultSet;
188 this.lowerCase = lowerCase;
189 this.useName = useName;
190 introspect(resultSet);
191
192 }
193
194
195
196
197
198 /***
199 * Flag defining whether column names should be lower cased when
200 * converted to property names.
201 */
202 protected boolean lowerCase = true;
203
204
205 /***
206 * The set of dynamic properties that are part of this DynaClass.
207 */
208 protected DynaProperty properties[] = null;
209
210
211 /***
212 * The set of dynamic properties that are part of this DynaClass,
213 * keyed by the property name. Individual descriptor instances will
214 * be the same instances as those in the <code>properties</code> list.
215 */
216 protected Map propertiesMap = new HashMap();
217
218
219 /***
220 * <p>The <code>ResultSet</code> we are wrapping.</p>
221 */
222 protected ResultSet resultSet = null;
223
224
225
226
227
228 /***
229 * <p>Return an <code>Iterator</code> of {@link DynaBean} instances for
230 * each row of the wrapped <code>ResultSet</code>, in "forward" order.
231 * Unless the underlying result set supports scrolling, this method
232 * should be called only once.</p>
233 */
234 public Iterator iterator() {
235
236 return (new ResultSetIterator(this));
237
238 }
239
240
241
242
243
244 /***
245 * <p>Return the result set we are wrapping.</p>
246 */
247 ResultSet getResultSet() {
248
249 return (this.resultSet);
250
251 }
252
253
254
255
256 /***
257 * <p>Loads the class of the given name which by default uses the class loader used
258 * to load this library.
259 * Dervations of this class could implement alternative class loading policies such as
260 * using custom ClassLoader or using the Threads's context class loader etc.
261 * </p>
262 */
263 protected Class loadClass(String className) throws SQLException {
264
265 try {
266 return getClass().getClassLoader().loadClass(className);
267 }
268 catch (Exception e) {
269 throw new SQLException("Cannot load column class '" +
270 className + "': " + e);
271 }
272 }
273 }