KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > persist > Persist


1 // $Id: Persist.java 12 2007-08-29 05:23:13Z jcamaia $
2

3 package net.sf.persist;
4
5 import java.io.IOException JavaDoc;
6 import java.lang.reflect.Method JavaDoc;
7 import java.math.BigDecimal JavaDoc;
8 import java.sql.Blob JavaDoc;
9 import java.sql.Clob JavaDoc;
10 import java.sql.Connection JavaDoc;
11 import java.sql.ParameterMetaData JavaDoc;
12 import java.sql.PreparedStatement JavaDoc;
13 import java.sql.ResultSet JavaDoc;
14 import java.sql.ResultSetMetaData JavaDoc;
15 import java.sql.SQLException JavaDoc;
16 import java.util.ArrayList JavaDoc;
17 import java.util.Arrays JavaDoc;
18 import java.util.Iterator JavaDoc;
19 import java.util.LinkedHashMap JavaDoc;
20 import java.util.List JavaDoc;
21 import java.util.Map JavaDoc;
22 import java.util.concurrent.ConcurrentHashMap JavaDoc;
23 import java.util.concurrent.ConcurrentMap JavaDoc;
24
25 /**
26  * The main class for the persistence engine.
27  * <p>
28  * A Persist instance is bound to a {@link java.sql.Connection} object.
29  * Internally, Persist caches table-object mappings under <i>cache names</i>
30  * that allow for different mappings (most likely from different database
31  * schemas) to coexist. The default cache name is used if no cache name is
32  * specified in the constructor.
33  * <p>
34  * Persist instances are not thread safe, in particular because
35  * {@link java.sql.Connection} objects are not thread safe.
36  * <p>
37  * Persist instances are created with the following defaults:
38  * <ul>
39  * <li> closePreparedStatementsAfterRead=true This will work for most reads
40  * (select queries) that do not return long-lasting objects, such as streams or
41  * LOB handlers. If a query returns InputStream, Reader, Blob or Clob objects,
42  * closePreparedStatementsAfterRead should be set to false, and closing the
43  * PreparedStatement must be controlled manually. This is because those
44  * datatypes stream data from database after the PreparedStatement execution.
45  * <li> updateAutoGeneratedKeys=false This means that objects that are inserted
46  * either using insert() or executeUpdate() (with autoGeneratedKeys option) will
47  * not have their primary keys automatically updated against the generated keys
48  * in the database. Please consult your JDBC driver support for querying
49  * auto-generated keys in {@link java.sql.PreparedStatement}
50  * <li> {@link DefaultNameGuesser} which will take names in the form
51  * CompoundName (for classes) or compoundName (for fields) and return a set of
52  * guessed names such as [compound_name, compound_names, compoundname,
53  * compoundnames].
54  * </ul>
55  *
56  * @see TableMapping
57  */

58 public final class Persist {
59
60     private Connection JavaDoc connection;
61     private boolean updateAutoGeneratedKeys = false;
62     private PreparedStatement JavaDoc lastPreparedStatement = null;
63     private boolean closePreparedStatementsAfterRead = true;
64
65     private static ConcurrentMap JavaDoc<String JavaDoc, ConcurrentMap JavaDoc<Class JavaDoc, Mapping>> mappingCaches = new ConcurrentHashMap JavaDoc();
66     private static ConcurrentMap JavaDoc<String JavaDoc, NameGuesser> nameGuessers = new ConcurrentHashMap JavaDoc();
67
68     private static final String JavaDoc DEFAULT_CACHE = "default cache";
69
70     private String JavaDoc cacheName = DEFAULT_CACHE;
71     private NameGuesser nameGuesser = null;
72
73     static {
74         mappingCaches.put(DEFAULT_CACHE, new ConcurrentHashMap JavaDoc());
75         nameGuessers.put(DEFAULT_CACHE, new DefaultNameGuesser());
76
77         if (Log.isDebugEnabled(Log.ENGINE)) {
78             Log.debug(Log.ENGINE, "Caches initialized");
79         }
80     }
81
82     // ---------- constructors ----------
83

84     /**
85      * Creates a Persist instance that will use the default cache for
86      * table-object mappings.
87      *
88      * @param connection {@link java.sql.Connection} object to be used
89      * @since 1.0
90      */

91     public Persist(Connection JavaDoc connection) {
92         this(DEFAULT_CACHE, connection);
93     }
94
95     /**
96      * Creates a Persist instance that will use the given cache name for
97      * table-object mappings.
98      *
99      * @param cacheName Name of the cache to be used
100      * @param connection {@link java.sql.Connection} object to be used
101      * @since 1.0
102      */

103     public Persist(String JavaDoc cacheName, Connection JavaDoc connection) {
104
105         if (cacheName == null) {
106             cacheName = DEFAULT_CACHE;
107         }
108
109         this.cacheName = cacheName;
110         this.connection = connection;
111
112         this.nameGuesser = nameGuessers.get(cacheName);
113         if (this.nameGuesser == null) {
114             // this block may execute more than once from different threads --
115
// not a problem, though
116
this.nameGuesser = new DefaultNameGuesser();
117             nameGuessers.put(cacheName, this.nameGuesser);
118         }
119
120         if (Log.isDebugEnabled(Log.ENGINE)) {
121             Log.debug(Log.ENGINE, "New instance for cache [" + cacheName + "] and connection [" + connection + "]");
122         }
123     }
124
125     // ---------- name guesser ----------
126

127     /**
128      * Sets the {@link NameGuesser} for a given mappings cache.
129      *
130      * @param cacheName Name of the cache to be used
131      * @param nameGuesser {@link NameGuesser} implementation
132      * @since 1.0
133      */

134     public static void setNameGuesser(final String JavaDoc cacheName, final NameGuesser nameGuesser) {
135         nameGuessers.put(cacheName, nameGuesser);
136
137         // purge mappings cache so that name mappings are coherent
138
mappingCaches.put(cacheName, new ConcurrentHashMap JavaDoc());
139
140         if (Log.isDebugEnabled(Log.ENGINE)) {
141             Log.debug(Log.ENGINE, "Name guesser set for cache [" + cacheName + "]");
142         }
143     }
144
145     /**
146      * Sets the name guesser for the default mappings cache.
147      *
148      * @param nameGuesser {@link NameGuesser} implementation
149      * @since 1.0
150      */

151     public static void setNameGuesser(final NameGuesser nameGuesser) {
152         nameGuessers.put(DEFAULT_CACHE, nameGuesser);
153     }
154
155     // ---------- autoUpdateGeneratedKeys getter/setter ----------
156

157     /**
158      * Sets the behavior for updating auto-generated keys.
159      *
160      * @param updateAutoGeneratedKeys if set to true, auto-generated keys will
161      * be updated after the execution of insert or executeUpdate operations that
162      * may trigger auto-generation of keys in the database
163      * @since 1.0
164      */

165     public void setUpdateAutoGeneratedKeys(final boolean updateAutoGeneratedKeys) {
166         this.updateAutoGeneratedKeys = updateAutoGeneratedKeys;
167
168         if (Log.isDebugEnabled(Log.ENGINE)) {
169             Log.debug(Log.ENGINE, "setUpdateAutoGeneratedKeys(" + updateAutoGeneratedKeys + ")");
170         }
171     }
172
173     /**
174      * Returns true if updating auto-generated keys is enabled.
175      */

176     public boolean isUpdateAutoGeneratedKeys() {
177         return updateAutoGeneratedKeys;
178     }
179
180     // ---------- mappings cache ----------
181

182     /**
183      * Returns the mapping for the given object class.
184      *
185      * @param objectClass {@link java.lang.Class} object to get a
186      * {@link TableMapping} for
187      * @since 1.0
188      */

189     public Mapping getMapping(final Class JavaDoc objectClass) {
190
191         if (cacheName == null) {
192             cacheName = DEFAULT_CACHE;
193         }
194
195         if (!mappingCaches.containsKey(cacheName)) {
196             // more than one map may end up being inserted here for the same
197
// cacheName, but this is not problematic
198
mappingCaches.put(cacheName, new ConcurrentHashMap JavaDoc());
199         }
200
201         final ConcurrentMap JavaDoc<Class JavaDoc, Mapping> mappingCache = mappingCaches.get(cacheName);
202
203         if (!mappingCache.containsKey(objectClass)) {
204             try {
205                 // more than one map may end up being inserted here for the same
206
// objectClass, but this is not
207
// problematic
208
mappingCache.put(objectClass, Mapping.getMapping(connection.getMetaData(), objectClass, nameGuesser));
209
210                 if (Log.isDebugEnabled(Log.ENGINE)) {
211                     Log.debug(Log.ENGINE, "Cached mapping for [" + objectClass.getCanonicalName() + "]");
212                 }
213             } catch (SQLException JavaDoc e) {
214                 throw new RuntimeSQLException(e);
215             }
216         }
217
218         return mappingCache.get(objectClass);
219     }
220
221     /**
222      * Utility method that will get a TableMapping for a given class. If the
223      * mapping for the class is not a TableMapping, will throw an exception
224      * specifying the given calling method name.
225      */

226     private TableMapping getTableMapping(Class JavaDoc objectClass, String JavaDoc callingMethodName) {
227         final Mapping mapping = getMapping(objectClass);
228         if (!(mapping instanceof TableMapping)) {
229             throw new RuntimeSQLException("Class [" + objectClass.getCanonicalName()
230                     + "] has a @NoTable annotation defined, therefore " + callingMethodName + " can't work with it. "
231                     + "If this class is supposed to be mapped to a table, @NoTable should not be used.");
232         }
233         return (TableMapping) mapping;
234     }
235
236     // ---------- connection ----------
237

238     /**
239      * Returns the {@link java.sql.Connection Connection} associated with this
240      * Persist instance.
241      * @since 1.0
242      */

243     public Connection JavaDoc getConnection() {
244         return connection;
245     }
246
247     /**
248      * Commits the {@link java.sql.Connection Connection} associated with this
249      * Persist instance.
250      *
251      * @see java.sql.Connection#commit()
252      * @since 1.0
253      */

254     public void commit() {
255         try {
256             connection.commit();
257
258             if (Log.isDebugEnabled(Log.ENGINE)) {
259                 Log.debug(Log.ENGINE, "Connection commited");
260             }
261         } catch (SQLException JavaDoc e) {
262             throw new RuntimeSQLException(e);
263         }
264     }
265
266     /**
267      * Rolls back the {@link java.sql.Connection Connection} associated with
268      * this Persist instance.
269      *
270      * @see java.sql.Connection#rollback()
271      * @since 1.0
272      */

273     public void rollback() {
274         try {
275             connection.rollback();
276
277             if (Log.isDebugEnabled(Log.ENGINE)) {
278                 Log.debug(Log.ENGINE, "Connection rolled back");
279             }
280         } catch (SQLException JavaDoc e) {
281             throw new RuntimeSQLException(e);
282         }
283     }
284
285     /**
286      * Sets the auto commit behavior for the
287      * {@link java.sql.Connection Connection} associated with this Persist
288      * instance.
289      * @see java.sql.Connection#setAutoCommit(boolean)
290      * @since 1.0
291      */

292     public void setAutoCommit(final boolean autoCommit) {
293         try {
294             connection.setAutoCommit(autoCommit);
295
296             if (Log.isDebugEnabled(Log.ENGINE)) {
297                 Log.debug(Log.ENGINE, "Connection setAutoCommit(" + autoCommit + ")");
298             }
299         } catch (SQLException JavaDoc e) {
300             throw new RuntimeSQLException(e);
301         }
302     }
303
304     // ---------- prepared statement ----------
305

306     /**
307      * Creates a {@link java.sql.PreparedStatement}, setting the names of the
308      * auto-generated keys to be retrieved.
309      *
310      * @param sql SQL statement to create the {@link java.sql.PreparedStatement}
311      * from
312      * @param autoGeneratedKeys names of the columns that will have
313      * auto-generated values produced during the execution of the
314      * {@link java.sql.PreparedStatement}
315      * @since 1.0
316      */

317     public PreparedStatement JavaDoc getPreparedStatement(final String JavaDoc sql, final String JavaDoc[] autoGeneratedKeys) {
318         try {
319             if (autoGeneratedKeys == null || autoGeneratedKeys.length == 0) {
320                 lastPreparedStatement = getPreparedStatement(sql);
321             } else {
322                 lastPreparedStatement = connection.prepareStatement(sql, autoGeneratedKeys);
323             }
324         } catch (SQLException JavaDoc e) {
325             throw new RuntimeSQLException("Error creating prepared statement for sql [" + sql
326                     + "] with autoGeneratedKeys " + Arrays.toString(autoGeneratedKeys) + ": " + e.getMessage(), e);
327         }
328
329         if (Log.isDebugEnabled(Log.ENGINE)) {
330             Log.debug(Log.ENGINE, "Generated PreparedStatement [" + lastPreparedStatement + "] for [" + sql
331                     + "] using autoGeneratedKeys " + Arrays.toString(autoGeneratedKeys));
332         }
333
334         return lastPreparedStatement;
335     }
336
337     /**
338      * Creates a {@link java.sql.PreparedStatement} with no parameters.
339      *
340      * @param sql SQL statement to create the {@link java.sql.PreparedStatement}
341      * from
342      * @since 1.0
343      */

344     public PreparedStatement JavaDoc getPreparedStatement(final String JavaDoc sql) {
345
346         try {
347             lastPreparedStatement = connection.prepareStatement(sql);
348         } catch (SQLException JavaDoc e) {
349             throw new RuntimeSQLException("Error creating prepared statement for sql [" + sql + "]: " + e.getMessage(),
350                     e);
351         }
352
353         if (Log.isDebugEnabled(Log.ENGINE)) {
354             Log.debug(Log.ENGINE, "Generated PreparedStatement [" + lastPreparedStatement + "] for [" + sql + "]");
355         }
356
357         return lastPreparedStatement;
358     }
359
360     /**
361      * Closes a {@link java.sql.PreparedStatement}.
362      *
363      * @param statement {@link java.sql.PreparedStatement} to be closed
364      * @see java.sql.PreparedStatement#close()
365      * @since 1.0
366      */

367     public void closePreparedStatement(final PreparedStatement JavaDoc statement) {
368         try {
369             statement.close();
370         } catch (SQLException JavaDoc e) {
371             throw new RuntimeSQLException("Error closing prepared statement: " + e.getMessage(), e);
372         }
373
374         if (Log.isDebugEnabled(Log.ENGINE)) {
375             Log.debug(Log.ENGINE, "Closed PreparedStatement [" + statement + "]");
376         }
377
378         lastPreparedStatement = null;
379     }
380
381     /**
382      * Returns the last {@link java.sql.PreparedStatement} used by the engine.
383      *
384      * @since 1.0
385      */

386     public PreparedStatement JavaDoc getLastPreparedStatement() {
387         return lastPreparedStatement;
388     }
389
390     /**
391      * Closes the last {@link java.sql.PreparedStatement} used by the engine.
392      *
393      * @see java.sql.PreparedStatement#close()
394      * @since 1.0
395      */

396     public void closeLastPreparedStatement() {
397         closePreparedStatement(lastPreparedStatement);
398     }
399
400     /**
401      * Sets the behavior for closing {@link java.sql.PreparedStatement}
402      * instances after execution. This will only affect reads, since any update
403      * operations (insert, delete, update) will always have their
404      * {@link java.sql.PreparedStatement} instances automatically closed.
405      * <p>
406      * If a query returns InputStream, Reader, Blob or Clob objects, this should
407      * be set to false, and closing the PreparedStatement must be controlled
408      * manually. This is because those datatypes stream data from database after
409      * the PreparedStatement execution.
410      *
411      * @param closePreparedStatementsAfterRead if true,
412      * {@link java.sql.PreparedStatement} instances for read queries will be
413      * automatically closed
414      * @since 1.0
415      */

416     public void setClosePreparedStatementsAfterRead(final boolean closePreparedStatementsAfterRead) {
417         this.closePreparedStatementsAfterRead = closePreparedStatementsAfterRead;
418
419         if (Log.isDebugEnabled(Log.ENGINE)) {
420             Log.debug(Log.ENGINE, "setClosePreparedStatementsAfterRead(" + closePreparedStatementsAfterRead + ")");
421         }
422     }
423
424     /**
425      * Returns true if {@link java.sql.PreparedStatement} instances are
426      * automatically closed after read (select or otherwise) queries.
427      *
428      * @since 1.0
429      */

430     public boolean isClosePreparedStatementsAfterRead() {
431         return this.closePreparedStatementsAfterRead;
432     }
433
434     // ---------- mappers ----------
435

436     /**
437      * Sets parameters in the given prepared statement.
438      * <p>
439      * Parameters will be set using PreparedStatement set methods related with
440      * the Java types of the parameters, according with the following table:
441      * <ul>
442      * <li> Boolean/boolean: setBoolean
443      * <li> Byte/byte: setByte
444      * <li> Short/short: setShort
445      * <li> Integer/integer: setInt
446      * <li> Long/long: setLong
447      * <li> Float/float: setFloat
448      * <li> Double/double: setDouble
449      * <li> Character/char: setString
450      * <li> Character[]/char[]: setString
451      * <li> Byte[]/byte[]: setBytes
452      * <li> String: setString
453      * <li> java.math.BigDecimal: setBigDecimal
454      * <li> java.io.Reader: setCharacterStream
455      * <li> java.io.InputStream: setBinaryStream
456      * <li> java.util.Date: setTimestamp
457      * <li> java.sql.Date: setDate
458      * <li> java.sql.Time: setTime
459      * <li> java.sql.Timestamp: setTimestamp
460      * <li> java.sql.Clob : setClob
461      * <li> java.sql.Blob: setBlob
462      * </ul>
463      *
464      * @param stmt {@link java.sql.PreparedStatement} to have parameters set
465      * into
466      * @param parameters varargs or Object[] with parameters values
467      * @throws RuntimeSQLException if a database access error occurs or this
468      * method is called on a closed PreparedStatement; if a parameter type does
469      * not have a matching set method (as outlined above)
470      * @throws RuntimeIOException if an error occurs while reading data from a
471      * Reader or InputStream parameter
472      * @since 1.0
473      */

474     public static void setParameters(final PreparedStatement JavaDoc stmt, final Object JavaDoc[] parameters) {
475
476         // if no parameters, do nothing
477
if (parameters == null || parameters.length == 0) {
478             return;
479         }
480
481         ParameterMetaData JavaDoc stmtMetaData = null;
482
483         for (int i = 1; i <= parameters.length; i++) {
484
485             final Object JavaDoc parameter = parameters[i - 1];
486
487             if (parameter == null) {
488
489                 // lazy assignment of stmtMetaData
490
if (stmtMetaData == null) {
491                     try {
492                         stmtMetaData = stmt.getParameterMetaData();
493                     } catch (SQLException JavaDoc e) {
494                         throw new RuntimeException JavaDoc(e);
495                     }
496                 }
497
498                 // get sql type from prepared statement metadata
499
int sqlType;
500                 try {
501                     sqlType = stmtMetaData.getParameterType(i);
502                 } catch (SQLException JavaDoc e2) {
503                     // feature not supported, use NULL
504
sqlType = java.sql.Types.NULL;
505                 }
506
507                 try {
508                     stmt.setNull(i, sqlType);
509                 } catch (SQLException JavaDoc e) {
510                     throw new RuntimeSQLException("Could not set null into parameter [" + i
511                             + "] using java.sql.Types [" + Log.sqlTypeToString(sqlType) + "] " + e.getMessage(), e);
512                 }
513
514                 if (Log.isDebugEnabled(Log.PARAMETERS)) {
515                     Log.debug(Log.PARAMETERS, "Parameter [" + i + "] from PreparedStatement [" + stmt
516                             + "] set to [null] using java.sql.Types [" + Log.sqlTypeToString(sqlType) + "]");
517                 }
518
519                 continue;
520             }
521
522             try {
523
524                 final Class JavaDoc type = parameter.getClass();
525
526                 if (type == Boolean JavaDoc.class || type == boolean.class) {
527                     stmt.setBoolean(i, (Boolean JavaDoc) parameter);
528                 } else if (type == Byte JavaDoc.class || type == byte.class) {
529                     stmt.setByte(i, (Byte JavaDoc) parameter);
530                 } else if (type == Short JavaDoc.class || type == short.class) {
531                     stmt.setShort(i, (Short JavaDoc) parameter);
532                 } else if (type == Integer JavaDoc.class || type == int.class) {
533                     stmt.setInt(i, (Integer JavaDoc) parameter);
534                 } else if (type == Long JavaDoc.class || type == long.class) {
535                     stmt.setLong(i, (Long JavaDoc) parameter);
536                 } else if (type == Float JavaDoc.class || type == float.class) {
537                     stmt.setFloat(i, (Float JavaDoc) parameter);
538                 } else if (type == Double JavaDoc.class || type == double.class) {
539                     stmt.setDouble(i, (Double JavaDoc) parameter);
540                 } else if (type == Character JavaDoc.class || type == char.class) {
541                     stmt.setString(i, parameter == null ? null : "" + (Character JavaDoc) parameter);
542                 } else if (type == char[].class) {
543                     // not efficient, will create a new String object
544
stmt.setString(i, parameter == null ? null : new String JavaDoc((char[]) parameter));
545                 } else if (type == Character JavaDoc[].class) {
546                     // not efficient, will duplicate the array and create a new String object
547
final Character JavaDoc[] src = (Character JavaDoc[]) parameter;
548                     final char[] dst = new char[src.length];
549                     for (int j = 0; j < src.length; j++) { // can't use System.arraycopy here
550
dst[j] = src[j];
551                     }
552                     stmt.setString(i, new String JavaDoc(dst));
553                 } else if (type == String JavaDoc.class) {
554                     stmt.setString(i, (String JavaDoc) parameter);
555                 } else if (type == BigDecimal JavaDoc.class) {
556                     stmt.setBigDecimal(i, (BigDecimal JavaDoc) parameter);
557                 } else if (type == byte[].class) {
558                     stmt.setBytes(i, (byte[]) parameter);
559                 } else if (type == Byte JavaDoc[].class) {
560                     // not efficient, will duplicate the array
561
final Byte JavaDoc[] src = (Byte JavaDoc[]) parameter;
562                     final byte[] dst = new byte[src.length];
563                     for (int j = 0; j < src.length; j++) { // can't use System.arraycopy here
564
dst[j] = src[j];
565                     }
566                     stmt.setBytes(i, dst);
567                 } else if (parameter instanceof java.io.Reader JavaDoc) {
568                     final java.io.Reader JavaDoc reader = (java.io.Reader JavaDoc) parameter;
569
570                     // the jdbc api for setCharacterStream requires the number
571
// of characters to be read so this will end up reading
572
// data twice (here and inside the jdbc driver)
573
// besides, the reader must support reset()
574
int size = 0;
575                     try {
576                         reader.reset();
577                         while (reader.read() != -1) {
578                             size++;
579                         }
580                         reader.reset();
581                     } catch (IOException JavaDoc e) {
582                         throw new RuntimeIOException(e);
583                     }
584                     stmt.setCharacterStream(i, reader, size);
585                 } else if (parameter instanceof java.io.InputStream JavaDoc) {
586                     final java.io.InputStream JavaDoc inputStream = (java.io.InputStream JavaDoc) parameter;
587
588                     // the jdbc api for setBinaryStream requires the number of
589
// bytes to be read so this will end up reading the stream
590
// twice (here and inside the jdbc driver)
591
// besides, the stream must support reset()
592
int size = 0;
593                     try {
594                         inputStream.reset();
595                         while (inputStream.read() != -1) {
596                             size++;
597                         }
598                         inputStream.reset();
599                     } catch (IOException JavaDoc e) {
600                         throw new RuntimeIOException(e);
601                     }
602                     stmt.setBinaryStream(i, inputStream, size);
603                 } else if (parameter instanceof Clob JavaDoc) {
604                     stmt.setClob(i, (Clob JavaDoc) parameter);
605                 } else if (parameter instanceof Blob JavaDoc) {
606                     stmt.setBlob(i, (Blob JavaDoc) parameter);
607                 } else if (type == java.util.Date JavaDoc.class) {
608                     final java.util.Date JavaDoc date = (java.util.Date JavaDoc) parameter;
609                     stmt.setTimestamp(i, new java.sql.Timestamp JavaDoc(date.getTime()));
610                 } else if (type == java.sql.Date JavaDoc.class) {
611                     stmt.setDate(i, (java.sql.Date JavaDoc) parameter);
612                 } else if (type == java.sql.Time JavaDoc.class) {
613                     stmt.setTime(i, (java.sql.Time JavaDoc) parameter);
614                 } else if (type == java.sql.Timestamp JavaDoc.class) {
615                     stmt.setTimestamp(i, (java.sql.Timestamp JavaDoc) parameter);
616                 } else {
617                     // last resort; this should cover all database-specific
618
// object types
619
stmt.setObject(i, parameter);
620                 }
621
622                 if (Log.isDebugEnabled(Log.PARAMETERS)) {
623                     Log.debug(Log.PARAMETERS, "PreparedStatement [" + stmt + "] Parameter [" + i + "] type ["
624                             + type.getSimpleName() + "] set to [" + Log.objectToString(parameter) + "]");
625                 }
626
627             } catch (SQLException JavaDoc e) {
628                 throw new RuntimeSQLException(e);
629             }
630         }
631     }
632
633     /**
634      * Returns true if the provided class is a type supported natively (as
635      * opposed to a bean).
636      *
637      * @param type {@link java.lang.Class} type to be tested
638      * @since 1.0
639      */

640     private static boolean isNativeType(final Class JavaDoc type) {
641
642         // to return an arbitrary object use Object.class
643

644         return (type == boolean.class || type == Boolean JavaDoc.class || type == byte.class || type == Byte JavaDoc.class
645                 || type == short.class || type == Short JavaDoc.class || type == int.class || type == Integer JavaDoc.class
646                 || type == long.class || type == Long JavaDoc.class || type == float.class || type == Float JavaDoc.class
647                 || type == double.class || type == Double JavaDoc.class || type == char.class || type == Character JavaDoc.class
648                 || type == byte[].class || type == Byte JavaDoc[].class || type == char[].class || type == Character JavaDoc[].class
649                 || type == String JavaDoc.class || type == BigDecimal JavaDoc.class || type == java.util.Date JavaDoc.class
650                 || type == java.sql.Date JavaDoc.class || type == java.sql.Time JavaDoc.class || type == java.sql.Timestamp JavaDoc.class
651                 || type == java.io.InputStream JavaDoc.class || type == java.io.Reader JavaDoc.class || type == java.sql.Clob JavaDoc.class
652                 || type == java.sql.Blob JavaDoc.class || type == Object JavaDoc.class);
653     }
654
655     /**
656      * Reads a column from the current row in the provided
657      * {@link java.sql.ResultSet} and returns an instance of the specified Java
658      * {@link java.lang.Class} containing the values read.
659      * <p>
660      * This method is used while converting {@link java.sql.ResultSet} rows to
661      * objects. The class type is the field type in the target bean.
662      * <p>
663      * Correspondence between class types and ResultSet.get methods is as
664      * follows:
665      * <ul>
666      * <li> Boolean/boolean: getBoolean
667      * <li> Byte/byte: getByte
668      * <li> Short/short: getShort
669      * <li> Integer/int: getInt
670      * <li> Long/long: getLong
671      * <li> Float/float: getFloat
672      * <li> Double/double: getDouble
673      * <li> Character/char: getString
674      * <li> Character[]/char[]: getString
675      * <li> Byte[]/byte[]: setBytes
676      * <li> String: setString
677      * <li> java.math.BigDecimal: getBigDecimal
678      * <li> java.io.Reader: getCharacterStream
679      * <li> java.io.InputStream: getBinaryStream
680      * <li> java.util.Date: getTimestamp
681      * <li> java.sql.Date: getDate
682      * <li> java.sql.Time: getTime
683      * <li> java.sql.Timestamp: getTimestamp
684      * <li> java.sql.Clob: getClob
685      * <li> java.sql.Blob: getBlob
686      * </ul>
687      * <p>
688      * null's will be respected for any non-native types. This means that if a
689      * field is of type Integer it will be able to receive a null value from the
690      * ResultSet; on the other hand, if a field is of type int it will receive 0
691      * for a null value from the {@link java.sql.ResultSet}.
692      *
693      * @param resultSet {@link java.sql.ResultSet} (positioned in the row to be
694      * processed)
695      * @param column column index in the result set (starting with 1)
696      * @param type {@link java.lang.Class} of the object to be returned
697      * @since 1.0
698      */

699     public static Object JavaDoc getValueFromResultSet(final ResultSet JavaDoc resultSet, final int column, final Class JavaDoc type) {
700
701         Object JavaDoc value = null;
702
703         try {
704
705             if (type == boolean.class) {
706                 value = resultSet.getBoolean(column);
707             } else if (type == Boolean JavaDoc.class) {
708                 value = resultSet.getObject(column) == null ? null : resultSet.getBoolean(column);
709             } else if (type == byte.class) {
710                 value = resultSet.getByte(column);
711             } else if (type == Byte JavaDoc.class) {
712                 value = resultSet.getObject(column) == null ? null : resultSet.getByte(column);
713             } else if (type == short.class) {
714                 value = resultSet.getShort(column);
715             } else if (type == Short JavaDoc.class) {
716                 value = resultSet.getObject(column) == null ? null : resultSet.getShort(column);
717             } else if (type == int.class) {
718                 value = resultSet.getInt(column);
719             } else if (type == Integer JavaDoc.class) {
720                 value = resultSet.getObject(column) == null ? null : resultSet.getInt(column);
721             } else if (type == long.class) {
722                 value = resultSet.getLong(column);
723             } else if (type == Long JavaDoc.class) {
724                 value = resultSet.getObject(column) == null ? null : resultSet.getLong(column);
725             } else if (type == float.class) {
726                 value = resultSet.getFloat(column);
727             } else if (type == Float JavaDoc.class) {
728                 value = resultSet.getObject(column) == null ? null : resultSet.getFloat(column);
729             } else if (type == double.class) {
730                 value = resultSet.getDouble(column);
731             } else if (type == Double JavaDoc.class) {
732                 value = resultSet.getObject(column) == null ? null : resultSet.getDouble(column);
733             } else if (type == BigDecimal JavaDoc.class) {
734                 value = resultSet.getObject(column) == null ? null : resultSet.getBigDecimal(column);
735             } else if (type == String JavaDoc.class) {
736                 value = resultSet.getString(column);
737             } else if (type == Character JavaDoc.class || type == char.class) {
738                 final String JavaDoc str = resultSet.getString(column);
739                 if (str != null && str.length() > 1) {
740                     throw new RuntimeSQLException("Column [" + column + "] returned a string with length ["
741                             + str.length() + "] but field type [" + type.getSimpleName()
742                             + "] can only accept 1 character");
743                 }
744                 value = (str == null || str.length() == 0) ? null : str.charAt(0);
745             } else if (type == byte[].class || type == Byte JavaDoc[].class) {
746                 value = resultSet.getBytes(column);
747             } else if (type == char[].class || type == Character JavaDoc[].class) {
748                 final String JavaDoc str = resultSet.getString(column);
749                 value = (str == null) ? null : str.toCharArray();
750             } else if (type == java.util.Date JavaDoc.class) {
751                 final java.sql.Timestamp JavaDoc timestamp = resultSet.getTimestamp(column);
752                 value = (timestamp == null) ? null : new java.util.Date JavaDoc(timestamp.getTime());
753             } else if (type == java.sql.Date JavaDoc.class) {
754                 value = resultSet.getDate(column);
755             } else if (type == java.sql.Time JavaDoc.class) {
756                 value = resultSet.getTime(column);
757             } else if (type == java.sql.Timestamp JavaDoc.class) {
758                 value = resultSet.getTimestamp(column);
759             } else if (type == java.io.InputStream JavaDoc.class) {
760                 value = resultSet.getBinaryStream(column);
761             } else if (type == java.io.Reader JavaDoc.class) {
762                 value = resultSet.getCharacterStream(column);
763             } else if (type == java.sql.Clob JavaDoc.class) {
764                 value = resultSet.getClob(column);
765             } else if (type == java.sql.Blob JavaDoc.class) {
766                 value = resultSet.getBlob(column);
767             } else {
768                 // this will work for database-specific types
769
value = resultSet.getObject(column);
770             }
771
772         } catch (SQLException JavaDoc e) {
773             throw new RuntimeSQLException(e);
774         }
775
776         if (Log.isDebugEnabled(Log.RESULTS)) {
777             Log.debug(Log.RESULTS, "Read ResultSet [" + resultSet + "] column [" + column + "]"
778                     + (value == null ? "" : " type [" + value.getClass().getSimpleName() + "]") + " value ["
779                     + Log.objectToString(value) + "]");
780         }
781
782         return value;
783     }
784
785     /**
786      * Reads a column from the current row in the provided
787      * {@link java.sql.ResultSet} and return a value correspondent to the SQL
788      * type provided (as defined in {@link java.sql.Types java.sql.Types}).
789      * <p>
790      * This method is used while converting results sets to maps. The SQL type
791      * comes from the {@link java.sql.ResultSetMetaData ResultSetMetaData} for a
792      * given column.
793      * <p>
794      * Correspondence between {@link java.sql.Types java.sql.Types} and
795      * ResultSet.get methods is as follows:
796      * <ul>
797      * <li> ARRAY: getArray
798      * <li> BIGINT: getLong
799      * <li> BIT: getBoolean
800      * <li> BLOB: getBytes
801      * <li> BOOLEAN: getBoolean
802      * <li> CHAR: getString
803      * <li> CLOB: getString
804      * <li> DATALINK: getBinaryStream
805      * <li> DATE: getDate
806      * <li> DECIMAL: getBigDecimal
807      * <li> DOUBLE: getDouble
808      * <li> FLOAT: getFloat
809      * <li> INTEGER: getInt
810      * <li> JAVA_OBJECT: getObject
811      * <li> LONGVARBINARY: getBytes
812      * <li> LONGVARCHAR: getString
813      * <li> NULL: getNull
814      * <li> NCHAR: getString
815      * <li> NUMERIC: getBigDecimal
816      * <li> OTHER: getObject
817      * <li> REAL: getDouble
818      * <li> REF: getRef
819      * <li> SMALLINT: getInt
820      * <li> TIME: getTime
821      * <li> TIMESTAMP: getTimestamp
822      * <li> TINYINT: getInt
823      * <li> VARBINARY: getBytes
824      * <li> VARCHAR: getString
825      * <li> [Oracle specific] 100: getFloat
826      * <li> [Oracle specific] 101: getDouble
827      * </ul>
828      * <p>
829      * null's are respected for all types. This means that if a column is of
830      * type LONG and its value comes from the database as null, this method will
831      * return null for it.
832      *
833      * @param resultSet {@link java.sql.ResultSet} (positioned in the row to be
834      * processed)
835      * @param column Column index in the result set (starting with 1)
836      * @param type type of the column (as defined in
837      * {@link java.sql.Types java.sql.Types})
838      * @since 1.0
839      */

840     public static Object JavaDoc getValueFromResultSet(final ResultSet JavaDoc resultSet, final int column, final int type) {
841
842         Object JavaDoc value = null;
843
844         try {
845
846             if (type == java.sql.Types.ARRAY) {
847                 value = resultSet.getArray(column);
848             } else if (type == java.sql.Types.BIGINT) {
849                 value = resultSet.getObject(column) == null ? null : resultSet.getLong(column);
850             } else if (type == java.sql.Types.BINARY) {
851                 value = resultSet.getBytes(column);
852             } else if (type == java.sql.Types.BIT) {
853                 value = resultSet.getObject(column) == null ? null : resultSet.getBoolean(column);
854             } else if (type == java.sql.Types.BLOB) {
855                 value = resultSet.getBytes(column);
856             } else if (type == java.sql.Types.BOOLEAN) {
857                 value = resultSet.getObject(column) == null ? null : resultSet.getBoolean(column);
858             } else if (type == java.sql.Types.CHAR) {
859                 value = resultSet.getString(column);
860             } else if (type == java.sql.Types.CLOB) {
861                 value = resultSet.getString(column);
862             } else if (type == java.sql.Types.DATALINK) {
863                 value = resultSet.getBinaryStream(column);
864             } else if (type == java.sql.Types.DATE) {
865                 value = resultSet.getDate(column);
866             } else if (type == java.sql.Types.DECIMAL) {
867                 value = resultSet.getBigDecimal(column);
868             } else if (type == java.sql.Types.DOUBLE) {
869                 value = resultSet.getObject(column) == null ? null : resultSet.getDouble(column);
870             } else if (type == java.sql.Types.FLOAT) {
871                 value = resultSet.getObject(column) == null ? null : resultSet.getFloat(column);
872             } else if (type == java.sql.Types.INTEGER) {
873                 value = resultSet.getObject(column) == null ? null : resultSet.getInt(column);
874             } else if (type == java.sql.Types.JAVA_OBJECT) {
875                 value = resultSet.getObject(column);
876             } else if (type == java.sql.Types.LONGVARBINARY) {
877                 value = resultSet.getBytes(column);
878             } else if (type == java.sql.Types.LONGVARCHAR) {
879                 value = resultSet.getString(column);
880             } else if (type == java.sql.Types.NULL) {
881                 value = null;
882             } else if (type == java.sql.Types.NUMERIC) {
883                 value = resultSet.getBigDecimal(column);
884             } else if (type == java.sql.Types.OTHER) {
885                 value = resultSet.getObject(column);
886             } else if (type == java.sql.Types.REAL) {
887                 value = resultSet.getObject(column) == null ? null : resultSet.getDouble(column);
888             } else if (type == java.sql.Types.REF) {
889                 value = resultSet.getRef(column);
890             } else if (type == java.sql.Types.SMALLINT) {
891                 value = resultSet.getObject(column) == null ? null : resultSet.getInt(column);
892             } else if (type == java.sql.Types.TIME) {
893                 value = resultSet.getTime(column);
894             } else if (type == java.sql.Types.TIMESTAMP) {
895                 value = resultSet.getTimestamp(column);
896             } else if (type == java.sql.Types.TINYINT) {
897                 value = resultSet.getObject(column) == null ? null : resultSet.getInt(column);
898             } else if (type == java.sql.Types.VARBINARY) {
899                 value = resultSet.getBytes(column);
900             } else if (type == java.sql.Types.VARCHAR) {
901                 value = resultSet.getString(column);
902             }
903
904             // oracle specific
905
else if (type == 100) {
906                 value = resultSet.getObject(column) == null ? null : resultSet.getFloat(column);
907             } else if (type == 101) {
908                 value = resultSet.getObject(column) == null ? null : resultSet.getDouble(column);
909             }
910
911             else {
912                 throw new RuntimeSQLException("Could not get value for result set using type ["
913                         + Log.sqlTypeToString(type) + "] on column [" + column + "]");
914             }
915
916         } catch (SQLException JavaDoc e) {
917             throw new RuntimeSQLException(e);
918         }
919
920         if (Log.isDebugEnabled(Log.RESULTS)) {
921             Log.debug(Log.RESULTS, "Read ResultSet [" + resultSet + "] column [" + column + "] sql type ["
922                     + Log.sqlTypeToString(type) + "]"
923                     + (value == null ? "" : " type [" + value.getClass().getSimpleName() + "]") + " value ["
924                     + Log.objectToString(value) + "]");
925         }
926
927         return value;
928     }
929
930     /**
931      * Returns a list of values for the fields in the provided object that match
932      * the provided list of columns according with the object mapping.
933      *
934      * @param object source object to obtain parameter values
935      * @param columns name of the database columns to get parameters for
936      * @param mapping mapping for the object class
937      * @since 1.0
938      */

939     private static Object JavaDoc[] getParametersFromObject(final Object JavaDoc object, final String JavaDoc[] columns,
940             final TableMapping mapping) {
941
942         Object JavaDoc[] parameters = new Object JavaDoc[columns.length];
943         for (int i = 0; i < columns.length; i++) {
944             final String JavaDoc columnName = columns[i];
945             final Method JavaDoc getter = mapping.getGetterForColumn(columnName);
946
947             Object JavaDoc value = null;
948             try {
949                 value = getter.invoke(object, new Object JavaDoc[] {});
950             } catch (Exception JavaDoc e) {
951                 throw new RuntimeSQLException("Could not access getter for column [" + columnName + "]", e);
952             }
953
954             parameters[i] = value;
955         }
956
957         return parameters;
958     }
959
960     /**
961      * Reads a row from the provided {@link java.sql.ResultSet} and converts it
962      * to an object instance of the given class.
963      * <p>
964      * See {@link #getValueFromResultSet(ResultSet, int, Class)} for details on
965      * the mappings between ResultSet.get methods and Java types.
966      *
967      * @param objectClass type of the object to be returned
968      * @param resultSet {@link java.sql.ResultSet} (positioned in the row to be
969      * processed)
970      * @see #isNativeType(Class)
971      * @see #getValueFromResultSet(ResultSet, int, Class)
972      * @since 1.0
973      */

974     public Object JavaDoc loadObject(final Class JavaDoc objectClass, final ResultSet JavaDoc resultSet) throws SQLException JavaDoc {
975
976         final ResultSetMetaData JavaDoc resultSetMetaData = resultSet.getMetaData();
977
978         Object JavaDoc ret = null;
979
980         // for native objects (int, long, String, Date, etc.)
981
if (isNativeType(objectClass)) {
982             if (resultSetMetaData.getColumnCount() != 1) {
983                 throw new RuntimeSQLException("ResultSet returned [" + resultSetMetaData.getColumnCount()
984                         + "] columns but 1 column was expected to load data into an instance of ["
985                         + objectClass.getName() + "]");
986             }
987             ret = getValueFromResultSet(resultSet, 1, objectClass);
988         }
989
990         // for beans
991
else {
992
993             final Mapping mapping = getMapping(objectClass);
994
995             try {
996                 ret = objectClass.newInstance();
997             } catch (Exception JavaDoc e) {
998                 throw new RuntimeSQLException(e);
999             }
1000
1001            for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
1002                final String JavaDoc columnName = resultSetMetaData.getColumnName(i).toLowerCase();
1003                final Method JavaDoc setter = mapping.getSetterForColumn(columnName);
1004                if (setter == null) {
1005                    throw new RuntimeSQLException("Column [" + columnName
1006                            + "] from result set does not have a mapping to a field in ["
1007                            + objectClass.getName() + "]");
1008                }
1009
1010                final Class JavaDoc type = setter.getParameterTypes()[0];
1011                final Object JavaDoc value = getValueFromResultSet(resultSet, i, type);
1012
1013                try {
1014                    setter.invoke(ret, new Object JavaDoc[] { value });
1015                } catch (Exception JavaDoc e) {
1016                    throw new RuntimeSQLException("Error setting value [" + value + "]"
1017                            + (value == null ? "" : " of type [" + value.getClass().getName() + "]") + " from column ["
1018                            + columnName + "] using setter [" + setter + "]: " + e.getMessage(), e);
1019                }
1020            }
1021
1022        }
1023
1024        return ret;
1025    }
1026
1027    /**
1028     * Reads a row from the provided {@link java.sql.ResultSet} and converts it
1029     * to a map having the column names as keys and results as values.
1030     * <p>
1031     * See {@link #getValueFromResultSet(ResultSet, int, int)} for details on
1032     * the mappings between ResultSet.get methods and
1033     * {@link java.sql.Types java.sql.Types} types
1034     *
1035     * @param resultSet {@link java.sql.ResultSet} (positioned in the row to be
1036     * processed)
1037     * @since 1.0
1038     */

1039    public static Map JavaDoc<String JavaDoc, Object JavaDoc> loadMap(final ResultSet JavaDoc resultSet) throws SQLException JavaDoc {
1040
1041        final Map JavaDoc ret = new LinkedHashMap JavaDoc();
1042        final ResultSetMetaData JavaDoc resultSetMetaData = resultSet.getMetaData();
1043
1044        for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
1045            final String JavaDoc columnName = resultSetMetaData.getColumnName(i).toLowerCase();
1046            final int type = resultSetMetaData.getColumnType(i);
1047            final Object JavaDoc value = getValueFromResultSet(resultSet, i, type);
1048            ret.put(columnName, value);
1049        }
1050
1051        return ret;
1052    }
1053
1054    /**
1055     * Set auto-generated keys (returned from an insert operation) into an
1056     * object.
1057     *
1058     * @param object {@link java.lang.Object} to have fields mapped to
1059     * auto-generated keys set
1060     * @param result {@link Result} containing auto-generated keys
1061     * @since 1.0
1062     */

1063    public void setAutoGeneratedKeys(final Object JavaDoc object, final Result result) {
1064
1065        if (result.getGeneratedKeys().size() != 0) {
1066            final TableMapping mapping = getTableMapping(object.getClass(), "setAutoGeneratedKeys()");
1067            for (int i = 0; i < mapping.getAutoGeneratedColumns().length; i++) {
1068                final String JavaDoc columnName = mapping.getAutoGeneratedColumns()[i];
1069                final Method JavaDoc setter = mapping.getSetterForColumn(columnName);
1070                final Object JavaDoc key = result.getGeneratedKeys().get(i);
1071                try {
1072                    setter.invoke(object, new Object JavaDoc[] { key });
1073                } catch (Exception JavaDoc e) {
1074                    throw new RuntimeSQLException("Could not invoke setter [" + setter + "] with auto generated key ["
1075                            + key + "] of class [" + key.getClass().getName() + "]", e);
1076                }
1077            }
1078        }
1079
1080    }
1081
1082    // ---------- execute ----------
1083

1084    /**
1085     * Executes an update and return a {@link Result} object containing the
1086     * number of rows modified and auto-generated keys produced.
1087     * <p>
1088     * Parameters will be set according with the correspondence defined in
1089     * {@link #setParameters(PreparedStatement, int[], Object[])}
1090     *
1091     * @param objectClass Class of the object related with the query. Used to
1092     * determine the types of the auto-incremented keys. Only important if
1093     * autoGeneratedKeys contains values.
1094     * @param sql SQL code to be executed.
1095     * @param autoGeneratedKeys List of columns that are going to be
1096     * auto-generated during the query execution.
1097     * @param parameters Parameters to be used in the PreparedStatement.
1098     * @since 1.0
1099     */

1100    public Result executeUpdate(final Class JavaDoc objectClass, final String JavaDoc sql, final String JavaDoc[] autoGeneratedKeys,
1101            final Object JavaDoc...parameters) {
1102
1103        long begin = 0;
1104        if (Log.isDebugEnabled(Log.PROFILING)) {
1105            begin = System.currentTimeMillis();
1106        }
1107
1108        final PreparedStatement JavaDoc stmt = getPreparedStatement(sql, autoGeneratedKeys);
1109
1110        setParameters(stmt, parameters);
1111
1112        int rowsModified = 0;
1113        try {
1114            rowsModified = stmt.executeUpdate();
1115        } catch (SQLException JavaDoc e) {
1116            throw new RuntimeSQLException("Error executing sql [" + sql + "] with parameters "
1117                    + Arrays.toString(parameters) + ": " + e.getMessage(), e);
1118        }
1119
1120        final List JavaDoc generatedKeys = new ArrayList JavaDoc();
1121        if (autoGeneratedKeys.length != 0) {
1122            try {
1123                final Mapping mapping = getMapping(objectClass);
1124                final ResultSet JavaDoc resultSet = stmt.getGeneratedKeys();
1125                for (int i = 0; i < autoGeneratedKeys.length; i++) {
1126                    resultSet.next();
1127
1128                    // get the auto-generated key using the ResultSet.get method
1129
// that matches
1130
// the bean setter parameter type
1131
final Method JavaDoc setter = mapping.getSetterForColumn(autoGeneratedKeys[i]);
1132                    final Class JavaDoc type = setter.getParameterTypes()[0];
1133                    final Object JavaDoc value = Persist.getValueFromResultSet(resultSet, 1, type);
1134
1135                    generatedKeys.add(value);
1136                }
1137                resultSet.close();
1138            } catch (SQLException JavaDoc e) {
1139                throw new RuntimeSQLException("This JDBC driver does not support PreparedStatement.getGeneratedKeys()."
1140                        + " Please use setUpdateAutoGeneratedKeys(false) in your Persist instance"
1141                        + " to disable attempts to use that feature");
1142            }
1143        }
1144
1145        Result result = new Result(rowsModified, generatedKeys);
1146
1147        if (Log.isDebugEnabled(Log.PROFILING)) {
1148            final long end = System.currentTimeMillis();
1149            Log.debug(Log.PROFILING, "executeUpdate in [" + (end - begin) + "ms] for sql [" + sql + "]");
1150        }
1151
1152        return result;
1153    }
1154
1155    /**
1156     * Executes an update and returns the number of rows modified.
1157     * <p>
1158     * Parameters will be set according with the correspondence defined in
1159     * {@link #setParameters(PreparedStatement, int[], Object[])}
1160     *
1161     * @param sql SQL code to be executed.
1162     * @param parameters Parameters to be used in the PreparedStatement.
1163     * @since 1.0
1164     */

1165    public int executeUpdate(final String JavaDoc sql, final Object JavaDoc...parameters) {
1166
1167        final PreparedStatement JavaDoc stmt = getPreparedStatement(sql);
1168        int rowsModified = 0;
1169
1170        try {
1171            setParameters(stmt, parameters);
1172            rowsModified = stmt.executeUpdate();
1173        } catch (SQLException JavaDoc e) {
1174            throw new RuntimeSQLException("Error executing sql [" + sql + "] with parameters "
1175                    + Arrays.toString(parameters) + ": " + e.getMessage(), e);
1176        }
1177
1178        closePreparedStatement(stmt);
1179        return rowsModified;
1180    }
1181
1182    // ---------- insert ----------
1183

1184    /**
1185     * Inserts an object into the database.
1186     *
1187     * @since 1.0
1188     */

1189    public int insert(final Object JavaDoc object) {
1190        final TableMapping mapping = getTableMapping(object.getClass(), "insert()");
1191        final String JavaDoc sql = mapping.getInsertSql();
1192        final String JavaDoc[] columns = mapping.getNotAutoGeneratedColumns();
1193        final Object JavaDoc[] parameters = getParametersFromObject(object, columns, mapping);
1194
1195        int ret = 0;
1196        if (updateAutoGeneratedKeys) {
1197            if (mapping.supportsGetGeneratedKeys()) {
1198                final Result result = executeUpdate(object.getClass(), sql, mapping.getAutoGeneratedColumns(),
1199                        parameters);
1200                setAutoGeneratedKeys(object, result);
1201                ret = result.getRowsModified();
1202            } else {
1203                throw new RuntimeSQLException("While inserting instance of [" + object.getClass().getName()
1204                        + "] autoUpdateGeneratedKeys is set to [true] but the database doesn't support this feature");
1205            }
1206        } else {
1207            ret = executeUpdate(sql, parameters);
1208        }
1209        return ret;
1210    }
1211
1212    /**
1213     * Inserts a batch of objects into the database.
1214     *
1215     * @since 1.0
1216     */

1217    // TODO: use batch updates
1218
public int[] insertBatch(final Object JavaDoc...objects) {
1219        if (objects == null || objects.length == 0) {
1220            return new int[0];
1221        }
1222        final int[] results = new int[objects.length];
1223        for (int i = 0; i < objects.length; i++) {
1224            results[i] = insert(objects[i]);
1225        }
1226        return results;
1227    }
1228
1229    // ---------- update ----------
1230

1231    /**
1232     * Updates an object in the database. The object will be identified using
1233     * its mapped table's primary key. If no primary keys are defined in the
1234     * mapped table, a {@link RuntimeSQLException} will be thrown.
1235     *
1236     * @since 1.0
1237     */

1238    public int update(final Object JavaDoc object) {
1239        final TableMapping mapping = getTableMapping(object.getClass(), "update()");
1240
1241        if (mapping.getPrimaryKeys().length == 0) {
1242            throw new RuntimeSQLException("Table " + mapping.getTableName() + " doesn't have a primary key");
1243        }
1244        final String JavaDoc sql = mapping.getUpdateSql();
1245        final String JavaDoc[] columns = new String JavaDoc[mapping.getNotPrimaryKeys().length + mapping.getPrimaryKeys().length];
1246        int i = 0;
1247        for (String JavaDoc notPrimaryKey : mapping.getNotPrimaryKeys()) {
1248            columns[i++] = notPrimaryKey;
1249        }
1250        for (String JavaDoc primaryKey : mapping.getPrimaryKeys()) {
1251            columns[i++] = primaryKey;
1252        }
1253        final Object JavaDoc[] parameters = getParametersFromObject(object, columns, mapping);
1254        return executeUpdate(sql, parameters);
1255    }
1256
1257    /**
1258     * Updates a batch of objects in the database. The objects will be
1259     * identified using their mapped table's primary keys. If no primary keys
1260     * are defined in the mapped table, a {@link RuntimeSQLException} will be
1261     * thrown.
1262     *
1263     * @since 1.0
1264     */

1265    public int[] updateBatch(final Object JavaDoc...objects) {
1266        // TODO: use batch updates
1267
if (objects == null || objects.length == 0) {
1268            return new int[0];
1269        }
1270        int[] results = new int[objects.length];
1271        for (int i = 0; i < objects.length; i++) {
1272            results[i] = update(objects[i]);
1273        }
1274        return results;
1275    }
1276
1277    // ---------- delete ----------
1278

1279    /**
1280     * Deletes an object in the database. The object will be identified using
1281     * its mapped table's primary key. If no primary keys are defined in the
1282     * mapped table, a RuntimeSQLException will be thrown.
1283     *
1284     * @since 1.0
1285     */

1286    public int delete(final Object JavaDoc object) {
1287        final TableMapping mapping = getTableMapping(object.getClass(), "delete()");
1288        if (mapping.getPrimaryKeys().length == 0) {
1289            throw new RuntimeSQLException("Table " + mapping.getTableName() + " doesn't have a primary key");
1290        }
1291        final String JavaDoc sql = mapping.getDeleteSql();
1292        final String JavaDoc[] columns = mapping.getPrimaryKeys();
1293        final Object JavaDoc[] parameters = getParametersFromObject(object, columns, mapping);
1294        return executeUpdate(sql, parameters);
1295    }
1296
1297    /**
1298     * Updates a batch of objects in the database. The objects will be
1299     * identified using their matched table's primary keys. If no primary keys
1300     * are defined in a given object, a RuntimeSQLException will be thrown.
1301     *
1302     * @since 1.0
1303     */

1304    public int[] deleteBatch(final Object JavaDoc...objects) {
1305        // TODO: use batch updates
1306
if (objects == null || objects.length == 0) {
1307            return new int[0];
1308        }
1309        int[] results = new int[objects.length];
1310        for (int i = 0; i < objects.length; i++) {
1311            results[i] = delete(objects[i]);
1312        }
1313        return results;
1314    }
1315
1316    // ---------- read ----------
1317

1318    // --- single objects ---
1319

1320    /**
1321     * Reads a single object from the database by mapping the results of the SQL
1322     * query into an instance of the given object class. Only the columns
1323     * returned from the SQL query will be set into the object instance. If a
1324     * given column can't be mapped to the target object instance, a
1325     * {@link RuntimeSQLException} will be thrown.
1326     *
1327     * @since 1.0
1328     */

1329    public <T> T read(final Class JavaDoc<T> objectClass, final String JavaDoc sql) {
1330        return read(objectClass, sql, (Object JavaDoc[]) null);
1331    }
1332
1333    /**
1334     * Reads a single object from the database by mapping the results of the
1335     * parameterized SQL query into an instance of the given object class. Only
1336     * the columns returned from the SQL query will be set into the object
1337     * instance. If a given column can't be mapped to the target object
1338     * instance, a {@link RuntimeSQLException} will be thrown.
1339     * <p>
1340     * Parameters will be set according with the correspondence defined in
1341     * {@link #setParameters(PreparedStatement, int[], Object[])}
1342     *
1343     * @since 1.0
1344     */

1345    public <T> T read(final Class JavaDoc<T> objectClass, final String JavaDoc sql, final Object JavaDoc...parameters) {
1346        final PreparedStatement JavaDoc stmt = getPreparedStatement(sql);
1347        return read(objectClass, stmt, parameters);
1348    }
1349
1350    /**
1351     * Reads a single object from the database by mapping the results of the
1352     * execution of the given PreparedStatement into an instance of the given
1353     * object class. Only the columns returned from the PreparedStatement
1354     * execution will be set into the object instance. If a given column can't
1355     * be mapped to the target object instance, a {@link RuntimeSQLException}
1356     * will be thrown.
1357     * <p>
1358     * Parameters will be set according with the correspondence defined in
1359     * {@link #setParameters(PreparedStatement, int[], Object[])}
1360     *
1361     * @since 1.0
1362     */

1363    public <T> T read(final Class JavaDoc<T> objectClass, final PreparedStatement JavaDoc statement, final Object JavaDoc...parameters) {
1364        setParameters(statement, parameters);
1365        try {
1366            final ResultSet JavaDoc resultSet = statement.executeQuery();
1367            final T ret = read(objectClass, resultSet);
1368            if (closePreparedStatementsAfterRead) {
1369                closePreparedStatement(statement);
1370            }
1371            return ret;
1372        } catch (SQLException JavaDoc e) {
1373            throw new RuntimeSQLException(e);
1374        }
1375    }
1376
1377    /**
1378     * Reads a single object from the database by mapping the content of the
1379     * ResultSet current row into an instance of the given object class. Only
1380     * columns contained in the ResultSet will be set into the object instance.
1381     * If a given column can't be mapped to the target object instance, a
1382     * RuntimeSQLException will be thrown.
1383     *
1384     * @since 1.0
1385     */

1386    public <T> T read(final Class JavaDoc<T> objectClass, final ResultSet JavaDoc resultSet) {
1387        long begin = 0;
1388        if (Log.isDebugEnabled(Log.PROFILING)) {
1389            begin = System.currentTimeMillis();
1390        }
1391
1392        Object JavaDoc ret = null;
1393        try {
1394            if (resultSet.next()) {
1395                ret = loadObject(objectClass, resultSet);
1396                if (resultSet.next()) {
1397                    throw new RuntimeSQLException("Non-unique result returned");
1398                }
1399            }
1400        } catch (SQLException JavaDoc e) {
1401            throw new RuntimeSQLException(e);
1402        }
1403
1404        if (Log.isDebugEnabled(Log.PROFILING)) {
1405            final long end = System.currentTimeMillis();
1406            Log.debug(Log.PROFILING, "read in [" + (end - begin) + "ms] for object type ["
1407                    + objectClass.getSimpleName() + "]");
1408        }
1409
1410        return (T) ret;
1411    }
1412
1413    /**
1414     * Reads an object from the database by its primary keys.
1415     *
1416     * @since 1.0
1417     */

1418    public <T> T readByPrimaryKey(final Class JavaDoc<T> objectClass, final Object JavaDoc...primaryKeyValues) {
1419        final TableMapping mapping = getTableMapping(objectClass, "readByPrimaryKey()");
1420        final String JavaDoc sql = mapping.getSelectSql();
1421        return read(objectClass, sql, primaryKeyValues);
1422    }
1423
1424    // --- lists ---
1425

1426    /**
1427     * Reads a list of objects from the database by mapping the content of the
1428     * ResultSet into instances of the given object class. Only columns
1429     * contained in the ResultSet will be set into the object instances. If a
1430     * given column can't be mapped to a target object instance, a
1431     * RuntimeSQLException will be thrown.
1432     *
1433     * @since 1.0
1434     */

1435    public <T> List JavaDoc<T> readList(final Class JavaDoc<T> objectClass, final ResultSet JavaDoc resultSet) {
1436
1437        long begin = 0;
1438        if (Log.isDebugEnabled(Log.PROFILING)) {
1439            begin = System.currentTimeMillis();
1440        }
1441
1442        final List JavaDoc<T> ret = new ArrayList JavaDoc();
1443        try {
1444            while (resultSet.next()) {
1445                ret.add((T) loadObject(objectClass, resultSet));
1446            }
1447        } catch (SQLException JavaDoc e) {
1448            throw new RuntimeSQLException(e);
1449        }
1450
1451        if (Log.isDebugEnabled(Log.PROFILING)) {
1452            final long end = System.currentTimeMillis();
1453            Log.debug(Log.PROFILING, "readList in [" + (end - begin) + "ms] for object type ["
1454                    + objectClass.getSimpleName() + "]");
1455        }
1456
1457        return ret;
1458    }
1459
1460    /**
1461     * Reads a list of objects from the database by mapping the results of the
1462     * execution of the given PreparedStatement into instances of the given
1463     * object class. Only the columns returned from the PreparedStatement
1464     * execution will be set into the object instances. If a given column can't
1465     * be mapped to a target object instance, a RuntimeSQLException will be
1466     * thrown.
1467     * <p>
1468     * Parameters will be set according with the correspondence defined in
1469     * {@link #setParameters(PreparedStatement, int[], Object[])}
1470     *
1471     * @since 1.0
1472     */

1473    public <T> List JavaDoc<T> readList(final Class JavaDoc<T> objectClass, final PreparedStatement JavaDoc statement,
1474            final Object JavaDoc...parameters) {
1475        setParameters(statement, parameters);
1476        try {
1477            final ResultSet JavaDoc resultSet = statement.executeQuery();
1478            return readList(objectClass, resultSet);
1479        } catch (SQLException JavaDoc e) {
1480            throw new RuntimeSQLException(e);
1481        }
1482    }
1483
1484    /**
1485     * Reads a list of objects from the database by mapping the results of the
1486     * parameterized SQL query into instances of the given object class. Only
1487     * the columns returned from the SQL query will be set into the object
1488     * instance. If a given column can't be mapped to the target object
1489     * instance, a {@link RuntimeSQLException} will be thrown.
1490     * <p>
1491     * Parameters will be set according with the correspondence defined in
1492     * {@link #setParameters(PreparedStatement, int[], Object[])}
1493     *
1494     * @since 1.0
1495     */

1496    public <T> List JavaDoc<T> readList(final Class JavaDoc<T> objectClass, final String JavaDoc sql, final Object JavaDoc...parameters) {
1497        final PreparedStatement JavaDoc stmt = getPreparedStatement(sql);
1498        final List JavaDoc ret = readList(objectClass, stmt, parameters);
1499        if (closePreparedStatementsAfterRead) {
1500            closePreparedStatement(stmt);
1501        }
1502        return ret;
1503    }
1504
1505    /**
1506     * Reads a list of objects from the database by mapping the results of the
1507     * SQL query into instances of the given object class. Only the columns
1508     * returned from the SQL query will be set into the object instance. If a
1509     * given column can't be mapped to the target object instance, a
1510     * {@link RuntimeSQLException} will be thrown.
1511     *
1512     * @since 1.0
1513     */

1514    public <T> List JavaDoc<T> readList(final Class JavaDoc<T> objectClass, final String JavaDoc sql) {
1515        return readList(objectClass, sql, (Object JavaDoc[]) null);
1516    }
1517
1518    /**
1519     * Reads a list of all objects in the database mapped to the given object
1520     * class.
1521     *
1522     * @since 1.0
1523     */

1524    public <T> List JavaDoc<T> readList(final Class JavaDoc<T> objectClass) {
1525        final TableMapping mapping = getTableMapping(objectClass, "readList(Class)");
1526        final String JavaDoc sql = mapping.getSelectAllSql();
1527        return readList(objectClass, sql);
1528    }
1529
1530    // --- iterators ---
1531

1532    /**
1533     * Returns an {@link java.util.Iterator} for a list of objects from the
1534     * database that map the contents of the ResultSet into instances of the
1535     * given object class. Only columns contained in the ResultSet will be set
1536     * into the object instances. If a given column can't be mapped to a target
1537     * object instance, a {@link RuntimeSQLException} will be thrown.
1538     *
1539     * @since 1.0
1540     */

1541    public <T> Iterator JavaDoc<T> readIterator(final Class JavaDoc<T> objectClass, final ResultSet JavaDoc resultSet) {
1542
1543        long begin = 0;
1544        if (Log.isDebugEnabled(Log.PROFILING)) {
1545            begin = System.currentTimeMillis();
1546        }
1547
1548        final ResultSetIterator i = new ResultSetIterator(this, objectClass, resultSet, ResultSetIterator.TYPE_OBJECT);
1549
1550        if (Log.isDebugEnabled(Log.PROFILING)) {
1551            final long end = System.currentTimeMillis();
1552            Log.debug(Log.PROFILING, "readIterator in [" + (end - begin) + "ms] for object type ["
1553                    + objectClass.getSimpleName() + "]");
1554        }
1555
1556        return i;
1557    }
1558
1559    /**
1560     * Returns an {@link java.util.Iterator} for a list of objects from the
1561     * database that map the contents of the execution of the given
1562     * PreparedStatement into instances of the given object class. Only columns
1563     * contained in the ResultSet will be set into the object instances. If a
1564     * given column can't be mapped to a target object instance, a
1565     * RuntimeSQLException will be thrown.
1566     * <p>
1567     * Parameters will be set according with the correspondence defined in
1568     * {@link #setParameters(PreparedStatement, int[], Object[])}
1569     *
1570     * @since 1.0
1571     */

1572    public <T> Iterator JavaDoc<T> readIterator(final Class JavaDoc<T> objectClass, final PreparedStatement JavaDoc statement,
1573            final Object JavaDoc...parameters) {
1574        setParameters(statement, parameters);
1575        try {
1576            final ResultSet JavaDoc resultSet = statement.executeQuery();
1577            return readIterator(objectClass, resultSet);
1578        } catch (SQLException JavaDoc e) {
1579            throw new RuntimeSQLException(e);
1580        }
1581    }
1582
1583    /**
1584     * Returns an {@link java.util.Iterator} for a list of objects from the
1585     * database that map the contents of the execution of the given SQL query
1586     * into instances of the given object class. Only columns contained in the
1587     * ResultSet will be set into the object instances. If a given column can't
1588     * be mapped to a target object instance, a {@link RuntimeSQLException} will
1589     * be thrown.
1590     * <p>
1591     * Parameters will be set according with the correspondence defined in
1592     * {@link #setParameters(PreparedStatement, int[], Object[])}
1593     *
1594     * @since 1.0
1595     */

1596    public <T> Iterator JavaDoc<T> readIterator(final Class JavaDoc<T> objectClass, final String JavaDoc sql, final Object JavaDoc...parameters) {
1597        final PreparedStatement JavaDoc stmt = getPreparedStatement(sql);
1598        final Iterator JavaDoc ret = readIterator(objectClass, stmt, parameters);
1599        // don't close the prepared statement otherwise the result set in the
1600
// iterator will be closed
1601
return ret;
1602    }
1603
1604    /**
1605     * Returns an {@link java.util.Iterator} for a list of objects from the
1606     * database that map the contents of the execution of the given SQL query
1607     * into instances of the given object class. Only columns contained in the
1608     * ResultSet will be set into the object instances. If a given column can't
1609     * be mapped to a target object instance, a {@link RuntimeSQLException} will
1610     * be thrown.
1611     *
1612     * @since 1.0
1613     */

1614    public <T> Iterator JavaDoc<T> readIterator(final Class JavaDoc<T> objectClass, final String JavaDoc sql) {
1615        return readIterator(objectClass, sql, (Object JavaDoc[]) null);
1616    }
1617
1618    /**
1619     * Returns an {@link java.util.Iterator} for a list of all objects in the
1620     * database mapped to the given object class.
1621     *
1622     * @since 1.0
1623     */

1624    public <T> Iterator JavaDoc<T> readIterator(final Class JavaDoc<T> objectClass) {
1625        final TableMapping mapping = getTableMapping(objectClass, "readIterator(Class)");
1626        final String JavaDoc sql = mapping.getSelectAllSql();
1627        return readIterator(objectClass, sql);
1628    }
1629
1630    // ---------- read (map) ----------
1631

1632    // --- single objects ---
1633

1634    /**
1635     * Reads a single object from the database by mapping the results of the SQL
1636     * query into an instance of {@link java.util.Map}.
1637     * <p>
1638     * Types returned from the database will be converted to Java types in the
1639     * map according with the correspondence defined in
1640     * {@link #getValueFromResultSet(ResultSet, int, int)}.
1641     *
1642     * @since 1.0
1643     */

1644    public Map JavaDoc<String JavaDoc, Object JavaDoc> readMap(final String JavaDoc sql) {
1645        return readMap(sql, (Object JavaDoc[]) null);
1646    }
1647
1648    /**
1649     * Reads a single object from the database by mapping the results of the SQL
1650     * query into an instance of {@link java.util.Map}.
1651     * <p>
1652     * Types returned from the database will be converted to Java types in the
1653     * map according with the correspondence defined in
1654     * {@link #getValueFromResultSet(ResultSet, int, int)}.
1655     * <p>
1656     * Parameters will be set according with the correspondence defined in
1657     * {@link Persist#setParameters(PreparedStatement, int[], Object[])}
1658     *
1659     * @since 1.0
1660     */

1661    public Map JavaDoc<String JavaDoc, Object JavaDoc> readMap(final String JavaDoc sql, final Object JavaDoc...parameters) {
1662        final PreparedStatement JavaDoc stmt = getPreparedStatement(sql);
1663        final Map JavaDoc<String JavaDoc, Object JavaDoc> ret = readMap(stmt, parameters);
1664        if (closePreparedStatementsAfterRead) {
1665            closePreparedStatement(stmt);
1666        }
1667        return ret;
1668    }
1669
1670    /**
1671     * Reads a single object from the database by mapping the results of the
1672     * PreparedStatement execution into an instance of {@link java.util.Map}.
1673     * <p>
1674     * Types returned from the database will be converted to Java types in the
1675     * map according with the correspondence defined in
1676     * {@link #getValueFromResultSet(ResultSet, int, int)}.
1677     * <p>
1678     * Parameters will be set according with the correspondence defined in
1679     * {@link Persist#setParameters(PreparedStatement, int[], Object[])}
1680     *
1681     * @since 1.0
1682     */

1683    public Map JavaDoc<String JavaDoc, Object JavaDoc> readMap(final PreparedStatement JavaDoc statement, final Object JavaDoc...parameters) {
1684        setParameters(statement, parameters);
1685        try {
1686            final ResultSet JavaDoc resultSet = statement.executeQuery();
1687            return readMap(resultSet);
1688        } catch (SQLException JavaDoc e) {
1689            throw new RuntimeSQLException(e);
1690        }
1691    }
1692
1693    /**
1694     * Reads a single object from the database by mapping the results of the
1695     * current ResultSet row into an instance of {@link java.util.Map}.
1696     * <p>
1697     * Types returned from the database will be converted to Java types in the
1698     * map according with the correspondence defined in
1699     * {@link #getValueFromResultSet(ResultSet, int, int)}.
1700     *
1701     * @since 1.0
1702     */

1703    public Map JavaDoc<String JavaDoc, Object JavaDoc> readMap(final ResultSet JavaDoc resultSet) {
1704
1705        long begin = 0;
1706        if (Log.isDebugEnabled(Log.PROFILING)) {
1707            begin = System.currentTimeMillis();
1708        }
1709
1710        Map JavaDoc<String JavaDoc, Object JavaDoc> ret = null;
1711
1712        try {
1713            if (resultSet.next()) {
1714                ret = loadMap(resultSet);
1715                if (resultSet.next()) {
1716                    throw new RuntimeSQLException("Non-unique result returned");
1717                }
1718            } else {
1719                ret = null;
1720            }
1721        } catch (SQLException JavaDoc e) {
1722            throw new RuntimeSQLException(e);
1723        }
1724
1725        if (Log.isDebugEnabled(Log.PROFILING)) {
1726            final long end = System.currentTimeMillis();
1727            Log.debug(Log.PROFILING, "readMap in [" + (end - begin) + "ms]");
1728        }
1729
1730        return ret;
1731    }
1732
1733    // --- list ---
1734

1735    /**
1736     * Reads a list of objects from the database by mapping the ResultSet rows
1737     * to instances of {@link java.util.Map}.
1738     * <p>
1739     * Types returned from the database will be converted to Java types in the
1740     * map according with the correspondence defined in
1741     * {@link #getValueFromResultSet(ResultSet, int, int)}.
1742     *
1743     * @since 1.0
1744     */

1745    public List JavaDoc<Map JavaDoc<String JavaDoc, Object JavaDoc>> readMapList(final ResultSet JavaDoc resultSet) {
1746
1747        long begin = 0;
1748        if (Log.isDebugEnabled(Log.PROFILING)) {
1749            begin = System.currentTimeMillis();
1750        }
1751
1752        final List JavaDoc ret = new ArrayList JavaDoc();
1753        try {
1754            while (resultSet.next()) {
1755                ret.add(loadMap(resultSet));
1756            }
1757        } catch (SQLException JavaDoc e) {
1758            throw new RuntimeSQLException(e);
1759        }
1760
1761        if (Log.isDebugEnabled(Log.PROFILING)) {
1762            final long end = System.currentTimeMillis();
1763            Log.debug(Log.PROFILING, "readMapList [" + (end - begin) + "ms]");
1764        }
1765
1766        return ret;
1767    }
1768
1769    /**
1770     * Reads a list of objects from the database by mapping the
1771     * PreparedStatement execution results to instances of {@link java.util.Map}.
1772     * <p>
1773     * Types returned from the database will be converted to Java types in the
1774     * map according with the correspondence defined in
1775     * {@link #getValueFromResultSet(ResultSet, int, int)}.
1776     * <p>
1777     * Parameters will be set according with the correspondence defined in
1778     * {@link #setParameters(PreparedStatement, int[], Object[])}
1779     *
1780     * @since 1.0
1781     */

1782    public List JavaDoc<Map JavaDoc<String JavaDoc, Object JavaDoc>> readMapList(final PreparedStatement JavaDoc statement, final Object JavaDoc...parameters) {
1783        setParameters(statement, parameters);
1784        try {
1785            final ResultSet JavaDoc resultSet = statement.executeQuery();
1786            return readMapList(resultSet);
1787        } catch (SQLException JavaDoc e) {
1788            throw new RuntimeSQLException(e);
1789        }
1790    }
1791
1792    /**
1793     * Reads a list of objects from the database by mapping the SQL execution
1794     * results to instances of {@link java.util.Map}.
1795     * <p>
1796     * Types returned from the database will be converted to Java types in the
1797     * map according with the correspondence defined in
1798     * {@link #getValueFromResultSet(ResultSet, int, int)}.
1799     * <p>
1800     * Parameters will be set according with the correspondence defined in
1801     * {@link #setParameters(PreparedStatement, int[], Object[])}
1802     *
1803     * @since 1.0
1804     */

1805    public List JavaDoc<Map JavaDoc<String JavaDoc, Object JavaDoc>> readMapList(final String JavaDoc sql, final Object JavaDoc...parameters) {
1806        final PreparedStatement JavaDoc stmt = getPreparedStatement(sql);
1807        final List JavaDoc<Map JavaDoc<String JavaDoc, Object JavaDoc>> ret = readMapList(stmt, parameters);
1808        if (closePreparedStatementsAfterRead) {
1809            closePreparedStatement(stmt);
1810        }
1811        return ret;
1812    }
1813
1814    /**
1815     * Reads a list of all objects in the database mapped to the given object
1816     * class and return each result as an instance of {@link java.util.Map}.
1817     * <p>
1818     * Types returned from the database will be converted to Java types in the
1819     * map according with the correspondence defined in
1820     * {@link #getValueFromResultSet(ResultSet, int, int)}.
1821     *
1822     * @since 1.0
1823     */

1824    public List JavaDoc<Map JavaDoc<String JavaDoc, Object JavaDoc>> readMapList(final String JavaDoc sql) {
1825        return readMapList(sql, (Object JavaDoc[]) null);
1826    }
1827
1828    // --- iterator ---
1829

1830    /**
1831     * Returns an {@link java.util.Iterator} for a list of {@link java.util.Map}
1832     * instances containing data from the provided ResultSet rows.
1833     * <p>
1834     * Types returned from the database will be converted to Java types in the
1835     * map according with the correspondence defined in
1836     * {@link #getValueFromResultSet(ResultSet, int, int)}.
1837     *
1838     * @since 1.0
1839     */

1840    public Iterator JavaDoc readMapIterator(final ResultSet JavaDoc resultSet) {
1841        return new ResultSetIterator(this, null, resultSet, ResultSetIterator.TYPE_MAP);
1842    }
1843
1844    /**
1845     * Returns an {@link java.util.Iterator} for a list of {@link java.util.Map}
1846     * instances containing data from the execution of the provided
1847     * PreparedStatement.
1848     * <p>
1849     * Types returned from the database will be converted to Java types in the
1850     * map according with the correspondence defined in
1851     * {@link #getValueFromResultSet(ResultSet, int, int)}.
1852     * <p>
1853     * Parameters will be set according with the correspondence defined in
1854     * {@link Persist#setParameters(PreparedStatement, int[], Object[])}
1855     *
1856     * @since 1.0
1857     */

1858    public Iterator JavaDoc readMapIterator(final PreparedStatement JavaDoc statement, final Object JavaDoc...parameters) {
1859        setParameters(statement, parameters);
1860        try {
1861            final ResultSet JavaDoc resultSet = statement.executeQuery();
1862            return readMapIterator(resultSet);
1863        } catch (SQLException JavaDoc e) {
1864            throw new RuntimeSQLException(e);
1865        }
1866    }
1867
1868    /**
1869     * Returns an {@link java.util.Iterator} for a list of {@link java.util.Map}
1870     * instances containing data from the execution of the provided parametrized
1871     * SQL.
1872     * <p>
1873     * Types returned from the database will be converted to Java types in the
1874     * map according with the correspondence defined in
1875     * {@link #getValueFromResultSet(ResultSet, int, int)}.
1876     * <p>
1877     * Parameters will be set according with the correspondence defined in
1878     * {@link #setParameters(PreparedStatement, int[], Object[])}
1879     *
1880     * @since 1.0
1881     */

1882    public Iterator JavaDoc readMapIterator(final String JavaDoc sql, final Object JavaDoc...parameters) {
1883
1884        long begin = 0;
1885        if (Log.isDebugEnabled(Log.PROFILING)) {
1886            begin = System.currentTimeMillis();
1887        }
1888
1889        final PreparedStatement JavaDoc stmt = getPreparedStatement(sql);
1890        final Iterator JavaDoc ret = readMapIterator(stmt, parameters);
1891
1892        if (Log.isDebugEnabled(Log.PROFILING)) {
1893            final long end = System.currentTimeMillis();
1894            Log.debug(Log.PROFILING, "readMapIterator in [" + (end - begin) + "ms]");
1895        }
1896
1897        return ret;
1898    }
1899
1900    /**
1901     * Returns an {@link java.util.Iterator} for a list of {@link java.util.Map}
1902     * instances containing data from the execution of the provided SQL.
1903     * <p>
1904     * Types returned from the database will be converted to Java types in the
1905     * map according with the correspondence defined in
1906     * {@link #getValueFromResultSet(ResultSet, int, int)}.
1907     *
1908     * @since 1.0
1909     */

1910    public Iterator JavaDoc readMapIterator(final String JavaDoc sql) {
1911        return readMapIterator(sql, (Object JavaDoc[]) null);
1912    }
1913
1914}
Popular Tags