Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
ResultSetUtils |
|
| 7.833333333333333;7.833 |
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 | package org.apache.commons.scaffold.sql; | |
18 | ||
19 | ||
20 | import java.beans.PropertyDescriptor; | |
21 | import java.lang.reflect.InvocationTargetException; | |
22 | import java.lang.reflect.Method; | |
23 | import java.sql.ResultSet; | |
24 | import java.sql.ResultSetMetaData; | |
25 | import java.sql.SQLException; | |
26 | import java.sql.Types; | |
27 | import java.util.ArrayList; | |
28 | import java.util.Collection; | |
29 | import java.util.HashMap; | |
30 | import java.util.Iterator; | |
31 | import java.util.Map; | |
32 | ||
33 | import org.apache.commons.beanutils.BeanUtils; | |
34 | import org.apache.commons.beanutils.PropertyUtils; | |
35 | ||
36 | ||
37 | /** | |
38 | * General purpose utility methods related to ResultSets | |
39 | * | |
40 | * @author Ted Husted | |
41 | * @version $Revision: 561366 $ $Date: 2007-07-31 16:58:29 +0100 (Tue, 31 Jul 2007) $ | |
42 | */ | |
43 | 0 | public class ResultSetUtils { |
44 | ||
45 | ||
46 | /** | |
47 | * Returns next record of result set as a Map. | |
48 | * The keys of the map are the column names, | |
49 | * as returned by the metadata. | |
50 | * The values are the columns as Objects. | |
51 | * | |
52 | * @param resultSet The ResultSet to process. | |
53 | * @exception SQLException if an error occurs. | |
54 | */ | |
55 | public static Map getMap(ResultSet resultSet) | |
56 | throws SQLException { | |
57 | ||
58 | // Acquire resultSet MetaData | |
59 | 0 | ResultSetMetaData metaData = resultSet.getMetaData(); |
60 | 0 | int cols = metaData.getColumnCount(); |
61 | ||
62 | // Create hashmap, sized to number of columns | |
63 | 0 | HashMap row = new HashMap(cols,1); |
64 | ||
65 | // Transfer record into hashmap | |
66 | 0 | if (resultSet.next()) { |
67 | 0 | for (int i=1; i<=cols ; i++) { |
68 | 0 | row.put(metaData.getColumnName(i), |
69 | resultSet.getObject(i)); | |
70 | } | |
71 | } // end while | |
72 | ||
73 | 0 | return ((Map) row); |
74 | ||
75 | } // end getMap | |
76 | ||
77 | ||
78 | /** | |
79 | * Return a Collection of Maps, each representing | |
80 | * a row from the ResultSet. | |
81 | * The keys of the map are the column names, | |
82 | * as returned by the metadata. | |
83 | * The values are the columns as Objects. | |
84 | * | |
85 | * @param resultSet The ResultSet to process. | |
86 | * @exception SQLException if an error occurs. | |
87 | */ | |
88 | public static Collection getMaps(ResultSet resultSet) | |
89 | throws SQLException { | |
90 | ||
91 | // Acquire resultSet MetaData | |
92 | 0 | ResultSetMetaData metaData = resultSet.getMetaData(); |
93 | 0 | int cols = metaData.getColumnCount(); |
94 | ||
95 | // Use ArrayList to maintain ResultSet sequence | |
96 | 0 | ArrayList list = new ArrayList(); |
97 | ||
98 | // Scroll to each record, make map of row, add to list | |
99 | 0 | while (resultSet.next()) { |
100 | 0 | HashMap row = new HashMap(cols,1); |
101 | 0 | for (int i=1; i<=cols ; i++) { |
102 | 0 | row.put(metaData.getColumnName(i), |
103 | resultSet.getString(i)); | |
104 | } | |
105 | 0 | list.add(row); |
106 | 0 | } // end while |
107 | ||
108 | 0 | return ((Collection) list); |
109 | ||
110 | } // end getMaps | |
111 | ||
112 | ||
113 | /** | |
114 | * Populate the JavaBean properties of the specified bean, based on | |
115 | * the specified name/value pairs. This method uses Java reflection APIs | |
116 | * to identify corresponding "property setter" method names. The type of | |
117 | * the value in the Map must match the setter type. The setter must | |
118 | * expect a single arguement (the one on the Map). | |
119 | * <p> | |
120 | * The particular setter method to be called for each property is | |
121 | * determined using the usual JavaBeans introspection mechanisms. Thus, | |
122 | * you may identify custom setter methods using a BeanInfo class that is | |
123 | * associated with the class of the bean itself. If no such BeanInfo | |
124 | * class is available, the standard method name conversion ("set" plus | |
125 | * the capitalized name of the property in question) is used. | |
126 | * <p> | |
127 | * <strong>NOTE</strong>: It is contrary to the JavaBeans Specification | |
128 | * to have more than one setter method (with different argument | |
129 | * signatures) for the same property. | |
130 | * <p> | |
131 | * This method adopted from the Apache Commons BeanUtils.populate. | |
132 | * | |
133 | * @author Craig R. McClanahan | |
134 | * @author Ralph Schaer | |
135 | * @author Chris Audley | |
136 | * @author Rey Fran�ois | |
137 | * @author Gregor Ra�man | |
138 | * @author Ted Husted | |
139 | * | |
140 | * @param bean JavaBean whose properties are being populated | |
141 | * @param properties Map keyed by property name, with the | |
142 | * corresponding value to be set | |
143 | * | |
144 | * @exception IllegalAccessException if the caller does not have | |
145 | * access to the property accessor method | |
146 | * @exception InvocationTargetException if the property accessor method | |
147 | * throws an exception | |
148 | * @deprecated Use BeanUtils.CopyProperties instead. | |
149 | */ | |
150 | public static void setProperties(Object bean, Map properties) | |
151 | throws IllegalAccessException, InvocationTargetException { | |
152 | ||
153 | 0 | if ((bean == null) || (properties == null)) |
154 | 0 | return; |
155 | ||
156 | /* | |
157 | if (debug >= 1) | |
158 | System.out.println("BeanUtils.populate(" + bean + ", " + | |
159 | properties + ")"); | |
160 | */ | |
161 | ||
162 | // Loop through the property name/value pairs to be set | |
163 | 0 | Iterator names = properties.keySet().iterator(); |
164 | 0 | while (names.hasNext()) { |
165 | ||
166 | // Identify the property name and value(s) to be assigned | |
167 | 0 | String name = (String) names.next(); |
168 | 0 | if (name == null) |
169 | 0 | continue; |
170 | ||
171 | // Get the property descriptor of the requested property (if any) | |
172 | 0 | PropertyDescriptor descriptor = null; |
173 | try { | |
174 | 0 | descriptor = PropertyUtils.getPropertyDescriptor(bean, name); |
175 | 0 | } catch (Throwable t) { |
176 | /* | |
177 | if (debug >= 1) | |
178 | System.out.println(" getPropertyDescriptor: " + t); | |
179 | */ | |
180 | 0 | descriptor = null; |
181 | 0 | } |
182 | 0 | if (descriptor == null) { |
183 | /* | |
184 | if (debug >= 1) | |
185 | System.out.println(" No such property, skipping"); | |
186 | */ | |
187 | 0 | continue; |
188 | } | |
189 | ||
190 | /* | |
191 | if (debug >= 1) | |
192 | System.out.println(" Property descriptor is '" + | |
193 | descriptor + "'"); | |
194 | */ | |
195 | ||
196 | // Identify the relevant setter method (if there is one) | |
197 | 0 | Method setter = descriptor.getWriteMethod(); |
198 | 0 | if (setter == null) { |
199 | /* | |
200 | if (debug >= 1) | |
201 | System.out.println(" No setter method, skipping"); | |
202 | */ | |
203 | 0 | continue; |
204 | } | |
205 | ||
206 | // Obtain value to be set | |
207 | 0 | Object[] args = new Object[1]; |
208 | 0 | args[0] = properties.get(name); // This MUST match setter type |
209 | ||
210 | /* | |
211 | if (debug >= 1) | |
212 | System.out.println(" name='" + name + "', value.class='" + | |
213 | (value == null ? "NONE" : | |
214 | value.getClass().getName()) + "'"); | |
215 | */ | |
216 | /* | |
217 | if (debug >= 1) | |
218 | System.out.println(" Setting to " + | |
219 | (parameters[0] == null ? "NULL" : | |
220 | "'" + parameters[0] + "'")); | |
221 | */ | |
222 | ||
223 | // Invoke the setter method | |
224 | 0 | setter.invoke(bean,args); |
225 | 0 | } |
226 | ||
227 | /* | |
228 | if (debug >= 1) | |
229 | System.out.println("============================================"); | |
230 | */ | |
231 | ||
232 | 0 | } // end setProperties |
233 | ||
234 | ||
235 | /** | |
236 | * Map JDBC objects to Java equivalents. | |
237 | * Used by getBean() and getBeans(). | |
238 | * <p> | |
239 | * Some types not supported. | |
240 | * Many not work with all drivers. | |
241 | * <p> | |
242 | * Makes binary conversions of BIGINT, DATE, DECIMAL, DOUBLE, FLOAT, INTEGER, | |
243 | * REAL, SMALLINT, TIME, TIMESTAMP, TINYINT. | |
244 | * Makes Sting conversions of CHAR, CLOB, VARCHAR, LONGVARCHAR, BLOB, LONGVARBINARY, | |
245 | * VARBINARY. | |
246 | * <p> | |
247 | * DECIMAL, INTEGER, SMALLINT, TIMESTAMP, CHAR, VARCHAR tested with MySQL and Poolman. | |
248 | * Others not guaranteed. | |
249 | */ | |
250 | public static void putEntry( | |
251 | Map properties, | |
252 | ResultSetMetaData metaData, | |
253 | ResultSet resultSet, | |
254 | int i) | |
255 | throws SQLException { | |
256 | ||
257 | /* | |
258 | In a perfect universe, this would be enough | |
259 | properties.put( | |
260 | metaData.getColumnName(i), | |
261 | resultSet.getObject(i)); | |
262 | But only String, Timestamp, and Integer seem to get through that way. | |
263 | */ | |
264 | ||
265 | 0 | String columnName = metaData.getColumnName(i); |
266 | ||
267 | 0 | switch (metaData.getColumnType(i)) { |
268 | ||
269 | // http://java.sun.com/j2se/1.3.0/docs/api/java/sql/Types.html | |
270 | ||
271 | case Types.BIGINT: | |
272 | 0 | properties.put(columnName, |
273 | new Long(resultSet.getLong(i))); | |
274 | 0 | break; |
275 | ||
276 | case Types.DATE: | |
277 | 0 | properties.put(columnName, |
278 | resultSet.getDate(i)); | |
279 | 0 | break; |
280 | ||
281 | case Types.DECIMAL: | |
282 | case Types.DOUBLE: | |
283 | 0 | properties.put(columnName, |
284 | new Double(resultSet.getDouble(i))); | |
285 | 0 | break; |
286 | ||
287 | case Types.FLOAT: | |
288 | 0 | properties.put(columnName, |
289 | new Float(resultSet.getFloat(i))); | |
290 | 0 | break; |
291 | ||
292 | case Types.INTEGER: | |
293 | 0 | properties.put(columnName, |
294 | new Integer(resultSet.getInt(i))); | |
295 | 0 | break; |
296 | ||
297 | case Types.REAL: | |
298 | 0 | properties.put(columnName, |
299 | new Double(resultSet.getString(i))); | |
300 | 0 | break; |
301 | ||
302 | case Types.SMALLINT: | |
303 | 0 | properties.put(columnName, |
304 | new Short(resultSet.getShort(i))); | |
305 | 0 | break; |
306 | ||
307 | case Types.TIME: | |
308 | 0 | properties.put(columnName, |
309 | resultSet.getTime(i)); | |
310 | 0 | break; |
311 | ||
312 | case Types.TIMESTAMP: | |
313 | 0 | properties.put(columnName, |
314 | resultSet.getTimestamp(i)); | |
315 | 0 | break; |
316 | ||
317 | // :FIXME: Throws java.lang.ClassCastException: java.lang.Integer | |
318 | // :FIXME: with Poolman and MySQL unless use getString. | |
319 | case Types.TINYINT: | |
320 | 0 | properties.put(columnName, |
321 | new Byte(resultSet.getString(i))); | |
322 | 0 | break; |
323 | ||
324 | case Types.CHAR: | |
325 | case Types.CLOB: | |
326 | case Types.VARCHAR: | |
327 | case Types.LONGVARCHAR: | |
328 | // :FIXME: Handle binaries differently? | |
329 | case Types.BLOB: | |
330 | case Types.LONGVARBINARY: | |
331 | case Types.VARBINARY: | |
332 | 0 | properties.put(columnName, |
333 | resultSet.getString(i)); | |
334 | 0 | break; |
335 | ||
336 | /* | |
337 | :FIXME: Add handlers for | |
338 | ARRAY | |
339 | BINARY | |
340 | BIT | |
341 | DISTINCT | |
342 | JAVA_OBJECT | |
343 | NULL | |
344 | NUMERIC | |
345 | OTHER | |
346 | REF | |
347 | STRUCT | |
348 | */ | |
349 | ||
350 | // Otherwise, pass as *String property to be converted | |
351 | default: | |
352 | 0 | properties.put(columnName + "String", |
353 | resultSet.getString(i)); | |
354 | break; | |
355 | } // end switch | |
356 | ||
357 | 0 | } // end putEntry |
358 | ||
359 | ||
360 | /** | |
361 | * Populate target bean with the first record from a ResultSet. | |
362 | * | |
363 | * @param resultSet The ResultSet whose parameters are to be used | |
364 | * to populate bean properties | |
365 | * @param target An instance of the bean to populate | |
366 | * @exception SQLException if an exception is thrown while setting | |
367 | * property values, populating the bean, or accessing the ResultSet | |
368 | * @return True if resultSet contained a next element | |
369 | */ | |
370 | public static boolean getElement(Object target, ResultSet resultSet) | |
371 | throws SQLException { | |
372 | ||
373 | // Check prerequisites | |
374 | 0 | if ((target==null) || (resultSet==null)) |
375 | 0 | throw new SQLException("getElement: Null parameter"); |
376 | ||
377 | // Acquire resultSet MetaData | |
378 | 0 | ResultSetMetaData metaData = resultSet.getMetaData(); |
379 | 0 | int cols = metaData.getColumnCount(); |
380 | ||
381 | // Create hashmap, sized to number of columns | |
382 | 0 | HashMap properties = new HashMap(cols,1); |
383 | ||
384 | // Scroll to next record and pump into hashmap | |
385 | 0 | boolean found = false; |
386 | 0 | if (resultSet.next()) { |
387 | 0 | found = true; |
388 | 0 | for (int i=1; i<=cols ; i++) { |
389 | 0 | putEntry(properties,metaData,resultSet,i); |
390 | } | |
391 | try { | |
392 | 0 | BeanUtils.copyProperties(target,properties); |
393 | } | |
394 | 0 | catch (Throwable t) { |
395 | 0 | throw new SQLException("ResultSetUtils.getElement: " + |
396 | t.getMessage() + properties.toString()); | |
397 | 0 | } |
398 | ||
399 | } // end if | |
400 | ||
401 | 0 | return found; |
402 | ||
403 | } // end getElement | |
404 | ||
405 | ||
406 | /** | |
407 | * Return a ArrayList of beans populated from a ResultSet. | |
408 | * | |
409 | * @param resultSet The ResultSet whose parameters are to be used | |
410 | * to populate bean properties | |
411 | * @param target An instance of the bean to populate | |
412 | * @exception SQLException if an exception is thrown while setting | |
413 | * property values, populating the bean, or accessing the ResultSet | |
414 | */ | |
415 | public static Collection getCollection(Object target, ResultSet resultSet) | |
416 | throws SQLException { | |
417 | ||
418 | // Check prerequisites | |
419 | 0 | if ((target==null) || (resultSet==null)) |
420 | 0 | throw new SQLException("getCollection: Null parameter"); |
421 | ||
422 | // Acquire resultSet MetaData | |
423 | 0 | ResultSetMetaData metaData = resultSet.getMetaData(); |
424 | 0 | int cols = metaData.getColumnCount(); |
425 | ||
426 | // Create hashmap, sized to number of columns | |
427 | 0 | HashMap properties = new HashMap(cols,1); |
428 | ||
429 | // Use ArrayList to maintain ResultSet sequence | |
430 | 0 | ArrayList list = new ArrayList(); |
431 | ||
432 | // Acquire target class | |
433 | 0 | Class factory = target.getClass(); |
434 | ||
435 | // Scroll to next record and pump into hashmap | |
436 | 0 | while (resultSet.next()) { |
437 | 0 | for (int i=1; i<=cols ; i++) { |
438 | 0 | putEntry(properties,metaData,resultSet,i); |
439 | } | |
440 | try { | |
441 | 0 | Object bean = factory.newInstance(); |
442 | 0 | BeanUtils.copyProperties(bean,properties); |
443 | 0 | list.add(bean); |
444 | } | |
445 | 0 | catch (Throwable t) { |
446 | 0 | throw new SQLException("RequestUtils.getCollection: " + |
447 | t.getMessage()); | |
448 | 0 | } |
449 | ||
450 | 0 | properties.clear(); |
451 | ||
452 | } // end while | |
453 | ||
454 | 0 | return ((Collection) list); |
455 | ||
456 | } // end getCollection | |
457 | ||
458 | ||
459 | } // end ResultSetUtils | |
460 |