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: }
|