Source Code Cross Referenced for Persist.java in  » Database-ORM » Persist » net » sf » persist » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Database ORM » Persist » net.sf.persist 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        // $Id: Persist.java 15 2007-09-01 03:09:40Z jcamaia $
0002:
0003:        package net.sf.persist;
0004:
0005:        import java.io.IOException;
0006:        import java.lang.reflect.Method;
0007:        import java.math.BigDecimal;
0008:        import java.sql.Blob;
0009:        import java.sql.Clob;
0010:        import java.sql.Connection;
0011:        import java.sql.ParameterMetaData;
0012:        import java.sql.PreparedStatement;
0013:        import java.sql.ResultSet;
0014:        import java.sql.ResultSetMetaData;
0015:        import java.sql.SQLException;
0016:        import java.util.ArrayList;
0017:        import java.util.Arrays;
0018:        import java.util.Iterator;
0019:        import java.util.LinkedHashMap;
0020:        import java.util.List;
0021:        import java.util.Map;
0022:        import java.util.concurrent.ConcurrentHashMap;
0023:        import java.util.concurrent.ConcurrentMap;
0024:
0025:        /**
0026:         * The main class for the persistence engine.
0027:         * <p>
0028:         * A Persist instance is bound to a {@link java.sql.Connection} object.
0029:         * Internally, Persist caches table-object mappings under <i>cache names</i>
0030:         * that allow for different mappings (most likely from different database
0031:         * schemas) to coexist. The default cache name is used if no cache name is
0032:         * specified in the constructor.
0033:         * <p>
0034:         * Persist instances are not thread safe, in particular because
0035:         * {@link java.sql.Connection} objects are not thread safe.
0036:         * <p>
0037:         * Persist instances are created with the following defaults:
0038:         * <ul>
0039:         * <li> closePreparedStatementsAfterRead=true This will work for most reads
0040:         * (select queries) that do not return long-lasting objects, such as streams or
0041:         * LOB handlers. If a query returns InputStream, Reader, Blob or Clob objects,
0042:         * closePreparedStatementsAfterRead should be set to false, and closing the
0043:         * PreparedStatement must be controlled manually. This is because those
0044:         * datatypes stream data from database after the PreparedStatement execution.
0045:         * <li> updateAutoGeneratedKeys=false This means that objects that are inserted
0046:         * either using insert() or executeUpdate() (with autoGeneratedKeys option) will
0047:         * not have their primary keys automatically updated against the generated keys
0048:         * in the database. Please consult your JDBC driver support for querying
0049:         * auto-generated keys in {@link java.sql.PreparedStatement}
0050:         * <li> {@link DefaultNameGuesser} which will take names in the form
0051:         * CompoundName (for classes) or compoundName (for fields) and return a set of
0052:         * guessed names such as [compound_name, compound_names, compoundname,
0053:         * compoundnames].
0054:         * </ul>
0055:         * 
0056:         * @see TableMapping
0057:         */
0058:        public final class Persist {
0059:
0060:            private Connection connection;
0061:            private boolean updateAutoGeneratedKeys = false;
0062:            private PreparedStatement lastPreparedStatement = null;
0063:            private boolean closePreparedStatementsAfterRead = true;
0064:
0065:            private static ConcurrentMap<String, ConcurrentMap<Class, Mapping>> mappingCaches = new ConcurrentHashMap();
0066:            private static ConcurrentMap<String, NameGuesser> nameGuessers = new ConcurrentHashMap();
0067:
0068:            private static final String DEFAULT_CACHE = "default cache";
0069:
0070:            private String cacheName = DEFAULT_CACHE;
0071:            private NameGuesser nameGuesser = null;
0072:
0073:            static {
0074:                mappingCaches.put(DEFAULT_CACHE, new ConcurrentHashMap());
0075:                nameGuessers.put(DEFAULT_CACHE, new DefaultNameGuesser());
0076:
0077:                if (Log.isDebugEnabled(Log.ENGINE)) {
0078:                    Log.debug(Log.ENGINE, "Caches initialized");
0079:                }
0080:            }
0081:
0082:            // ---------- constructors ----------
0083:
0084:            /**
0085:             * Creates a Persist instance that will use the default cache for
0086:             * table-object mappings.
0087:             * 
0088:             * @param connection {@link java.sql.Connection} object to be used
0089:             * @since 1.0
0090:             */
0091:            public Persist(Connection connection) {
0092:                this (DEFAULT_CACHE, connection);
0093:            }
0094:
0095:            /**
0096:             * Creates a Persist instance that will use the given cache name for
0097:             * table-object mappings.
0098:             * 
0099:             * @param cacheName Name of the cache to be used
0100:             * @param connection {@link java.sql.Connection} object to be used
0101:             * @since 1.0
0102:             */
0103:            public Persist(String cacheName, Connection connection) {
0104:
0105:                if (cacheName == null) {
0106:                    cacheName = DEFAULT_CACHE;
0107:                }
0108:
0109:                this .cacheName = cacheName;
0110:                this .connection = connection;
0111:
0112:                this .nameGuesser = nameGuessers.get(cacheName);
0113:                if (this .nameGuesser == null) {
0114:                    // this block may execute more than once from different threads --
0115:                    // not a problem, though
0116:                    this .nameGuesser = new DefaultNameGuesser();
0117:                    nameGuessers.put(cacheName, this .nameGuesser);
0118:                }
0119:
0120:                if (Log.isDebugEnabled(Log.ENGINE)) {
0121:                    Log.debug(Log.ENGINE, "New instance for cache ["
0122:                            + cacheName + "] and connection [" + connection
0123:                            + "]");
0124:                }
0125:            }
0126:
0127:            // ---------- name guesser ----------
0128:
0129:            /**
0130:             * Sets the {@link NameGuesser} for a given mappings cache.
0131:             * 
0132:             * @param cacheName Name of the cache to be used
0133:             * @param nameGuesser {@link NameGuesser} implementation
0134:             * @since 1.0
0135:             */
0136:            public static void setNameGuesser(final String cacheName,
0137:                    final NameGuesser nameGuesser) {
0138:                nameGuessers.put(cacheName, nameGuesser);
0139:
0140:                // purge mappings cache so that name mappings are coherent
0141:                mappingCaches.put(cacheName, new ConcurrentHashMap());
0142:
0143:                if (Log.isDebugEnabled(Log.ENGINE)) {
0144:                    Log.debug(Log.ENGINE, "Name guesser set for cache ["
0145:                            + cacheName + "]");
0146:                }
0147:            }
0148:
0149:            /**
0150:             * Sets the name guesser for the default mappings cache.
0151:             * 
0152:             * @param nameGuesser {@link NameGuesser} implementation
0153:             * @since 1.0
0154:             */
0155:            public static void setNameGuesser(final NameGuesser nameGuesser) {
0156:                nameGuessers.put(DEFAULT_CACHE, nameGuesser);
0157:            }
0158:
0159:            // ---------- autoUpdateGeneratedKeys getter/setter ----------
0160:
0161:            /**
0162:             * Sets the behavior for updating auto-generated keys.
0163:             * 
0164:             * @param updateAutoGeneratedKeys if set to true, auto-generated keys will
0165:             * be updated after the execution of insert or executeUpdate operations that
0166:             * may trigger auto-generation of keys in the database
0167:             * @since 1.0
0168:             */
0169:            public void setUpdateAutoGeneratedKeys(
0170:                    final boolean updateAutoGeneratedKeys) {
0171:                this .updateAutoGeneratedKeys = updateAutoGeneratedKeys;
0172:
0173:                if (Log.isDebugEnabled(Log.ENGINE)) {
0174:                    Log.debug(Log.ENGINE, "setUpdateAutoGeneratedKeys("
0175:                            + updateAutoGeneratedKeys + ")");
0176:                }
0177:            }
0178:
0179:            /**
0180:             * Returns true if updating auto-generated keys is enabled.
0181:             */
0182:            public boolean isUpdateAutoGeneratedKeys() {
0183:                return updateAutoGeneratedKeys;
0184:            }
0185:
0186:            // ---------- mappings cache ----------
0187:
0188:            /**
0189:             * Returns the mapping for the given object class.
0190:             * 
0191:             * @param objectClass {@link java.lang.Class} object to get a
0192:             * {@link TableMapping} for
0193:             * @since 1.0
0194:             */
0195:            public Mapping getMapping(final Class objectClass) {
0196:
0197:                if (cacheName == null) {
0198:                    cacheName = DEFAULT_CACHE;
0199:                }
0200:
0201:                if (!mappingCaches.containsKey(cacheName)) {
0202:                    // more than one map may end up being inserted here for the same
0203:                    // cacheName, but this is not problematic
0204:                    mappingCaches.put(cacheName, new ConcurrentHashMap());
0205:                }
0206:
0207:                final ConcurrentMap<Class, Mapping> mappingCache = mappingCaches
0208:                        .get(cacheName);
0209:
0210:                if (!mappingCache.containsKey(objectClass)) {
0211:                    try {
0212:                        // more than one map may end up being inserted here for the same
0213:                        // objectClass, but this is not
0214:                        // problematic
0215:                        mappingCache.put(objectClass, Mapping.getMapping(
0216:                                connection.getMetaData(), objectClass,
0217:                                nameGuesser));
0218:
0219:                        if (Log.isDebugEnabled(Log.ENGINE)) {
0220:                            Log.debug(Log.ENGINE, "Cached mapping for ["
0221:                                    + objectClass.getCanonicalName() + "]");
0222:                        }
0223:                    } catch (SQLException e) {
0224:                        throw new RuntimeSQLException(e);
0225:                    }
0226:                }
0227:
0228:                return mappingCache.get(objectClass);
0229:            }
0230:
0231:            /**
0232:             * Utility method that will get a TableMapping for a given class. If the
0233:             * mapping for the class is not a TableMapping, will throw an exception
0234:             * specifying the given calling method name.
0235:             */
0236:            private TableMapping getTableMapping(Class objectClass,
0237:                    String callingMethodName) {
0238:                final Mapping mapping = getMapping(objectClass);
0239:                if (!(mapping instanceof  TableMapping)) {
0240:                    throw new PersistException(
0241:                            "Class ["
0242:                                    + objectClass.getCanonicalName()
0243:                                    + "] has a @NoTable annotation defined, therefore "
0244:                                    + callingMethodName
0245:                                    + " can't work with it. "
0246:                                    + "If this class is supposed to be mapped to a table, @NoTable should not be used.");
0247:                }
0248:                return (TableMapping) mapping;
0249:            }
0250:
0251:            // ---------- connection ----------
0252:
0253:            /**
0254:             * Returns the {@link java.sql.Connection Connection} associated with this
0255:             * Persist instance.
0256:             * @since 1.0
0257:             */
0258:            public Connection getConnection() {
0259:                return connection;
0260:            }
0261:
0262:            /**
0263:             * Commits the {@link java.sql.Connection Connection} associated with this
0264:             * Persist instance.
0265:             * 
0266:             * @see java.sql.Connection#commit()
0267:             * @since 1.0
0268:             */
0269:            public void commit() {
0270:                try {
0271:                    connection.commit();
0272:
0273:                    if (Log.isDebugEnabled(Log.ENGINE)) {
0274:                        Log.debug(Log.ENGINE, "Connection commited");
0275:                    }
0276:                } catch (SQLException e) {
0277:                    throw new RuntimeSQLException(e);
0278:                }
0279:            }
0280:
0281:            /**
0282:             * Rolls back the {@link java.sql.Connection Connection} associated with
0283:             * this Persist instance.
0284:             * 
0285:             * @see java.sql.Connection#rollback()
0286:             * @since 1.0
0287:             */
0288:            public void rollback() {
0289:                try {
0290:                    connection.rollback();
0291:
0292:                    if (Log.isDebugEnabled(Log.ENGINE)) {
0293:                        Log.debug(Log.ENGINE, "Connection rolled back");
0294:                    }
0295:                } catch (SQLException e) {
0296:                    throw new RuntimeSQLException(e);
0297:                }
0298:            }
0299:
0300:            /**
0301:             * Sets the auto commit behavior for the
0302:             * {@link java.sql.Connection Connection} associated with this Persist
0303:             * instance.
0304:             * @see java.sql.Connection#setAutoCommit(boolean)
0305:             * @since 1.0
0306:             */
0307:            public void setAutoCommit(final boolean autoCommit) {
0308:                try {
0309:                    connection.setAutoCommit(autoCommit);
0310:
0311:                    if (Log.isDebugEnabled(Log.ENGINE)) {
0312:                        Log.debug(Log.ENGINE, "Connection setAutoCommit("
0313:                                + autoCommit + ")");
0314:                    }
0315:                } catch (SQLException e) {
0316:                    throw new RuntimeSQLException(e);
0317:                }
0318:            }
0319:
0320:            // ---------- prepared statement ----------
0321:
0322:            /**
0323:             * Creates a {@link java.sql.PreparedStatement}, setting the names of the
0324:             * auto-generated keys to be retrieved.
0325:             * 
0326:             * @param sql SQL statement to create the {@link java.sql.PreparedStatement}
0327:             * from
0328:             * @param autoGeneratedKeys names of the columns that will have
0329:             * auto-generated values produced during the execution of the
0330:             * {@link java.sql.PreparedStatement}
0331:             * @since 1.0
0332:             */
0333:            public PreparedStatement getPreparedStatement(final String sql,
0334:                    final String[] autoGeneratedKeys) {
0335:                try {
0336:                    if (autoGeneratedKeys == null
0337:                            || autoGeneratedKeys.length == 0) {
0338:                        lastPreparedStatement = getPreparedStatement(sql);
0339:                    } else {
0340:                        lastPreparedStatement = connection.prepareStatement(
0341:                                sql, autoGeneratedKeys);
0342:                    }
0343:                } catch (SQLException e) {
0344:                    throw new RuntimeSQLException(
0345:                            "Error creating prepared statement for sql [" + sql
0346:                                    + "] with autoGeneratedKeys "
0347:                                    + Arrays.toString(autoGeneratedKeys) + ": "
0348:                                    + e.getMessage(), e);
0349:                }
0350:
0351:                if (Log.isDebugEnabled(Log.ENGINE)) {
0352:                    Log.debug(Log.ENGINE, "Generated PreparedStatement ["
0353:                            + lastPreparedStatement + "] for [" + sql
0354:                            + "] using autoGeneratedKeys "
0355:                            + Arrays.toString(autoGeneratedKeys));
0356:                }
0357:
0358:                return lastPreparedStatement;
0359:            }
0360:
0361:            /**
0362:             * Creates a {@link java.sql.PreparedStatement} with no parameters.
0363:             * 
0364:             * @param sql SQL statement to create the {@link java.sql.PreparedStatement}
0365:             * from
0366:             * @since 1.0
0367:             */
0368:            public PreparedStatement getPreparedStatement(final String sql) {
0369:
0370:                try {
0371:                    lastPreparedStatement = connection.prepareStatement(sql);
0372:                } catch (SQLException e) {
0373:                    throw new RuntimeSQLException(
0374:                            "Error creating prepared statement for sql [" + sql
0375:                                    + "]: " + e.getMessage(), e);
0376:                }
0377:
0378:                if (Log.isDebugEnabled(Log.ENGINE)) {
0379:                    Log.debug(Log.ENGINE, "Generated PreparedStatement ["
0380:                            + lastPreparedStatement + "] for [" + sql + "]");
0381:                }
0382:
0383:                return lastPreparedStatement;
0384:            }
0385:
0386:            /**
0387:             * Closes a {@link java.sql.PreparedStatement}.
0388:             * 
0389:             * @param statement {@link java.sql.PreparedStatement} to be closed
0390:             * @see java.sql.PreparedStatement#close()
0391:             * @since 1.0
0392:             */
0393:            public void closePreparedStatement(final PreparedStatement statement) {
0394:                try {
0395:                    statement.close();
0396:                } catch (SQLException e) {
0397:                    throw new RuntimeSQLException(
0398:                            "Error closing prepared statement: "
0399:                                    + e.getMessage(), e);
0400:                }
0401:
0402:                if (Log.isDebugEnabled(Log.ENGINE)) {
0403:                    Log.debug(Log.ENGINE, "Closed PreparedStatement ["
0404:                            + statement + "]");
0405:                }
0406:
0407:                lastPreparedStatement = null;
0408:            }
0409:
0410:            /**
0411:             * Returns the last {@link java.sql.PreparedStatement} used by the engine.
0412:             * 
0413:             * @since 1.0
0414:             */
0415:            public PreparedStatement getLastPreparedStatement() {
0416:                return lastPreparedStatement;
0417:            }
0418:
0419:            /**
0420:             * Closes the last {@link java.sql.PreparedStatement} used by the engine.
0421:             * 
0422:             * @see java.sql.PreparedStatement#close()
0423:             * @since 1.0
0424:             */
0425:            public void closeLastPreparedStatement() {
0426:                closePreparedStatement(lastPreparedStatement);
0427:            }
0428:
0429:            /**
0430:             * Sets the behavior for closing {@link java.sql.PreparedStatement}
0431:             * instances after execution. This will only affect reads, since any update
0432:             * operations (insert, delete, update) will always have their
0433:             * {@link java.sql.PreparedStatement} instances automatically closed.
0434:             * <p>
0435:             * If a query returns InputStream, Reader, Blob or Clob objects, this should
0436:             * be set to false, and closing the PreparedStatement must be controlled
0437:             * manually. This is because those datatypes stream data from database after
0438:             * the PreparedStatement execution.
0439:             * 
0440:             * @param closePreparedStatementsAfterRead if true,
0441:             * {@link java.sql.PreparedStatement} instances for read queries will be
0442:             * automatically closed
0443:             * @since 1.0
0444:             */
0445:            public void setClosePreparedStatementsAfterRead(
0446:                    final boolean closePreparedStatementsAfterRead) {
0447:                this .closePreparedStatementsAfterRead = closePreparedStatementsAfterRead;
0448:
0449:                if (Log.isDebugEnabled(Log.ENGINE)) {
0450:                    Log.debug(Log.ENGINE,
0451:                            "setClosePreparedStatementsAfterRead("
0452:                                    + closePreparedStatementsAfterRead + ")");
0453:                }
0454:            }
0455:
0456:            /**
0457:             * Returns true if {@link java.sql.PreparedStatement} instances are
0458:             * automatically closed after read (select or otherwise) queries.
0459:             * 
0460:             * @since 1.0
0461:             */
0462:            public boolean isClosePreparedStatementsAfterRead() {
0463:                return this .closePreparedStatementsAfterRead;
0464:            }
0465:
0466:            // ---------- mappers ----------
0467:
0468:            /**
0469:             * Sets parameters in the given prepared statement.
0470:             * <p>
0471:             * Parameters will be set using PreparedStatement set methods related with
0472:             * the Java types of the parameters, according with the following table:
0473:             * <ul>
0474:             * <li> Boolean/boolean: setBoolean
0475:             * <li> Byte/byte: setByte
0476:             * <li> Short/short: setShort
0477:             * <li> Integer/integer: setInt
0478:             * <li> Long/long: setLong
0479:             * <li> Float/float: setFloat
0480:             * <li> Double/double: setDouble
0481:             * <li> Character/char: setString
0482:             * <li> Character[]/char[]: setString
0483:             * <li> Byte[]/byte[]: setBytes
0484:             * <li> String: setString
0485:             * <li> java.math.BigDecimal: setBigDecimal
0486:             * <li> java.io.Reader: setCharacterStream
0487:             * <li> java.io.InputStream: setBinaryStream
0488:             * <li> java.util.Date: setTimestamp
0489:             * <li> java.sql.Date: setDate
0490:             * <li> java.sql.Time: setTime
0491:             * <li> java.sql.Timestamp: setTimestamp
0492:             * <li> java.sql.Clob : setClob
0493:             * <li> java.sql.Blob: setBlob
0494:             * </ul>
0495:             * 
0496:             * @param stmt {@link java.sql.PreparedStatement} to have parameters set
0497:             * into
0498:             * @param parameters varargs or Object[] with parameters values
0499:             * @throws RuntimeSQLException if a database access error occurs or this
0500:             * method is called on a closed PreparedStatement; if a parameter type does
0501:             * not have a matching set method (as outlined above)
0502:             * @throws RuntimeIOException if an error occurs while reading data from a
0503:             * Reader or InputStream parameter
0504:             * @since 1.0
0505:             */
0506:            public static void setParameters(final PreparedStatement stmt,
0507:                    final Object[] parameters) {
0508:
0509:                // if no parameters, do nothing
0510:                if (parameters == null || parameters.length == 0) {
0511:                    return;
0512:                }
0513:
0514:                ParameterMetaData stmtMetaData = null;
0515:
0516:                for (int i = 1; i <= parameters.length; i++) {
0517:
0518:                    final Object parameter = parameters[i - 1];
0519:
0520:                    if (parameter == null) {
0521:
0522:                        // lazy assignment of stmtMetaData
0523:                        if (stmtMetaData == null) {
0524:                            try {
0525:                                stmtMetaData = stmt.getParameterMetaData();
0526:                            } catch (SQLException e) {
0527:                                throw new RuntimeSQLException(e);
0528:                            }
0529:                        }
0530:
0531:                        // get sql type from prepared statement metadata
0532:                        int sqlType;
0533:                        try {
0534:                            sqlType = stmtMetaData.getParameterType(i);
0535:                        } catch (SQLException e2) {
0536:                            // feature not supported, use NULL
0537:                            sqlType = java.sql.Types.NULL;
0538:                        }
0539:
0540:                        try {
0541:                            stmt.setNull(i, sqlType);
0542:                        } catch (SQLException e) {
0543:                            throw new RuntimeSQLException(
0544:                                    "Could not set null into parameter [" + i
0545:                                            + "] using java.sql.Types ["
0546:                                            + Log.sqlTypeToString(sqlType)
0547:                                            + "] " + e.getMessage(), e);
0548:                        }
0549:
0550:                        if (Log.isDebugEnabled(Log.PARAMETERS)) {
0551:                            Log.debug(Log.PARAMETERS, "Parameter [" + i
0552:                                    + "] from PreparedStatement [" + stmt
0553:                                    + "] set to [null] using java.sql.Types ["
0554:                                    + Log.sqlTypeToString(sqlType) + "]");
0555:                        }
0556:
0557:                        continue;
0558:                    }
0559:
0560:                    try {
0561:
0562:                        final Class type = parameter.getClass();
0563:
0564:                        if (type == Boolean.class || type == boolean.class) {
0565:                            stmt.setBoolean(i, (Boolean) parameter);
0566:                        } else if (type == Byte.class || type == byte.class) {
0567:                            stmt.setByte(i, (Byte) parameter);
0568:                        } else if (type == Short.class || type == short.class) {
0569:                            stmt.setShort(i, (Short) parameter);
0570:                        } else if (type == Integer.class || type == int.class) {
0571:                            stmt.setInt(i, (Integer) parameter);
0572:                        } else if (type == Long.class || type == long.class) {
0573:                            stmt.setLong(i, (Long) parameter);
0574:                        } else if (type == Float.class || type == float.class) {
0575:                            stmt.setFloat(i, (Float) parameter);
0576:                        } else if (type == Double.class || type == double.class) {
0577:                            stmt.setDouble(i, (Double) parameter);
0578:                        } else if (type == Character.class
0579:                                || type == char.class) {
0580:                            stmt.setString(i, parameter == null ? null : ""
0581:                                    + (Character) parameter);
0582:                        } else if (type == char[].class) {
0583:                            // not efficient, will create a new String object
0584:                            stmt.setString(i, parameter == null ? null
0585:                                    : new String((char[]) parameter));
0586:                        } else if (type == Character[].class) {
0587:                            // not efficient, will duplicate the array and create a new String object
0588:                            final Character[] src = (Character[]) parameter;
0589:                            final char[] dst = new char[src.length];
0590:                            for (int j = 0; j < src.length; j++) { // can't use System.arraycopy here
0591:                                dst[j] = src[j];
0592:                            }
0593:                            stmt.setString(i, new String(dst));
0594:                        } else if (type == String.class) {
0595:                            stmt.setString(i, (String) parameter);
0596:                        } else if (type == BigDecimal.class) {
0597:                            stmt.setBigDecimal(i, (BigDecimal) parameter);
0598:                        } else if (type == byte[].class) {
0599:                            stmt.setBytes(i, (byte[]) parameter);
0600:                        } else if (type == Byte[].class) {
0601:                            // not efficient, will duplicate the array
0602:                            final Byte[] src = (Byte[]) parameter;
0603:                            final byte[] dst = new byte[src.length];
0604:                            for (int j = 0; j < src.length; j++) { // can't use System.arraycopy here
0605:                                dst[j] = src[j];
0606:                            }
0607:                            stmt.setBytes(i, dst);
0608:                        } else if (parameter instanceof  java.io.Reader) {
0609:                            final java.io.Reader reader = (java.io.Reader) parameter;
0610:
0611:                            // the jdbc api for setCharacterStream requires the number
0612:                            // of characters to be read so this will end up reading 
0613:                            // data twice (here and inside the jdbc driver)
0614:                            // besides, the reader must support reset()
0615:                            int size = 0;
0616:                            try {
0617:                                reader.reset();
0618:                                while (reader.read() != -1) {
0619:                                    size++;
0620:                                }
0621:                                reader.reset();
0622:                            } catch (IOException e) {
0623:                                throw new RuntimeIOException(e);
0624:                            }
0625:                            stmt.setCharacterStream(i, reader, size);
0626:                        } else if (parameter instanceof  java.io.InputStream) {
0627:                            final java.io.InputStream inputStream = (java.io.InputStream) parameter;
0628:
0629:                            // the jdbc api for setBinaryStream requires the number of
0630:                            // bytes to be read so this will end up reading the stream 
0631:                            // twice (here and inside the jdbc driver)
0632:                            // besides, the stream must support reset()
0633:                            int size = 0;
0634:                            try {
0635:                                inputStream.reset();
0636:                                while (inputStream.read() != -1) {
0637:                                    size++;
0638:                                }
0639:                                inputStream.reset();
0640:                            } catch (IOException e) {
0641:                                throw new RuntimeIOException(e);
0642:                            }
0643:                            stmt.setBinaryStream(i, inputStream, size);
0644:                        } else if (parameter instanceof  Clob) {
0645:                            stmt.setClob(i, (Clob) parameter);
0646:                        } else if (parameter instanceof  Blob) {
0647:                            stmt.setBlob(i, (Blob) parameter);
0648:                        } else if (type == java.util.Date.class) {
0649:                            final java.util.Date date = (java.util.Date) parameter;
0650:                            stmt.setTimestamp(i, new java.sql.Timestamp(date
0651:                                    .getTime()));
0652:                        } else if (type == java.sql.Date.class) {
0653:                            stmt.setDate(i, (java.sql.Date) parameter);
0654:                        } else if (type == java.sql.Time.class) {
0655:                            stmt.setTime(i, (java.sql.Time) parameter);
0656:                        } else if (type == java.sql.Timestamp.class) {
0657:                            stmt
0658:                                    .setTimestamp(i,
0659:                                            (java.sql.Timestamp) parameter);
0660:                        } else {
0661:                            // last resort; this should cover all database-specific
0662:                            // object types
0663:                            stmt.setObject(i, parameter);
0664:                        }
0665:
0666:                        if (Log.isDebugEnabled(Log.PARAMETERS)) {
0667:                            Log.debug(Log.PARAMETERS, "PreparedStatement ["
0668:                                    + stmt + "] Parameter [" + i + "] type ["
0669:                                    + type.getSimpleName() + "] set to ["
0670:                                    + Log.objectToString(parameter) + "]");
0671:                        }
0672:
0673:                    } catch (SQLException e) {
0674:                        throw new RuntimeSQLException(e);
0675:                    }
0676:                }
0677:            }
0678:
0679:            /**
0680:             * Returns true if the provided class is a type supported natively (as
0681:             * opposed to a bean).
0682:             * 
0683:             * @param type {@link java.lang.Class} type to be tested
0684:             * @since 1.0
0685:             */
0686:            private static boolean isNativeType(final Class type) {
0687:
0688:                // to return an arbitrary object use Object.class
0689:
0690:                return (type == boolean.class || type == Boolean.class
0691:                        || type == byte.class || type == Byte.class
0692:                        || type == short.class || type == Short.class
0693:                        || type == int.class || type == Integer.class
0694:                        || type == long.class || type == Long.class
0695:                        || type == float.class || type == Float.class
0696:                        || type == double.class || type == Double.class
0697:                        || type == char.class || type == Character.class
0698:                        || type == byte[].class || type == Byte[].class
0699:                        || type == char[].class || type == Character[].class
0700:                        || type == String.class || type == BigDecimal.class
0701:                        || type == java.util.Date.class
0702:                        || type == java.sql.Date.class
0703:                        || type == java.sql.Time.class
0704:                        || type == java.sql.Timestamp.class
0705:                        || type == java.io.InputStream.class
0706:                        || type == java.io.Reader.class
0707:                        || type == java.sql.Clob.class
0708:                        || type == java.sql.Blob.class || type == Object.class);
0709:            }
0710:
0711:            /**
0712:             * Reads a column from the current row in the provided
0713:             * {@link java.sql.ResultSet} and returns an instance of the specified Java
0714:             * {@link java.lang.Class} containing the values read.
0715:             * <p>
0716:             * This method is used while converting {@link java.sql.ResultSet} rows to
0717:             * objects. The class type is the field type in the target bean.
0718:             * <p>
0719:             * Correspondence between class types and ResultSet.get methods is as
0720:             * follows:
0721:             * <ul>
0722:             * <li> Boolean/boolean: getBoolean
0723:             * <li> Byte/byte: getByte
0724:             * <li> Short/short: getShort
0725:             * <li> Integer/int: getInt
0726:             * <li> Long/long: getLong
0727:             * <li> Float/float: getFloat
0728:             * <li> Double/double: getDouble
0729:             * <li> Character/char: getString
0730:             * <li> Character[]/char[]: getString
0731:             * <li> Byte[]/byte[]: setBytes
0732:             * <li> String: setString
0733:             * <li> java.math.BigDecimal: getBigDecimal
0734:             * <li> java.io.Reader: getCharacterStream
0735:             * <li> java.io.InputStream: getBinaryStream
0736:             * <li> java.util.Date: getTimestamp
0737:             * <li> java.sql.Date: getDate
0738:             * <li> java.sql.Time: getTime
0739:             * <li> java.sql.Timestamp: getTimestamp
0740:             * <li> java.sql.Clob: getClob
0741:             * <li> java.sql.Blob: getBlob
0742:             * </ul>
0743:             * <p>
0744:             * null's will be respected for any non-native types. This means that if a
0745:             * field is of type Integer it will be able to receive a null value from the
0746:             * ResultSet; on the other hand, if a field is of type int it will receive 0
0747:             * for a null value from the {@link java.sql.ResultSet}.
0748:             * 
0749:             * @param resultSet {@link java.sql.ResultSet} (positioned in the row to be
0750:             * processed)
0751:             * @param column column index in the result set (starting with 1)
0752:             * @param type {@link java.lang.Class} of the object to be returned
0753:             * @since 1.0
0754:             */
0755:            public static Object getValueFromResultSet(
0756:                    final ResultSet resultSet, final int column,
0757:                    final Class type) {
0758:
0759:                Object value = null;
0760:
0761:                try {
0762:
0763:                    if (type == boolean.class) {
0764:                        value = resultSet.getBoolean(column);
0765:                    } else if (type == Boolean.class) {
0766:                        value = resultSet.getObject(column) == null ? null
0767:                                : resultSet.getBoolean(column);
0768:                    } else if (type == byte.class) {
0769:                        value = resultSet.getByte(column);
0770:                    } else if (type == Byte.class) {
0771:                        value = resultSet.getObject(column) == null ? null
0772:                                : resultSet.getByte(column);
0773:                    } else if (type == short.class) {
0774:                        value = resultSet.getShort(column);
0775:                    } else if (type == Short.class) {
0776:                        value = resultSet.getObject(column) == null ? null
0777:                                : resultSet.getShort(column);
0778:                    } else if (type == int.class) {
0779:                        value = resultSet.getInt(column);
0780:                    } else if (type == Integer.class) {
0781:                        value = resultSet.getObject(column) == null ? null
0782:                                : resultSet.getInt(column);
0783:                    } else if (type == long.class) {
0784:                        value = resultSet.getLong(column);
0785:                    } else if (type == Long.class) {
0786:                        value = resultSet.getObject(column) == null ? null
0787:                                : resultSet.getLong(column);
0788:                    } else if (type == float.class) {
0789:                        value = resultSet.getFloat(column);
0790:                    } else if (type == Float.class) {
0791:                        value = resultSet.getObject(column) == null ? null
0792:                                : resultSet.getFloat(column);
0793:                    } else if (type == double.class) {
0794:                        value = resultSet.getDouble(column);
0795:                    } else if (type == Double.class) {
0796:                        value = resultSet.getObject(column) == null ? null
0797:                                : resultSet.getDouble(column);
0798:                    } else if (type == BigDecimal.class) {
0799:                        value = resultSet.getObject(column) == null ? null
0800:                                : resultSet.getBigDecimal(column);
0801:                    } else if (type == String.class) {
0802:                        value = resultSet.getString(column);
0803:                    } else if (type == Character.class || type == char.class) {
0804:                        final String str = resultSet.getString(column);
0805:                        if (str != null && str.length() > 1) {
0806:                            throw new PersistException("Column [" + column
0807:                                    + "] returned a string with length ["
0808:                                    + str.length() + "] but field type ["
0809:                                    + type.getSimpleName()
0810:                                    + "] can only accept 1 character");
0811:                        }
0812:                        value = (str == null || str.length() == 0) ? null : str
0813:                                .charAt(0);
0814:                    } else if (type == byte[].class || type == Byte[].class) {
0815:                        value = resultSet.getBytes(column);
0816:                    } else if (type == char[].class
0817:                            || type == Character[].class) {
0818:                        final String str = resultSet.getString(column);
0819:                        value = (str == null) ? null : str.toCharArray();
0820:                    } else if (type == java.util.Date.class) {
0821:                        final java.sql.Timestamp timestamp = resultSet
0822:                                .getTimestamp(column);
0823:                        value = (timestamp == null) ? null
0824:                                : new java.util.Date(timestamp.getTime());
0825:                    } else if (type == java.sql.Date.class) {
0826:                        value = resultSet.getDate(column);
0827:                    } else if (type == java.sql.Time.class) {
0828:                        value = resultSet.getTime(column);
0829:                    } else if (type == java.sql.Timestamp.class) {
0830:                        value = resultSet.getTimestamp(column);
0831:                    } else if (type == java.io.InputStream.class) {
0832:                        value = resultSet.getBinaryStream(column);
0833:                    } else if (type == java.io.Reader.class) {
0834:                        value = resultSet.getCharacterStream(column);
0835:                    } else if (type == java.sql.Clob.class) {
0836:                        value = resultSet.getClob(column);
0837:                    } else if (type == java.sql.Blob.class) {
0838:                        value = resultSet.getBlob(column);
0839:                    } else {
0840:                        // this will work for database-specific types
0841:                        value = resultSet.getObject(column);
0842:                    }
0843:
0844:                } catch (SQLException e) {
0845:                    throw new RuntimeSQLException(e);
0846:                }
0847:
0848:                if (Log.isDebugEnabled(Log.RESULTS)) {
0849:                    Log.debug(Log.RESULTS, "Read ResultSet ["
0850:                            + resultSet
0851:                            + "] column ["
0852:                            + column
0853:                            + "]"
0854:                            + (value == null ? "" : " type ["
0855:                                    + value.getClass().getSimpleName() + "]")
0856:                            + " value [" + Log.objectToString(value) + "]");
0857:                }
0858:
0859:                return value;
0860:            }
0861:
0862:            /**
0863:             * Reads a column from the current row in the provided
0864:             * {@link java.sql.ResultSet} and return a value correspondent to the SQL
0865:             * type provided (as defined in {@link java.sql.Types java.sql.Types}).
0866:             * <p>
0867:             * This method is used while converting results sets to maps. The SQL type
0868:             * comes from the {@link java.sql.ResultSetMetaData ResultSetMetaData} for a
0869:             * given column.
0870:             * <p>
0871:             * Correspondence between {@link java.sql.Types java.sql.Types} and
0872:             * ResultSet.get methods is as follows:
0873:             * <ul>
0874:             * <li> ARRAY: getArray
0875:             * <li> BIGINT: getLong
0876:             * <li> BIT: getBoolean
0877:             * <li> BLOB: getBytes
0878:             * <li> BOOLEAN: getBoolean
0879:             * <li> CHAR: getString
0880:             * <li> CLOB: getString
0881:             * <li> DATALINK: getBinaryStream
0882:             * <li> DATE: getDate
0883:             * <li> DECIMAL: getBigDecimal
0884:             * <li> DOUBLE: getDouble
0885:             * <li> FLOAT: getFloat
0886:             * <li> INTEGER: getInt
0887:             * <li> JAVA_OBJECT: getObject
0888:             * <li> LONGVARBINARY: getBytes
0889:             * <li> LONGVARCHAR: getString
0890:             * <li> NULL: getNull
0891:             * <li> NCHAR: getString
0892:             * <li> NUMERIC: getBigDecimal
0893:             * <li> OTHER: getObject
0894:             * <li> REAL: getDouble
0895:             * <li> REF: getRef
0896:             * <li> SMALLINT: getInt
0897:             * <li> TIME: getTime
0898:             * <li> TIMESTAMP: getTimestamp
0899:             * <li> TINYINT: getInt
0900:             * <li> VARBINARY: getBytes
0901:             * <li> VARCHAR: getString
0902:             * <li> [Oracle specific] 100: getFloat
0903:             * <li> [Oracle specific] 101: getDouble
0904:             * </ul>
0905:             * <p>
0906:             * null's are respected for all types. This means that if a column is of
0907:             * type LONG and its value comes from the database as null, this method will
0908:             * return null for it.
0909:             * 
0910:             * @param resultSet {@link java.sql.ResultSet} (positioned in the row to be
0911:             * processed)
0912:             * @param column Column index in the result set (starting with 1)
0913:             * @param type type of the column (as defined in
0914:             * {@link java.sql.Types java.sql.Types})
0915:             * @since 1.0
0916:             */
0917:            public static Object getValueFromResultSet(
0918:                    final ResultSet resultSet, final int column, final int type) {
0919:
0920:                Object value = null;
0921:
0922:                try {
0923:
0924:                    if (type == java.sql.Types.ARRAY) {
0925:                        value = resultSet.getArray(column);
0926:                    } else if (type == java.sql.Types.BIGINT) {
0927:                        value = resultSet.getObject(column) == null ? null
0928:                                : resultSet.getLong(column);
0929:                    } else if (type == java.sql.Types.BINARY) {
0930:                        value = resultSet.getBytes(column);
0931:                    } else if (type == java.sql.Types.BIT) {
0932:                        value = resultSet.getObject(column) == null ? null
0933:                                : resultSet.getBoolean(column);
0934:                    } else if (type == java.sql.Types.BLOB) {
0935:                        value = resultSet.getBytes(column);
0936:                    } else if (type == java.sql.Types.BOOLEAN) {
0937:                        value = resultSet.getObject(column) == null ? null
0938:                                : resultSet.getBoolean(column);
0939:                    } else if (type == java.sql.Types.CHAR) {
0940:                        value = resultSet.getString(column);
0941:                    } else if (type == java.sql.Types.CLOB) {
0942:                        value = resultSet.getString(column);
0943:                    } else if (type == java.sql.Types.DATALINK) {
0944:                        value = resultSet.getBinaryStream(column);
0945:                    } else if (type == java.sql.Types.DATE) {
0946:                        value = resultSet.getDate(column);
0947:                    } else if (type == java.sql.Types.DECIMAL) {
0948:                        value = resultSet.getBigDecimal(column);
0949:                    } else if (type == java.sql.Types.DOUBLE) {
0950:                        value = resultSet.getObject(column) == null ? null
0951:                                : resultSet.getDouble(column);
0952:                    } else if (type == java.sql.Types.FLOAT) {
0953:                        value = resultSet.getObject(column) == null ? null
0954:                                : resultSet.getFloat(column);
0955:                    } else if (type == java.sql.Types.INTEGER) {
0956:                        value = resultSet.getObject(column) == null ? null
0957:                                : resultSet.getInt(column);
0958:                    } else if (type == java.sql.Types.JAVA_OBJECT) {
0959:                        value = resultSet.getObject(column);
0960:                    } else if (type == java.sql.Types.LONGVARBINARY) {
0961:                        value = resultSet.getBytes(column);
0962:                    } else if (type == java.sql.Types.LONGVARCHAR) {
0963:                        value = resultSet.getString(column);
0964:                    } else if (type == java.sql.Types.NULL) {
0965:                        value = null;
0966:                    } else if (type == java.sql.Types.NUMERIC) {
0967:                        value = resultSet.getBigDecimal(column);
0968:                    } else if (type == java.sql.Types.OTHER) {
0969:                        value = resultSet.getObject(column);
0970:                    } else if (type == java.sql.Types.REAL) {
0971:                        value = resultSet.getObject(column) == null ? null
0972:                                : resultSet.getDouble(column);
0973:                    } else if (type == java.sql.Types.REF) {
0974:                        value = resultSet.getRef(column);
0975:                    } else if (type == java.sql.Types.SMALLINT) {
0976:                        value = resultSet.getObject(column) == null ? null
0977:                                : resultSet.getInt(column);
0978:                    } else if (type == java.sql.Types.TIME) {
0979:                        value = resultSet.getTime(column);
0980:                    } else if (type == java.sql.Types.TIMESTAMP) {
0981:                        value = resultSet.getTimestamp(column);
0982:                    } else if (type == java.sql.Types.TINYINT) {
0983:                        value = resultSet.getObject(column) == null ? null
0984:                                : resultSet.getInt(column);
0985:                    } else if (type == java.sql.Types.VARBINARY) {
0986:                        value = resultSet.getBytes(column);
0987:                    } else if (type == java.sql.Types.VARCHAR) {
0988:                        value = resultSet.getString(column);
0989:                    }
0990:
0991:                    // oracle specific
0992:                    else if (type == 100) {
0993:                        value = resultSet.getObject(column) == null ? null
0994:                                : resultSet.getFloat(column);
0995:                    } else if (type == 101) {
0996:                        value = resultSet.getObject(column) == null ? null
0997:                                : resultSet.getDouble(column);
0998:                    }
0999:
1000:                    else {
1001:                        throw new PersistException(
1002:                                "Could not get value for result set using type ["
1003:                                        + Log.sqlTypeToString(type)
1004:                                        + "] on column [" + column + "]");
1005:                    }
1006:
1007:                } catch (SQLException e) {
1008:                    throw new RuntimeSQLException(e);
1009:                }
1010:
1011:                if (Log.isDebugEnabled(Log.RESULTS)) {
1012:                    Log.debug(Log.RESULTS, "Read ResultSet ["
1013:                            + resultSet
1014:                            + "] column ["
1015:                            + column
1016:                            + "] sql type ["
1017:                            + Log.sqlTypeToString(type)
1018:                            + "]"
1019:                            + (value == null ? "" : " type ["
1020:                                    + value.getClass().getSimpleName() + "]")
1021:                            + " value [" + Log.objectToString(value) + "]");
1022:                }
1023:
1024:                return value;
1025:            }
1026:
1027:            /**
1028:             * Returns a list of values for the fields in the provided object that match
1029:             * the provided list of columns according with the object mapping.
1030:             * 
1031:             * @param object source object to obtain parameter values
1032:             * @param columns name of the database columns to get parameters for
1033:             * @param mapping mapping for the object class
1034:             * @since 1.0
1035:             */
1036:            private static Object[] getParametersFromObject(
1037:                    final Object object, final String[] columns,
1038:                    final TableMapping mapping) {
1039:
1040:                Object[] parameters = new Object[columns.length];
1041:                for (int i = 0; i < columns.length; i++) {
1042:                    final String columnName = columns[i];
1043:                    final Method getter = mapping
1044:                            .getGetterForColumn(columnName);
1045:
1046:                    Object value = null;
1047:                    try {
1048:                        value = getter.invoke(object, new Object[] {});
1049:                    } catch (Exception e) {
1050:                        throw new PersistException(
1051:                                "Could not access getter for column ["
1052:                                        + columnName + "]", e);
1053:                    }
1054:
1055:                    parameters[i] = value;
1056:                }
1057:
1058:                return parameters;
1059:            }
1060:
1061:            /**
1062:             * Reads a row from the provided {@link java.sql.ResultSet} and converts it
1063:             * to an object instance of the given class.
1064:             * <p>
1065:             * See {@link #getValueFromResultSet(ResultSet, int, Class)} for details on
1066:             * the mappings between ResultSet.get methods and Java types.
1067:             * 
1068:             * @param objectClass type of the object to be returned
1069:             * @param resultSet {@link java.sql.ResultSet} (positioned in the row to be
1070:             * processed)
1071:             * @see #isNativeType(Class)
1072:             * @see #getValueFromResultSet(ResultSet, int, Class)
1073:             * @since 1.0
1074:             */
1075:            public Object loadObject(final Class objectClass,
1076:                    final ResultSet resultSet) throws SQLException {
1077:
1078:                final ResultSetMetaData resultSetMetaData = resultSet
1079:                        .getMetaData();
1080:
1081:                Object ret = null;
1082:
1083:                // for native objects (int, long, String, Date, etc.)
1084:                if (isNativeType(objectClass)) {
1085:                    if (resultSetMetaData.getColumnCount() != 1) {
1086:                        throw new PersistException(
1087:                                "ResultSet returned ["
1088:                                        + resultSetMetaData.getColumnCount()
1089:                                        + "] columns but 1 column was expected to load data into an instance of ["
1090:                                        + objectClass.getName() + "]");
1091:                    }
1092:                    ret = getValueFromResultSet(resultSet, 1, objectClass);
1093:                }
1094:
1095:                // for beans
1096:                else {
1097:
1098:                    final Mapping mapping = getMapping(objectClass);
1099:
1100:                    try {
1101:                        ret = objectClass.newInstance();
1102:                    } catch (Exception e) {
1103:                        throw new PersistException(e);
1104:                    }
1105:
1106:                    for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
1107:                        final String columnName = resultSetMetaData
1108:                                .getColumnName(i).toLowerCase();
1109:                        final Method setter = mapping
1110:                                .getSetterForColumn(columnName);
1111:                        if (setter == null) {
1112:                            throw new PersistException(
1113:                                    "Column ["
1114:                                            + columnName
1115:                                            + "] from result set does not have a mapping to a field in ["
1116:                                            + objectClass.getName() + "]");
1117:                        }
1118:
1119:                        final Class type = setter.getParameterTypes()[0];
1120:                        final Object value = getValueFromResultSet(resultSet,
1121:                                i, type);
1122:
1123:                        try {
1124:                            setter.invoke(ret, new Object[] { value });
1125:                        } catch (Exception e) {
1126:                            throw new PersistException("Error setting value ["
1127:                                    + value
1128:                                    + "]"
1129:                                    + (value == null ? "" : " of type ["
1130:                                            + value.getClass().getName() + "]")
1131:                                    + " from column [" + columnName
1132:                                    + "] using setter [" + setter + "]: "
1133:                                    + e.getMessage(), e);
1134:                        }
1135:                    }
1136:
1137:                }
1138:
1139:                return ret;
1140:            }
1141:
1142:            /**
1143:             * Reads a row from the provided {@link java.sql.ResultSet} and converts it
1144:             * to a map having the column names as keys and results as values.
1145:             * <p>
1146:             * See {@link #getValueFromResultSet(ResultSet, int, int)} for details on
1147:             * the mappings between ResultSet.get methods and
1148:             * {@link java.sql.Types java.sql.Types} types
1149:             * 
1150:             * @param resultSet {@link java.sql.ResultSet} (positioned in the row to be
1151:             * processed)
1152:             * @since 1.0
1153:             */
1154:            public static Map<String, Object> loadMap(final ResultSet resultSet)
1155:                    throws SQLException {
1156:
1157:                final Map ret = new LinkedHashMap();
1158:                final ResultSetMetaData resultSetMetaData = resultSet
1159:                        .getMetaData();
1160:
1161:                for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
1162:                    final String columnName = resultSetMetaData
1163:                            .getColumnName(i).toLowerCase();
1164:                    final int type = resultSetMetaData.getColumnType(i);
1165:                    final Object value = getValueFromResultSet(resultSet, i,
1166:                            type);
1167:                    ret.put(columnName, value);
1168:                }
1169:
1170:                return ret;
1171:            }
1172:
1173:            /**
1174:             * Set auto-generated keys (returned from an insert operation) into an
1175:             * object.
1176:             * 
1177:             * @param object {@link java.lang.Object} to have fields mapped to
1178:             * auto-generated keys set
1179:             * @param result {@link Result} containing auto-generated keys
1180:             * @since 1.0
1181:             */
1182:            public void setAutoGeneratedKeys(final Object object,
1183:                    final Result result) {
1184:
1185:                if (result.getGeneratedKeys().size() != 0) {
1186:                    final TableMapping mapping = getTableMapping(object
1187:                            .getClass(), "setAutoGeneratedKeys()");
1188:                    for (int i = 0; i < mapping.getAutoGeneratedColumns().length; i++) {
1189:                        final String columnName = mapping
1190:                                .getAutoGeneratedColumns()[i];
1191:                        final Method setter = mapping
1192:                                .getSetterForColumn(columnName);
1193:                        final Object key = result.getGeneratedKeys().get(i);
1194:                        try {
1195:                            setter.invoke(object, new Object[] { key });
1196:                        } catch (Exception e) {
1197:                            throw new PersistException(
1198:                                    "Could not invoke setter [" + setter
1199:                                            + "] with auto generated key ["
1200:                                            + key + "] of class ["
1201:                                            + key.getClass().getName() + "]", e);
1202:                        }
1203:                    }
1204:                }
1205:
1206:            }
1207:
1208:            // ---------- execute ----------
1209:
1210:            /**
1211:             * Executes an update and return a {@link Result} object containing the
1212:             * number of rows modified and auto-generated keys produced.
1213:             * <p>
1214:             * Parameters will be set according with the correspondence defined in
1215:             * {@link #setParameters(PreparedStatement, int[], Object[])}
1216:             * 
1217:             * @param objectClass Class of the object related with the query. Used to
1218:             * determine the types of the auto-incremented keys. Only important if
1219:             * autoGeneratedKeys contains values.
1220:             * @param sql SQL code to be executed.
1221:             * @param autoGeneratedKeys List of columns that are going to be
1222:             * auto-generated during the query execution.
1223:             * @param parameters Parameters to be used in the PreparedStatement.
1224:             * @since 1.0
1225:             */
1226:            public Result executeUpdate(final Class objectClass,
1227:                    final String sql, final String[] autoGeneratedKeys,
1228:                    final Object... parameters) {
1229:
1230:                long begin = 0;
1231:                if (Log.isDebugEnabled(Log.PROFILING)) {
1232:                    begin = System.currentTimeMillis();
1233:                }
1234:
1235:                final PreparedStatement stmt = getPreparedStatement(sql,
1236:                        autoGeneratedKeys);
1237:
1238:                setParameters(stmt, parameters);
1239:
1240:                int rowsModified = 0;
1241:                try {
1242:                    rowsModified = stmt.executeUpdate();
1243:                } catch (SQLException e) {
1244:                    throw new RuntimeSQLException("Error executing sql [" + sql
1245:                            + "] with parameters "
1246:                            + Arrays.toString(parameters) + ": "
1247:                            + e.getMessage(), e);
1248:                }
1249:
1250:                final List generatedKeys = new ArrayList();
1251:                if (autoGeneratedKeys.length != 0) {
1252:                    try {
1253:                        final Mapping mapping = getMapping(objectClass);
1254:                        final ResultSet resultSet = stmt.getGeneratedKeys();
1255:                        for (int i = 0; i < autoGeneratedKeys.length; i++) {
1256:                            resultSet.next();
1257:
1258:                            // get the auto-generated key using the ResultSet.get method
1259:                            // that matches
1260:                            // the bean setter parameter type
1261:                            final Method setter = mapping
1262:                                    .getSetterForColumn(autoGeneratedKeys[i]);
1263:                            final Class type = setter.getParameterTypes()[0];
1264:                            final Object value = Persist.getValueFromResultSet(
1265:                                    resultSet, 1, type);
1266:
1267:                            generatedKeys.add(value);
1268:                        }
1269:                        resultSet.close();
1270:                    } catch (SQLException e) {
1271:                        throw new RuntimeSQLException(
1272:                                "This JDBC driver does not support PreparedStatement.getGeneratedKeys()."
1273:                                        + " Please use setUpdateAutoGeneratedKeys(false) in your Persist instance"
1274:                                        + " to disable attempts to use that feature");
1275:                    }
1276:                }
1277:
1278:                Result result = new Result(rowsModified, generatedKeys);
1279:
1280:                if (Log.isDebugEnabled(Log.PROFILING)) {
1281:                    final long end = System.currentTimeMillis();
1282:                    Log.debug(Log.PROFILING, "executeUpdate in ["
1283:                            + (end - begin) + "ms] for sql [" + sql + "]");
1284:                }
1285:
1286:                return result;
1287:            }
1288:
1289:            /**
1290:             * Executes an update and returns the number of rows modified.
1291:             * <p>
1292:             * Parameters will be set according with the correspondence defined in
1293:             * {@link #setParameters(PreparedStatement, int[], Object[])}
1294:             * 
1295:             * @param sql SQL code to be executed.
1296:             * @param parameters Parameters to be used in the PreparedStatement.
1297:             * @since 1.0
1298:             */
1299:            public int executeUpdate(final String sql,
1300:                    final Object... parameters) {
1301:
1302:                final PreparedStatement stmt = getPreparedStatement(sql);
1303:                int rowsModified = 0;
1304:
1305:                try {
1306:                    setParameters(stmt, parameters);
1307:                    rowsModified = stmt.executeUpdate();
1308:                } catch (SQLException e) {
1309:                    throw new RuntimeSQLException("Error executing sql [" + sql
1310:                            + "] with parameters "
1311:                            + Arrays.toString(parameters) + ": "
1312:                            + e.getMessage(), e);
1313:                }
1314:
1315:                closePreparedStatement(stmt);
1316:                return rowsModified;
1317:            }
1318:
1319:            // ---------- insert ----------
1320:
1321:            /**
1322:             * Inserts an object into the database.
1323:             * 
1324:             * @since 1.0
1325:             */
1326:            public int insert(final Object object) {
1327:                final TableMapping mapping = getTableMapping(object.getClass(),
1328:                        "insert()");
1329:                final String sql = mapping.getInsertSql();
1330:                final String[] columns = mapping.getNotAutoGeneratedColumns();
1331:                final Object[] parameters = getParametersFromObject(object,
1332:                        columns, mapping);
1333:
1334:                int ret = 0;
1335:                if (updateAutoGeneratedKeys) {
1336:                    if (mapping.supportsGetGeneratedKeys()) {
1337:                        final Result result = executeUpdate(object.getClass(),
1338:                                sql, mapping.getAutoGeneratedColumns(),
1339:                                parameters);
1340:                        setAutoGeneratedKeys(object, result);
1341:                        ret = result.getRowsModified();
1342:                    } else {
1343:                        throw new PersistException(
1344:                                "While inserting instance of ["
1345:                                        + object.getClass().getName()
1346:                                        + "] autoUpdateGeneratedKeys is set to [true] but the database doesn't support this feature");
1347:                    }
1348:                } else {
1349:                    ret = executeUpdate(sql, parameters);
1350:                }
1351:                return ret;
1352:            }
1353:
1354:            /**
1355:             * Inserts a batch of objects into the database.
1356:             * 
1357:             * @since 1.0
1358:             */
1359:            // TODO: use batch updates
1360:            public int[] insertBatch(final Object... objects) {
1361:                if (objects == null || objects.length == 0) {
1362:                    return new int[0];
1363:                }
1364:                final int[] results = new int[objects.length];
1365:                for (int i = 0; i < objects.length; i++) {
1366:                    results[i] = insert(objects[i]);
1367:                }
1368:                return results;
1369:            }
1370:
1371:            // ---------- update ----------
1372:
1373:            /**
1374:             * Updates an object in the database. The object will be identified using
1375:             * its mapped table's primary key. If no primary keys are defined in the
1376:             * mapped table, a {@link PersistException} will be thrown.
1377:             * 
1378:             * @since 1.0
1379:             */
1380:            public int update(final Object object) {
1381:                final TableMapping mapping = getTableMapping(object.getClass(),
1382:                        "update()");
1383:
1384:                if (mapping.getPrimaryKeys().length == 0) {
1385:                    throw new PersistException("Table "
1386:                            + mapping.getTableName()
1387:                            + " doesn't have a primary key");
1388:                }
1389:                final String sql = mapping.getUpdateSql();
1390:                final String[] columns = new String[mapping.getNotPrimaryKeys().length
1391:                        + mapping.getPrimaryKeys().length];
1392:                int i = 0;
1393:                for (String notPrimaryKey : mapping.getNotPrimaryKeys()) {
1394:                    columns[i++] = notPrimaryKey;
1395:                }
1396:                for (String primaryKey : mapping.getPrimaryKeys()) {
1397:                    columns[i++] = primaryKey;
1398:                }
1399:                final Object[] parameters = getParametersFromObject(object,
1400:                        columns, mapping);
1401:                return executeUpdate(sql, parameters);
1402:            }
1403:
1404:            /**
1405:             * Updates a batch of objects in the database. The objects will be
1406:             * identified using their mapped table's primary keys. If no primary keys
1407:             * are defined in the mapped table, a {@link PersistException} will be
1408:             * thrown.
1409:             * 
1410:             * @since 1.0
1411:             */
1412:            public int[] updateBatch(final Object... objects) {
1413:                // TODO: use batch updates
1414:                if (objects == null || objects.length == 0) {
1415:                    return new int[0];
1416:                }
1417:                int[] results = new int[objects.length];
1418:                for (int i = 0; i < objects.length; i++) {
1419:                    results[i] = update(objects[i]);
1420:                }
1421:                return results;
1422:            }
1423:
1424:            // ---------- delete ----------
1425:
1426:            /**
1427:             * Deletes an object in the database. The object will be identified using
1428:             * its mapped table's primary key. If no primary keys are defined in the
1429:             * mapped table, a PersistException will be thrown.
1430:             * 
1431:             * @since 1.0
1432:             */
1433:            public int delete(final Object object) {
1434:                final TableMapping mapping = getTableMapping(object.getClass(),
1435:                        "delete()");
1436:                if (mapping.getPrimaryKeys().length == 0) {
1437:                    throw new PersistException("Table "
1438:                            + mapping.getTableName()
1439:                            + " doesn't have a primary key");
1440:                }
1441:                final String sql = mapping.getDeleteSql();
1442:                final String[] columns = mapping.getPrimaryKeys();
1443:                final Object[] parameters = getParametersFromObject(object,
1444:                        columns, mapping);
1445:                return executeUpdate(sql, parameters);
1446:            }
1447:
1448:            /**
1449:             * Updates a batch of objects in the database. The objects will be
1450:             * identified using their matched table's primary keys. If no primary keys
1451:             * are defined in a given object, a PersistException will be thrown.
1452:             * 
1453:             * @since 1.0
1454:             */
1455:            public int[] deleteBatch(final Object... objects) {
1456:                // TODO: use batch updates
1457:                if (objects == null || objects.length == 0) {
1458:                    return new int[0];
1459:                }
1460:                int[] results = new int[objects.length];
1461:                for (int i = 0; i < objects.length; i++) {
1462:                    results[i] = delete(objects[i]);
1463:                }
1464:                return results;
1465:            }
1466:
1467:            // ---------- read ----------
1468:
1469:            // --- single objects ---
1470:
1471:            /**
1472:             * Reads a single object from the database by mapping the results of the SQL
1473:             * query into an instance of the given object class. Only the columns
1474:             * returned from the SQL query will be set into the object instance. If a
1475:             * given column can't be mapped to the target object instance, a
1476:             * {@link PersistException} will be thrown.
1477:             * 
1478:             * @since 1.0
1479:             */
1480:            public <T> T read(final Class<T> objectClass, final String sql) {
1481:                return read(objectClass, sql, (Object[]) null);
1482:            }
1483:
1484:            /**
1485:             * Reads a single object from the database by mapping the results of the
1486:             * parameterized SQL query into an instance 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 PersistException} 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> T read(final Class<T> objectClass, final String sql,
1497:                    final Object... parameters) {
1498:                final PreparedStatement stmt = getPreparedStatement(sql);
1499:                return read(objectClass, stmt, parameters);
1500:            }
1501:
1502:            /**
1503:             * Reads a single object from the database by mapping the results of the
1504:             * execution of the given PreparedStatement into an instance of the given
1505:             * object class. Only the columns returned from the PreparedStatement
1506:             * execution will be set into the object instance. If a given column can't
1507:             * be mapped to the target object instance, a {@link PersistException}
1508:             * will be thrown.
1509:             * <p>
1510:             * Parameters will be set according with the correspondence defined in
1511:             * {@link #setParameters(PreparedStatement, int[], Object[])}
1512:             * 
1513:             * @since 1.0
1514:             */
1515:            public <T> T read(final Class<T> objectClass,
1516:                    final PreparedStatement statement,
1517:                    final Object... parameters) {
1518:                setParameters(statement, parameters);
1519:                try {
1520:                    final ResultSet resultSet = statement.executeQuery();
1521:                    final T ret = read(objectClass, resultSet);
1522:                    if (closePreparedStatementsAfterRead) {
1523:                        closePreparedStatement(statement);
1524:                    }
1525:                    return ret;
1526:                } catch (SQLException e) {
1527:                    throw new RuntimeSQLException(e);
1528:                }
1529:            }
1530:
1531:            /**
1532:             * Reads a single object from the database by mapping the content of the
1533:             * ResultSet current row into an instance of the given object class. Only
1534:             * columns contained in the ResultSet will be set into the object instance.
1535:             * If a given column can't be mapped to the target object instance, a
1536:             * PersistException will be thrown.
1537:             * 
1538:             * @since 1.0
1539:             */
1540:            public <T> T read(final Class<T> objectClass,
1541:                    final ResultSet resultSet) {
1542:                long begin = 0;
1543:                if (Log.isDebugEnabled(Log.PROFILING)) {
1544:                    begin = System.currentTimeMillis();
1545:                }
1546:
1547:                Object ret = null;
1548:                try {
1549:                    if (resultSet.next()) {
1550:                        ret = loadObject(objectClass, resultSet);
1551:                        if (resultSet.next()) {
1552:                            throw new PersistException(
1553:                                    "Non-unique result returned");
1554:                        }
1555:                    }
1556:                } catch (SQLException e) {
1557:                    throw new RuntimeSQLException(e);
1558:                }
1559:
1560:                if (Log.isDebugEnabled(Log.PROFILING)) {
1561:                    final long end = System.currentTimeMillis();
1562:                    Log.debug(Log.PROFILING, "read in [" + (end - begin)
1563:                            + "ms] for object type ["
1564:                            + objectClass.getSimpleName() + "]");
1565:                }
1566:
1567:                return (T) ret;
1568:            }
1569:
1570:            /**
1571:             * Reads an object from the database by its primary keys.
1572:             * 
1573:             * @since 1.0
1574:             */
1575:            public <T> T readByPrimaryKey(final Class<T> objectClass,
1576:                    final Object... primaryKeyValues) {
1577:                final TableMapping mapping = getTableMapping(objectClass,
1578:                        "readByPrimaryKey()");
1579:                final String sql = mapping.getSelectSql();
1580:                return read(objectClass, sql, primaryKeyValues);
1581:            }
1582:
1583:            // --- lists ---
1584:
1585:            /**
1586:             * Reads a list of objects from the database by mapping the content of the
1587:             * ResultSet into instances of the given object class. Only columns
1588:             * contained in the ResultSet will be set into the object instances. If a
1589:             * given column can't be mapped to a target object instance, a
1590:             * PersistException will be thrown.
1591:             * 
1592:             * @since 1.0
1593:             */
1594:            public <T> List<T> readList(final Class<T> objectClass,
1595:                    final ResultSet resultSet) {
1596:
1597:                long begin = 0;
1598:                if (Log.isDebugEnabled(Log.PROFILING)) {
1599:                    begin = System.currentTimeMillis();
1600:                }
1601:
1602:                final List<T> ret = new ArrayList();
1603:                try {
1604:                    while (resultSet.next()) {
1605:                        ret.add((T) loadObject(objectClass, resultSet));
1606:                    }
1607:                } catch (SQLException e) {
1608:                    throw new RuntimeSQLException(e);
1609:                }
1610:
1611:                if (Log.isDebugEnabled(Log.PROFILING)) {
1612:                    final long end = System.currentTimeMillis();
1613:                    Log.debug(Log.PROFILING, "readList in [" + (end - begin)
1614:                            + "ms] for object type ["
1615:                            + objectClass.getSimpleName() + "]");
1616:                }
1617:
1618:                return ret;
1619:            }
1620:
1621:            /**
1622:             * Reads a list of objects from the database by mapping the results of the
1623:             * execution of the given PreparedStatement into instances of the given
1624:             * object class. Only the columns returned from the PreparedStatement
1625:             * execution will be set into the object instances. If a given column can't
1626:             * be mapped to a target object instance, a PersistException will be
1627:             * thrown.
1628:             * <p>
1629:             * Parameters will be set according with the correspondence defined in
1630:             * {@link #setParameters(PreparedStatement, int[], Object[])}
1631:             * 
1632:             * @since 1.0
1633:             */
1634:            public <T> List<T> readList(final Class<T> objectClass,
1635:                    final PreparedStatement statement,
1636:                    final Object... parameters) {
1637:                setParameters(statement, parameters);
1638:                try {
1639:                    final ResultSet resultSet = statement.executeQuery();
1640:                    return readList(objectClass, resultSet);
1641:                } catch (SQLException e) {
1642:                    throw new RuntimeSQLException(e);
1643:                }
1644:            }
1645:
1646:            /**
1647:             * Reads a list of objects from the database by mapping the results of the
1648:             * parameterized SQL query into instances of the given object class. Only
1649:             * the columns returned from the SQL query will be set into the object
1650:             * instance. If a given column can't be mapped to the target object
1651:             * instance, a {@link PersistException} will be thrown.
1652:             * <p>
1653:             * Parameters will be set according with the correspondence defined in
1654:             * {@link #setParameters(PreparedStatement, int[], Object[])}
1655:             * 
1656:             * @since 1.0
1657:             */
1658:            public <T> List<T> readList(final Class<T> objectClass,
1659:                    final String sql, final Object... parameters) {
1660:                final PreparedStatement stmt = getPreparedStatement(sql);
1661:                final List ret = readList(objectClass, stmt, parameters);
1662:                if (closePreparedStatementsAfterRead) {
1663:                    closePreparedStatement(stmt);
1664:                }
1665:                return ret;
1666:            }
1667:
1668:            /**
1669:             * Reads a list of objects from the database by mapping the results of the
1670:             * SQL query into instances of the given object class. Only the columns
1671:             * returned from the SQL query will be set into the object instance. If a
1672:             * given column can't be mapped to the target object instance, a
1673:             * {@link PersistException} will be thrown.
1674:             * 
1675:             * @since 1.0
1676:             */
1677:            public <T> List<T> readList(final Class<T> objectClass,
1678:                    final String sql) {
1679:                return readList(objectClass, sql, (Object[]) null);
1680:            }
1681:
1682:            /**
1683:             * Reads a list of all objects in the database mapped to the given object
1684:             * class.
1685:             * 
1686:             * @since 1.0
1687:             */
1688:            public <T> List<T> readList(final Class<T> objectClass) {
1689:                final TableMapping mapping = getTableMapping(objectClass,
1690:                        "readList(Class)");
1691:                final String sql = mapping.getSelectAllSql();
1692:                return readList(objectClass, sql);
1693:            }
1694:
1695:            // --- iterators ---
1696:
1697:            /**
1698:             * Returns an {@link java.util.Iterator} for a list of objects from the
1699:             * database that map the contents of the ResultSet into instances of the
1700:             * given object class. Only columns contained in the ResultSet will be set
1701:             * into the object instances. If a given column can't be mapped to a target
1702:             * object instance, a {@link PersistException} will be thrown.
1703:             * 
1704:             * @since 1.0
1705:             */
1706:            public <T> Iterator<T> readIterator(final Class<T> objectClass,
1707:                    final ResultSet resultSet) {
1708:
1709:                long begin = 0;
1710:                if (Log.isDebugEnabled(Log.PROFILING)) {
1711:                    begin = System.currentTimeMillis();
1712:                }
1713:
1714:                final ResultSetIterator i = new ResultSetIterator(this ,
1715:                        objectClass, resultSet, ResultSetIterator.TYPE_OBJECT);
1716:
1717:                if (Log.isDebugEnabled(Log.PROFILING)) {
1718:                    final long end = System.currentTimeMillis();
1719:                    Log.debug(Log.PROFILING, "readIterator in ["
1720:                            + (end - begin) + "ms] for object type ["
1721:                            + objectClass.getSimpleName() + "]");
1722:                }
1723:
1724:                return i;
1725:            }
1726:
1727:            /**
1728:             * Returns an {@link java.util.Iterator} for a list of objects from the
1729:             * database that map the contents of the execution of the given
1730:             * PreparedStatement into instances of the given object class. Only columns
1731:             * contained in the ResultSet will be set into the object instances. If a
1732:             * given column can't be mapped to a target object instance, a
1733:             * PersistException will be thrown.
1734:             * <p>
1735:             * Parameters will be set according with the correspondence defined in
1736:             * {@link #setParameters(PreparedStatement, int[], Object[])}
1737:             * 
1738:             * @since 1.0
1739:             */
1740:            public <T> Iterator<T> readIterator(final Class<T> objectClass,
1741:                    final PreparedStatement statement,
1742:                    final Object... parameters) {
1743:                setParameters(statement, parameters);
1744:                try {
1745:                    final ResultSet resultSet = statement.executeQuery();
1746:                    return readIterator(objectClass, resultSet);
1747:                } catch (SQLException e) {
1748:                    throw new RuntimeSQLException(e);
1749:                }
1750:            }
1751:
1752:            /**
1753:             * Returns an {@link java.util.Iterator} for a list of objects from the
1754:             * database that map the contents of the execution of the given SQL query
1755:             * into instances of the given object class. Only columns contained in the
1756:             * ResultSet will be set into the object instances. If a given column can't
1757:             * be mapped to a target object instance, a {@link PersistException} will
1758:             * be thrown.
1759:             * <p>
1760:             * Parameters will be set according with the correspondence defined in
1761:             * {@link #setParameters(PreparedStatement, int[], Object[])}
1762:             * 
1763:             * @since 1.0
1764:             */
1765:            public <T> Iterator<T> readIterator(final Class<T> objectClass,
1766:                    final String sql, final Object... parameters) {
1767:                final PreparedStatement stmt = getPreparedStatement(sql);
1768:                final Iterator ret = readIterator(objectClass, stmt, parameters);
1769:                // don't close the prepared statement otherwise the result set in the
1770:                // iterator will be closed
1771:                return ret;
1772:            }
1773:
1774:            /**
1775:             * Returns an {@link java.util.Iterator} for a list of objects from the
1776:             * database that map the contents of the execution of the given SQL query
1777:             * into instances of the given object class. Only columns contained in the
1778:             * ResultSet will be set into the object instances. If a given column can't
1779:             * be mapped to a target object instance, a {@link PersistException} will
1780:             * be thrown.
1781:             * 
1782:             * @since 1.0
1783:             */
1784:            public <T> Iterator<T> readIterator(final Class<T> objectClass,
1785:                    final String sql) {
1786:                return readIterator(objectClass, sql, (Object[]) null);
1787:            }
1788:
1789:            /**
1790:             * Returns an {@link java.util.Iterator} for a list of all objects in the
1791:             * database mapped to the given object class.
1792:             * 
1793:             * @since 1.0
1794:             */
1795:            public <T> Iterator<T> readIterator(final Class<T> objectClass) {
1796:                final TableMapping mapping = getTableMapping(objectClass,
1797:                        "readIterator(Class)");
1798:                final String sql = mapping.getSelectAllSql();
1799:                return readIterator(objectClass, sql);
1800:            }
1801:
1802:            // ---------- read (map) ----------
1803:
1804:            // --- single objects ---
1805:
1806:            /**
1807:             * Reads a single object from the database by mapping the results of the SQL
1808:             * query into an instance of {@link java.util.Map}.
1809:             * <p>
1810:             * Types returned from the database will be converted to Java types in the
1811:             * map according with the correspondence defined in
1812:             * {@link #getValueFromResultSet(ResultSet, int, int)}.
1813:             * 
1814:             * @since 1.0
1815:             */
1816:            public Map<String, Object> readMap(final String sql) {
1817:                return readMap(sql, (Object[]) null);
1818:            }
1819:
1820:            /**
1821:             * Reads a single object from the database by mapping the results of the SQL
1822:             * query into an instance of {@link java.util.Map}.
1823:             * <p>
1824:             * Types returned from the database will be converted to Java types in the
1825:             * map according with the correspondence defined in
1826:             * {@link #getValueFromResultSet(ResultSet, int, int)}.
1827:             * <p>
1828:             * Parameters will be set according with the correspondence defined in
1829:             * {@link Persist#setParameters(PreparedStatement, int[], Object[])}
1830:             * 
1831:             * @since 1.0
1832:             */
1833:            public Map<String, Object> readMap(final String sql,
1834:                    final Object... parameters) {
1835:                final PreparedStatement stmt = getPreparedStatement(sql);
1836:                final Map<String, Object> ret = readMap(stmt, parameters);
1837:                if (closePreparedStatementsAfterRead) {
1838:                    closePreparedStatement(stmt);
1839:                }
1840:                return ret;
1841:            }
1842:
1843:            /**
1844:             * Reads a single object from the database by mapping the results of the
1845:             * PreparedStatement execution into an instance of {@link java.util.Map}.
1846:             * <p>
1847:             * Types returned from the database will be converted to Java types in the
1848:             * map according with the correspondence defined in
1849:             * {@link #getValueFromResultSet(ResultSet, int, int)}.
1850:             * <p>
1851:             * Parameters will be set according with the correspondence defined in
1852:             * {@link Persist#setParameters(PreparedStatement, int[], Object[])}
1853:             * 
1854:             * @since 1.0
1855:             */
1856:            public Map<String, Object> readMap(
1857:                    final PreparedStatement statement,
1858:                    final Object... parameters) {
1859:                setParameters(statement, parameters);
1860:                try {
1861:                    final ResultSet resultSet = statement.executeQuery();
1862:                    return readMap(resultSet);
1863:                } catch (SQLException e) {
1864:                    throw new RuntimeSQLException(e);
1865:                }
1866:            }
1867:
1868:            /**
1869:             * Reads a single object from the database by mapping the results of the
1870:             * current ResultSet row into an instance of {@link java.util.Map}.
1871:             * <p>
1872:             * Types returned from the database will be converted to Java types in the
1873:             * map according with the correspondence defined in
1874:             * {@link #getValueFromResultSet(ResultSet, int, int)}.
1875:             * 
1876:             * @since 1.0
1877:             */
1878:            public Map<String, Object> readMap(final ResultSet resultSet) {
1879:
1880:                long begin = 0;
1881:                if (Log.isDebugEnabled(Log.PROFILING)) {
1882:                    begin = System.currentTimeMillis();
1883:                }
1884:
1885:                Map<String, Object> ret = null;
1886:
1887:                try {
1888:                    if (resultSet.next()) {
1889:                        ret = loadMap(resultSet);
1890:                        if (resultSet.next()) {
1891:                            throw new PersistException(
1892:                                    "Non-unique result returned");
1893:                        }
1894:                    } else {
1895:                        ret = null;
1896:                    }
1897:                } catch (SQLException e) {
1898:                    throw new RuntimeSQLException(e);
1899:                }
1900:
1901:                if (Log.isDebugEnabled(Log.PROFILING)) {
1902:                    final long end = System.currentTimeMillis();
1903:                    Log.debug(Log.PROFILING, "readMap in [" + (end - begin)
1904:                            + "ms]");
1905:                }
1906:
1907:                return ret;
1908:            }
1909:
1910:            // --- list ---
1911:
1912:            /**
1913:             * Reads a list of objects from the database by mapping the ResultSet rows
1914:             * to instances of {@link java.util.Map}.
1915:             * <p>
1916:             * Types returned from the database will be converted to Java types in the
1917:             * map according with the correspondence defined in
1918:             * {@link #getValueFromResultSet(ResultSet, int, int)}.
1919:             * 
1920:             * @since 1.0
1921:             */
1922:            public List<Map<String, Object>> readMapList(
1923:                    final ResultSet resultSet) {
1924:
1925:                long begin = 0;
1926:                if (Log.isDebugEnabled(Log.PROFILING)) {
1927:                    begin = System.currentTimeMillis();
1928:                }
1929:
1930:                final List ret = new ArrayList();
1931:                try {
1932:                    while (resultSet.next()) {
1933:                        ret.add(loadMap(resultSet));
1934:                    }
1935:                } catch (SQLException e) {
1936:                    throw new RuntimeSQLException(e);
1937:                }
1938:
1939:                if (Log.isDebugEnabled(Log.PROFILING)) {
1940:                    final long end = System.currentTimeMillis();
1941:                    Log.debug(Log.PROFILING, "readMapList [" + (end - begin)
1942:                            + "ms]");
1943:                }
1944:
1945:                return ret;
1946:            }
1947:
1948:            /**
1949:             * Reads a list of objects from the database by mapping the
1950:             * PreparedStatement execution results to instances of {@link java.util.Map}.
1951:             * <p>
1952:             * Types returned from the database will be converted to Java types in the
1953:             * map according with the correspondence defined in
1954:             * {@link #getValueFromResultSet(ResultSet, int, int)}.
1955:             * <p>
1956:             * Parameters will be set according with the correspondence defined in
1957:             * {@link #setParameters(PreparedStatement, int[], Object[])}
1958:             * 
1959:             * @since 1.0
1960:             */
1961:            public List<Map<String, Object>> readMapList(
1962:                    final PreparedStatement statement,
1963:                    final Object... parameters) {
1964:                setParameters(statement, parameters);
1965:                try {
1966:                    final ResultSet resultSet = statement.executeQuery();
1967:                    return readMapList(resultSet);
1968:                } catch (SQLException e) {
1969:                    throw new RuntimeSQLException(e);
1970:                }
1971:            }
1972:
1973:            /**
1974:             * Reads a list of objects from the database by mapping the SQL execution
1975:             * results to instances of {@link java.util.Map}.
1976:             * <p>
1977:             * Types returned from the database will be converted to Java types in the
1978:             * map according with the correspondence defined in
1979:             * {@link #getValueFromResultSet(ResultSet, int, int)}.
1980:             * <p>
1981:             * Parameters will be set according with the correspondence defined in
1982:             * {@link #setParameters(PreparedStatement, int[], Object[])}
1983:             * 
1984:             * @since 1.0
1985:             */
1986:            public List<Map<String, Object>> readMapList(final String sql,
1987:                    final Object... parameters) {
1988:                final PreparedStatement stmt = getPreparedStatement(sql);
1989:                final List<Map<String, Object>> ret = readMapList(stmt,
1990:                        parameters);
1991:                if (closePreparedStatementsAfterRead) {
1992:                    closePreparedStatement(stmt);
1993:                }
1994:                return ret;
1995:            }
1996:
1997:            /**
1998:             * Reads a list of all objects in the database mapped to the given object
1999:             * class and return each result as an instance of {@link java.util.Map}.
2000:             * <p>
2001:             * Types returned from the database will be converted to Java types in the
2002:             * map according with the correspondence defined in
2003:             * {@link #getValueFromResultSet(ResultSet, int, int)}.
2004:             * 
2005:             * @since 1.0
2006:             */
2007:            public List<Map<String, Object>> readMapList(final String sql) {
2008:                return readMapList(sql, (Object[]) null);
2009:            }
2010:
2011:            // --- iterator ---
2012:
2013:            /**
2014:             * Returns an {@link java.util.Iterator} for a list of {@link java.util.Map}
2015:             * instances containing data from the provided ResultSet rows.
2016:             * <p>
2017:             * Types returned from the database will be converted to Java types in the
2018:             * map according with the correspondence defined in
2019:             * {@link #getValueFromResultSet(ResultSet, int, int)}.
2020:             * 
2021:             * @since 1.0
2022:             */
2023:            public Iterator readMapIterator(final ResultSet resultSet) {
2024:                return new ResultSetIterator(this , null, resultSet,
2025:                        ResultSetIterator.TYPE_MAP);
2026:            }
2027:
2028:            /**
2029:             * Returns an {@link java.util.Iterator} for a list of {@link java.util.Map}
2030:             * instances containing data from the execution of the provided
2031:             * PreparedStatement.
2032:             * <p>
2033:             * Types returned from the database will be converted to Java types in the
2034:             * map according with the correspondence defined in
2035:             * {@link #getValueFromResultSet(ResultSet, int, int)}.
2036:             * <p>
2037:             * Parameters will be set according with the correspondence defined in
2038:             * {@link Persist#setParameters(PreparedStatement, int[], Object[])}
2039:             * 
2040:             * @since 1.0
2041:             */
2042:            public Iterator readMapIterator(final PreparedStatement statement,
2043:                    final Object... parameters) {
2044:                setParameters(statement, parameters);
2045:                try {
2046:                    final ResultSet resultSet = statement.executeQuery();
2047:                    return readMapIterator(resultSet);
2048:                } catch (SQLException e) {
2049:                    throw new RuntimeSQLException(e);
2050:                }
2051:            }
2052:
2053:            /**
2054:             * Returns an {@link java.util.Iterator} for a list of {@link java.util.Map}
2055:             * instances containing data from the execution of the provided parametrized
2056:             * SQL.
2057:             * <p>
2058:             * Types returned from the database will be converted to Java types in the
2059:             * map according with the correspondence defined in
2060:             * {@link #getValueFromResultSet(ResultSet, int, int)}.
2061:             * <p>
2062:             * Parameters will be set according with the correspondence defined in
2063:             * {@link #setParameters(PreparedStatement, int[], Object[])}
2064:             * 
2065:             * @since 1.0
2066:             */
2067:            public Iterator readMapIterator(final String sql,
2068:                    final Object... parameters) {
2069:
2070:                long begin = 0;
2071:                if (Log.isDebugEnabled(Log.PROFILING)) {
2072:                    begin = System.currentTimeMillis();
2073:                }
2074:
2075:                final PreparedStatement stmt = getPreparedStatement(sql);
2076:                final Iterator ret = readMapIterator(stmt, parameters);
2077:
2078:                if (Log.isDebugEnabled(Log.PROFILING)) {
2079:                    final long end = System.currentTimeMillis();
2080:                    Log.debug(Log.PROFILING, "readMapIterator in ["
2081:                            + (end - begin) + "ms]");
2082:                }
2083:
2084:                return ret;
2085:            }
2086:
2087:            /**
2088:             * Returns an {@link java.util.Iterator} for a list of {@link java.util.Map}
2089:             * instances containing data from the execution of the provided SQL.
2090:             * <p>
2091:             * Types returned from the database will be converted to Java types in the
2092:             * map according with the correspondence defined in
2093:             * {@link #getValueFromResultSet(ResultSet, int, int)}.
2094:             * 
2095:             * @since 1.0
2096:             */
2097:            public Iterator readMapIterator(final String sql) {
2098:                return readMapIterator(sql, (Object[]) null);
2099:            }
2100:
2101:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.