0001: /*
0002: * Copyright 2001-2007 Geert Bevin <gbevin[remove] at uwyn dot com>
0003: * Distributed under the terms of either:
0004: * - the common development and distribution license (CDDL), v1.0; or
0005: * - the GNU Lesser General Public License, v2.1 or later
0006: * $Id: DbStatement.java 3648 2007-01-29 12:16:16Z gbevin $
0007: */
0008: package com.uwyn.rife.database;
0009:
0010: import com.uwyn.rife.config.RifeConfig;
0011: import com.uwyn.rife.database.DbResultSet;
0012: import com.uwyn.rife.database.exceptions.BatchExecutionErrorException;
0013: import com.uwyn.rife.database.exceptions.DatabaseException;
0014: import com.uwyn.rife.database.exceptions.ExecutionErrorException;
0015: import com.uwyn.rife.database.exceptions.MissingResultsException;
0016: import com.uwyn.rife.database.exceptions.StatementCloseErrorException;
0017: import com.uwyn.rife.database.queries.Query;
0018: import com.uwyn.rife.database.queries.ReadQuery;
0019: import com.uwyn.rife.tools.ExceptionUtils;
0020: import com.uwyn.rife.tools.JavaSpecificationUtils;
0021: import java.lang.reflect.Constructor;
0022: import java.sql.ResultSet;
0023: import java.sql.SQLException;
0024: import java.sql.SQLWarning;
0025: import java.sql.Statement;
0026: import java.util.logging.Level;
0027: import java.util.logging.Logger;
0028:
0029: /**
0030: * Provides a wrapper around the regular JDBC <code>Statement</code> class. It
0031: * can only be instantiated by calling the <code>createStatement</code> method on
0032: * an existing <code>DbConnection</code> instance.
0033: * <p>This class hooks into the database connection pool and cleans up as much
0034: * as possible in case of errors. The thrown <code>DatabaseException</code>
0035: * exceptions should thus only be used for error reporting and not for
0036: * releasing resources used by the framework.
0037: * <p>The <code>execute</code> and <code>executeQuery</code> methods store
0038: * their result set in the executing <code>DbStatement</code> instance. It's
0039: * recommended to use the <code>DbQueryManager</code>'s <code>fetch</code>
0040: * method to process the result set. If needed, one can also use the
0041: * <code>getResultSet</code> method to manually process the results through
0042: * plain JDBC. However, when exceptions are thrown during this procedure, it's
0043: * also the responsability of the user to correctly clean up all resources.
0044: * <p>Additional methods have been implemented to facilitate the retrieval of
0045: * queries which return only a single field and to easily check if a query
0046: * returned any result rows.
0047: *
0048: * @author Geert Bevin (gbevin[remove] at uwyn dot com)
0049: * @version $Revision: 3648 $
0050: * @see #executeQuery(String)
0051: * @see #execute(String)
0052: * @see #execute(String, int)
0053: * @see #execute(String, int[])
0054: * @see #execute(String, String[])
0055: * @see #getResultSet()
0056: * @see com.uwyn.rife.database.DbConnection#createStatement
0057: * @see com.uwyn.rife.database.DbQueryManager#fetch(ResultSet, DbRowProcessor)
0058: * @see java.sql.ResultSet
0059: * @see java.sql.Statement
0060: * @since 1.0
0061: */
0062: public class DbStatement implements Cloneable {
0063: private DbResultSet mResultSet = null;
0064:
0065: final Statement mStatement;
0066: final DbConnection mConnection;
0067:
0068: /**
0069: * Constructs a new <code>DbStatement</code> from an existing
0070: * <code>DbConnection</code> and <code>Statement</code>. This constructor
0071: * will never be called by a user of the api. The
0072: * <code>createStatement</code> of an existing <code>DbConnection</code>
0073: * instance should be used instead.
0074: *
0075: * @param connection a <code>DbConnection</code> instance
0076: * @param statement a JDBC <code>Statement</code> instance
0077: * @exception DatabaseException if a database access error occurs
0078: * @since 1.0
0079: */
0080: DbStatement(DbConnection connection, Statement statement)
0081: throws DatabaseException {
0082: assert connection != null;
0083: assert statement != null;
0084:
0085: mConnection = connection;
0086: mStatement = statement;
0087: }
0088:
0089: /**
0090: * Adds the given SQL command to the current list of commmands for this
0091: * <code>Statement</code> object. The commands in this list can be
0092: * executed as a batch by calling the method <code>executeBatch</code>.
0093: * <p>If an exception is thrown, this <code>DbStatement</code> is
0094: * automatically closed and an ongoing transaction will be automatically
0095: * rolled back if it belongs to the executing thread.
0096: *
0097: * @param sql typically this is a static SQL <code>INSERT</code> or
0098: * <code>UPDATE</code> statement
0099: * @exception DatabaseException if a database access error occurs, or the
0100: * driver does not support batch updates
0101: * @see #executeBatch
0102: * @since 1.0
0103: */
0104: public void addBatch(String sql) throws DatabaseException {
0105: try {
0106: mStatement.addBatch(sql);
0107: traceBatch(sql);
0108: } catch (SQLException e) {
0109: handleException();
0110: throw new DatabaseException(e);
0111: }
0112: }
0113:
0114: /**
0115: * Cancels this <code>DbStatement</code> object if both the DBMS and
0116: * driver support aborting a SQL statement. This method can be used by one
0117: * thread to cancel a statement that is being executed by another thread.
0118: * <p>If an exception is thrown, this <code>DbStatement</code> is
0119: * automatically closed and an ongoing transaction will be automatically
0120: * rolled back if it belongs to the executing thread.
0121: *
0122: * @exception DatabaseException if a database access error occurs
0123: * @since 1.0
0124: */
0125: public void cancel() throws DatabaseException {
0126: try {
0127: mStatement.cancel();
0128: } catch (SQLException e) {
0129: handleException();
0130: throw new DatabaseException(e);
0131: }
0132: }
0133:
0134: /**
0135: * Empties this <code>Statement</code> object's current list of SQL
0136: * commands.
0137: * <p>If an exception is thrown, this <code>DbStatement</code> is
0138: * automatically closed and an ongoing transaction will be automatically
0139: * rolled back if it belongs to the executing thread.
0140: *
0141: * @exception DatabaseException if a database access error occurs or the
0142: * driver does not support batch updates
0143: * @see #addBatch
0144: * @since 1.0
0145: */
0146: public void clearBatch() throws DatabaseException {
0147: try {
0148: mStatement.clearBatch();
0149: } catch (SQLException e) {
0150: handleException();
0151: throw new DatabaseException(e);
0152: }
0153: }
0154:
0155: /**
0156: * Clears all the warnings reported on this <code>DbStatement</code>
0157: * object. After a call to this method, the method
0158: * <code>getWarnings</code> will return <code>null</code> until a new
0159: * warning is reported for this <code>DbStatement</code> object.
0160: * <p>If an exception is thrown, this <code>DbStatement</code> is
0161: * automatically closed and an ongoing transaction will be automatically
0162: * rolled back if it belongs to the executing thread.
0163: *
0164: * @exception DatabaseException if a database access error occurs
0165: * @since 1.0
0166: */
0167: public void clearWarnings() throws DatabaseException {
0168: try {
0169: mStatement.clearWarnings();
0170: } catch (SQLException e) {
0171: handleException();
0172: throw new DatabaseException(e);
0173: }
0174: }
0175:
0176: /**
0177: * Releases this <code>DbStatement</code> object's database and JDBC
0178: * resources immediately instead of waiting for this to happen when it is
0179: * automatically closed. It is generally good practice to release
0180: * resources as soon as you are finished with them to avoid tying up
0181: * database resources.
0182: * <p>Calling the method <code>close</code> on a <code>DbStatement</code>
0183: * object that is already closed has no effect.
0184: * <p><b>Note:</b> A <code>DbStatement</code> object is automatically
0185: * closed when it is garbage collected. When a <code>DbStatement</code>
0186: * object is closed, its current <code>ResultSet</code> object, if one
0187: * exists, is also closed.
0188: *
0189: * @exception DatabaseException if a database access error occurs
0190: * @since 1.0
0191: */
0192: public void close() throws DatabaseException {
0193: try {
0194: mConnection.releaseStatement(this );
0195:
0196: // cleanup this statement
0197: cleanResultSet();
0198:
0199: mStatement.close();
0200: } catch (SQLException e) {
0201: throw new StatementCloseErrorException(mConnection
0202: .getDatasource(), e);
0203: }
0204: }
0205:
0206: protected long startTrace() {
0207: if (RifeConfig.Database.getSqlDebugTrace()) {
0208: Logger logger = Logger.getLogger("com.uwyn.rife.database");
0209: if (logger.isLoggable(Level.INFO)) {
0210: return System.currentTimeMillis();
0211: }
0212: }
0213:
0214: return 0;
0215: }
0216:
0217: protected void outputTrace(long start, String sql) {
0218: if (start != 0) {
0219: StringBuilder output = new StringBuilder();
0220:
0221: output.append(System.currentTimeMillis() - start);
0222: output.append("ms : ");
0223: output.append(sql);
0224:
0225: Logger.getLogger("com.uwyn.rife.database").info(
0226: output.toString());
0227: }
0228: }
0229:
0230: protected void traceBatch(String sql) {
0231: if (RifeConfig.Database.getSqlDebugTrace()) {
0232: Logger logger = Logger.getLogger("com.uwyn.rife.database");
0233: if (logger.isLoggable(Level.INFO)) {
0234: logger.info("batched : " + sql);
0235: }
0236: }
0237: }
0238:
0239: /**
0240: * Executes the given SQL statement, which may return multiple results. In
0241: * some (uncommon) situations, a single SQL statement may return multiple
0242: * result sets and/or update counts. Normally you can ignore this unless
0243: * you are (1) executing a stored procedure that you know may return
0244: * multiple results or (2) you are dynamically executing an unknown SQL
0245: * string.
0246: * <p>The <code>execute</code> method executes a SQL statement and
0247: * indicates the form of the first result. You must then use the methods
0248: * <code>getResultSet</code> or <code>getUpdateCount</code> to retrieve
0249: * the result, and <code>getMoreResults</code> to move to any subsequent
0250: * result(s).
0251: * <p>If an exception is thrown, this <code>DbStatement</code> is
0252: * automatically closed and an ongoing transaction will be automatically
0253: * rolled back if it belongs to the executing thread.
0254: *
0255: * @param sql any SQL statement
0256: * @return <code>true</code> if the first result is a
0257: * <code>ResultSet</code> object; or
0258: * <p><code>false</code> if it is an update count or there are no results
0259: * @exception DatabaseException if a database access error occurs
0260: * @see #getResultSet
0261: * @see #getUpdateCount
0262: * @see #getMoreResults
0263: * @since 1.0
0264: */
0265: public boolean execute(String sql) throws DatabaseException {
0266: try {
0267: waitForConnection();
0268:
0269: cleanResultSet();
0270:
0271: long start = startTrace();
0272: boolean result = mStatement.execute(sql);
0273: outputTrace(start, sql);
0274:
0275: setResultset(mStatement.getResultSet());
0276:
0277: return result;
0278: } catch (SQLException e) {
0279: handleException();
0280: throw new ExecutionErrorException(sql, mConnection
0281: .getDatasource(), e);
0282: }
0283: }
0284:
0285: /**
0286: * Executes the given SQL statement, which may return multiple results,
0287: * and signals the driver that any auto-generated keys should be made
0288: * available for retrieval. The driver will ignore this signal if the SQL
0289: * statement is not an <code>INSERT</code> statement.
0290: * <p>In some (uncommon) situations, a single SQL statement may return
0291: * multiple result sets and/or update counts. Normally you can ignore this
0292: * unless you are (1) executing a stored procedure that you know may
0293: * return multiple results or (2) you are dynamically executing an unknown
0294: * SQL string.
0295: * <p>The <code>execute</code> method executes a SQL statement and
0296: * indicates the form of the first result. You must then use the methods
0297: * <code>getResultSet</code> or <code>getUpdateCount</code> to retrieve
0298: * the result, and <code>getMoreResults</code> to move to any subsequent
0299: * result(s).
0300: * <p>If an exception is thrown, this <code>DbStatement</code> is
0301: * automatically closed and an ongoing transaction will be automatically
0302: * rolled back if it belongs to the executing thread.
0303: *
0304: * @param sql any SQL statement
0305: * @param autoGeneratedKeys a constant indicating whether auto-generated
0306: * keys should be made available for retrieval using the method
0307: * <code>getGeneratedKeys</code>; one of the following constants:
0308: * <code>Statement.RETURN_GENERATED_KEYS</code> or
0309: * <code>Statement.NO_GENERATED_KEYS</code>
0310: * @return <code>true</code> if the first result is a
0311: * <code>ResultSet</code> object; or
0312: * <p><code>false</code> if it is an update count or there are no results
0313: * @exception DatabaseException if a database access error occurs
0314: * @see Statement
0315: * @see #getResultSet
0316: * @see #getUpdateCount
0317: * @see #getMoreResults
0318: * @see #getGeneratedKeys
0319: * @since 1.0
0320: */
0321: public boolean execute(String sql, int autoGeneratedKeys)
0322: throws DatabaseException {
0323: try {
0324: waitForConnection();
0325:
0326: cleanResultSet();
0327:
0328: long start = startTrace();
0329: boolean result = mStatement.execute(sql, autoGeneratedKeys);
0330: outputTrace(start, sql);
0331:
0332: setResultset(mStatement.getResultSet());
0333:
0334: return result;
0335: } catch (SQLException e) {
0336: handleException();
0337: throw new ExecutionErrorException(sql, mConnection
0338: .getDatasource(), e);
0339: }
0340: }
0341:
0342: /**
0343: * Executes the given SQL statement, which may return multiple results,
0344: * and signals the driver that the auto-generated keys indicated in the
0345: * given array should be made available for retrieval. This array contains
0346: * the indexes of the columns in the target table that contain the
0347: * auto-generated keys that should be made available. The driver will
0348: * ignore the array if the given SQL statement is not an
0349: * <code>INSERT</code> statement.
0350: * <p>Under some (uncommon) situations, a single SQL statement may return
0351: * multiple result sets and/or update counts. Normally you can ignore this
0352: * unless you are (1) executing a stored procedure that you know may
0353: * return multiple results or (2) you are dynamically executing an unknown
0354: * SQL string.
0355: * <p>The <code>execute</code> method executes a SQL statement and
0356: * indicates the form of the first result. You must then use the methods
0357: * <code>getResultSet</code> or <code>getUpdateCount</code> to retrieve
0358: * the result, and <code>getMoreResults</code> to move to any subsequent
0359: * result(s).
0360: * <p>If an exception is thrown, this <code>DbStatement</code> is
0361: * automatically closed and an ongoing transaction will be automatically
0362: * rolled back if it belongs to the executing thread.
0363: *
0364: * @param sql any SQL statement
0365: * @param columnIndexes an array of the indexes of the columns in the
0366: * inserted row that should be made available for retrieval by a call to
0367: * the method <code>getGeneratedKeys</code>
0368: * @return <code>true</code> if the first result is a
0369: * <code>ResultSet</code> object; or
0370: * <p><code>false</code> if it is an update count or there are no results
0371: * @exception DatabaseException if a database access error occurs
0372: * @see #getResultSet
0373: * @see #getUpdateCount
0374: * @see #getMoreResults
0375: * @since 1.0
0376: */
0377: public boolean execute(String sql, int[] columnIndexes)
0378: throws DatabaseException {
0379: try {
0380: waitForConnection();
0381:
0382: cleanResultSet();
0383:
0384: long start = startTrace();
0385: boolean result = mStatement.execute(sql, columnIndexes);
0386: outputTrace(start, sql);
0387:
0388: setResultset(mStatement.getResultSet());
0389:
0390: return result;
0391: } catch (SQLException e) {
0392: handleException();
0393: throw new ExecutionErrorException(sql, mConnection
0394: .getDatasource(), e);
0395: }
0396: }
0397:
0398: /**
0399: * Executes the given SQL statement, which may return multiple results,
0400: * and signals the driver that the auto-generated keys indicated in the
0401: * given array should be made available for retrieval. This array contains
0402: * the names of the columns in the target table that contain the
0403: * auto-generated keys that should be made available. The driver will
0404: * ignore the array if the given SQL statement is not an
0405: * <code>INSERT</code> statement.
0406: * <p>In some (uncommon) situations, a single SQL statement may return
0407: * multiple result sets and/or update counts. Normally you can ignore this
0408: * unless you are (1) executing a stored procedure that you know may
0409: * return multiple results or (2) you are dynamically executing an unknown
0410: * SQL string.
0411: * <p>The <code>execute</code> method executes a SQL statement and
0412: * indicates the form of the first result. You must then use the methods
0413: * <code>getResultSet</code> or <code>getUpdateCount</code> to retrieve
0414: * the result, and <code>getMoreResults</code> to move to any subsequent
0415: * result(s).
0416: * <p>If an exception is thrown, this <code>DbStatement</code> is
0417: * automatically closed and an ongoing transaction will be automatically
0418: * rolled back if it belongs to the executing thread.
0419: *
0420: * @param sql any SQL statement
0421: * @param columnNames an array of the names of the columns in the inserted
0422: * row that should be made available for retrieval by a call to the method
0423: * <code>getGeneratedKeys</code>
0424: * @return <code>true</code> if the next result is a
0425: * <code>ResultSet</code> object; or
0426: * <p><code>false</code> if it is an update count or there are no more
0427: * results
0428: * @exception DatabaseException if a database access error occurs
0429: * @see #getResultSet
0430: * @see #getUpdateCount
0431: * @see #getMoreResults
0432: * @see #getGeneratedKeys
0433: * @since 1.0
0434: */
0435: public boolean execute(String sql, String[] columnNames)
0436: throws DatabaseException {
0437: try {
0438: waitForConnection();
0439:
0440: cleanResultSet();
0441:
0442: long start = startTrace();
0443: boolean result = mStatement.execute(sql, columnNames);
0444: outputTrace(start, sql);
0445:
0446: setResultset(mStatement.getResultSet());
0447:
0448: return result;
0449: } catch (SQLException e) {
0450: handleException();
0451: throw new ExecutionErrorException(sql, mConnection
0452: .getDatasource(), e);
0453: }
0454: }
0455:
0456: /**
0457: * Submits a batch of commands to the database for execution and if all
0458: * commands execute successfully, returns an array of update counts. The
0459: * <code>int</code> elements of the array that is returned are ordered to
0460: * correspond to the commands in the batch, which are ordered according to
0461: * the order in which they were added to the batch. The elements in the
0462: * array returned by the method <code>executeBatch</code> may be one of
0463: * the following:
0464: * <ol>
0465: * <li>A number greater than or equal to zero -- indicates that the
0466: * command was processed successfully and is an update count giving the
0467: * number of rows in the database that were affected by the command's
0468: * execution
0469: * <li>A value of <code>SUCCESS_NO_INFO</code> -- indicates that the
0470: * command was processed successfully but that the number of rows affected
0471: * is unknown
0472: * <p>If one of the commands in a batch update fails to execute properly,
0473: * this method throws a <code>BatchUpdateException</code>, and a JDBC
0474: * driver may or may not continue to process the remaining commands in the
0475: * batch. However, the driver's behavior must be consistent with a
0476: * particular DBMS, either always continuing to process commands or never
0477: * continuing to process commands. If the driver continues processing
0478: * after a failure, the array returned by the method
0479: * <code>BatchUpdateException.getUpdateCounts</code> will contain as many
0480: * elements as there are commands in the batch, and at least one of the
0481: * elements will be the following:
0482: * <p>
0483: * <li>A value of <code>EXECUTE_FAILED</code> -- indicates that the
0484: * command failed to execute successfully and occurs only if a driver
0485: * continues to process commands after a command fails
0486: * </ol>
0487: * <p>A driver is not required to implement this method.
0488: * <p>If an exception is thrown, this <code>DbStatement</code> is
0489: * automatically closed and an ongoing transaction will be automatically
0490: * rolled back if it belongs to the executing thread.
0491: *
0492: * @return an array of update counts containing one element for each
0493: * command in the batch. The elements of the array are ordered according
0494: * to the order in which commands were added to the batch.
0495: * @exception DatabaseException if a database access error occurs or the
0496: * driver does not support batch statements. The cause is a {@link
0497: * java.sql.BatchUpdateException} (a subclass of <code>SQLException</code>)
0498: * if one of the commands sent to the database fails to execute properly
0499: * or attempts to return a result set.
0500: * @since 1.0
0501: */
0502: public int[] executeBatch() throws DatabaseException {
0503: try {
0504: waitForConnection();
0505:
0506: return mStatement.executeBatch();
0507: } catch (SQLException e) {
0508: handleException();
0509: throw new BatchExecutionErrorException(mConnection
0510: .getDatasource(), e);
0511: }
0512: }
0513:
0514: /**
0515: * Executes the given SQL statement. The returned <code>ResultSet</code>
0516: * object is stored and can be retrieved with the
0517: * <code>getResultSet</code> method.
0518: * <p>If an exception is thrown, this <code>DbStatement</code> is
0519: * automatically closed and an ongoing transaction will be automatically
0520: * rolled back if it belongs to the executing thread.
0521: *
0522: * @param sql a SQL statement to be sent to the database, typically a
0523: * static SQL <code>SELECT</code> statement
0524: * @exception DatabaseException if a database access error occurs or the
0525: * given SQL statement produces anything other than a single
0526: * <code>ResultSet</code> object
0527: * @see #getResultSet
0528: * @since 1.0
0529: */
0530: public void executeQuery(String sql) throws DatabaseException {
0531: try {
0532: waitForConnection();
0533:
0534: cleanResultSet();
0535:
0536: long start = startTrace();
0537: mStatement.execute(sql);
0538: outputTrace(start, sql);
0539:
0540: setResultset(mStatement.getResultSet());
0541:
0542: return;
0543: } catch (SQLException e) {
0544: handleException();
0545: throw new ExecutionErrorException(sql, mConnection
0546: .getDatasource(), e);
0547: }
0548: }
0549:
0550: /**
0551: * Executes the given <code>Query</code> builder's SQL statement. The
0552: * returned <code>ResultSet</code> object is stored and can be retrieved
0553: * with the <code>getResultSet</code> method.
0554: * <p>If an exception is thrown, this <code>DbStatement</code> is
0555: * automatically closed and an ongoing transaction will be automatically
0556: * rolled back if it belongs to the executing thread.
0557: *
0558: * @param query a <code>Query</code> builder instance which provides a SQL
0559: * statement that queries the database
0560: * @exception DatabaseException if a database access error occurs or the
0561: * given SQL statement produces anything other than a single
0562: * <code>ResultSet</code> object
0563: * @see #getResultSet
0564: * @since 1.0
0565: */
0566: public void executeQuery(ReadQuery query) throws DatabaseException {
0567: if (null == query)
0568: throw new IllegalArgumentException("query can't be null.");
0569:
0570: executeQuery(query.getSql());
0571: }
0572:
0573: /**
0574: * Executes the given SQL statement, which may be an <code>INSERT</code>,
0575: * <code>UPDATE</code>, or <code>DELETE</code> statement or an SQL
0576: * statement that returns nothing, such as an SQL DDL statement.
0577: * <p>If an exception is thrown, this <code>DbStatement</code> is
0578: * automatically closed and an ongoing transaction will be automatically
0579: * rolled back if it belongs to the executing thread.
0580: *
0581: * @param sql an SQL <code>INSERT</code>, <code>UPDATE</code> or
0582: * <code>DELETE</code> statement or a SQL statement that returns nothing
0583: * @return the row count for <code>INSERT</code>, <code>UPDATE</code> or
0584: * <code>DELETE</code> statements; or
0585: * <p><code>0</code> for SQL statements that return nothing
0586: * @exception DatabaseException if a database access error occurs or the
0587: * given SQL statement produces a <code>ResultSet</code> object
0588: * @since 1.0
0589: */
0590: public int executeUpdate(String sql) throws DatabaseException {
0591: try {
0592: waitForConnection();
0593:
0594: long start = startTrace();
0595: int result = mStatement.executeUpdate(sql);
0596: outputTrace(start, sql);
0597:
0598: return result;
0599: } catch (SQLException e) {
0600: handleException();
0601: throw new ExecutionErrorException(sql, mConnection
0602: .getDatasource(), e);
0603: }
0604: }
0605:
0606: /**
0607: * Executes the given <code>Query</code> builder's SQL statement, which
0608: * may be an <code>INSERT</code>, <code>UPDATE</code>, or
0609: * <code>DELETE</code> statement or a SQL statement that returns nothing,
0610: * such as an SQL DDL statement.
0611: * <p>If an exception is thrown, this <code>DbStatement</code> is
0612: * automatically closed and an ongoing transaction will be automatically
0613: * rolled back if it belongs to the executing thread.
0614: *
0615: * @param query a <code>Query</code> builder instance which provides a SQL
0616: * statement that modifies the database
0617: * @return the row count for <code>INSERT</code>, <code>UPDATE</code> or
0618: * <code>DELETE</code> statements; or
0619: * <p><code>0</code> for SQL statements that return nothing
0620: * @exception DatabaseException if a database access error occurs or the
0621: * given SQL statement produces a <code>ResultSet</code> object
0622: * @since 1.0
0623: */
0624: public int executeUpdate(Query query) throws DatabaseException {
0625: if (null == query)
0626: throw new IllegalArgumentException("query can't be null.");
0627:
0628: return executeUpdate(query.getSql());
0629: }
0630:
0631: /**
0632: * Retrieves the direction for fetching rows from database tables that is
0633: * the default for result sets generated from this
0634: * <code>DbStatement</code> object. If this <code>DbStatement</code>
0635: * object has not set a fetch direction by calling the method
0636: * <code>setFetchDirection</code>, the return value is
0637: * implementation-specific.
0638: * <p>If an exception is thrown, this <code>DbStatement</code> is
0639: * automatically closed and an ongoing transaction will be automatically
0640: * rolled back if it belongs to the executing thread.
0641: *
0642: * @return the default fetch direction for result sets generated from this
0643: * <code>DbStatement</code> object
0644: * @exception DatabaseException if a database access error occurs
0645: * @see #setFetchDirection
0646: * @since 1.0
0647: */
0648: public int getFetchDirection() throws DatabaseException {
0649: try {
0650: return mStatement.getFetchDirection();
0651: } catch (SQLException e) {
0652: handleException();
0653: throw new DatabaseException(e);
0654: }
0655: }
0656:
0657: /**
0658: * Retrieves the number of result set rows that is the default fetch size
0659: * for <code>ResultSet</code> objects generated from this
0660: * <code>DbStatement</code> object. If this <code>DbStatement</code>
0661: * object has not set a fetch size by calling the method
0662: * <code>setFetchSize</code>, the return value is implementation-specific.
0663: * <p>If an exception is thrown, this <code>DbStatement</code> is
0664: * automatically closed and an ongoing transaction will be automatically
0665: * rolled back if it belongs to the executing thread.
0666: *
0667: * @return the default fetch size for result sets generated from this
0668: * <code>DbStatement</code> object
0669: * @exception DatabaseException if a database access error occurs
0670: * @see #setFetchSize
0671: * @since 1.0
0672: */
0673: public int getFetchSize() throws DatabaseException {
0674: try {
0675: return mStatement.getFetchSize();
0676: } catch (SQLException e) {
0677: handleException();
0678: throw new DatabaseException(e);
0679: }
0680: }
0681:
0682: /**
0683: * Retrieves any auto-generated keys created as a result of executing this
0684: * <code>DbStatement</code> object. If this DbStatement object did not
0685: * generate any keys, an empty <code>DbResultSet</code> object is
0686: * returned.
0687: * <p>If an exception is thrown, this <code>DbStatement</code> is
0688: * automatically closed and an ongoing transaction will be automatically
0689: * rolled back if it belongs to the executing thread.
0690: *
0691: * @return a <code>DbResultSet</code> object containing the auto-generated
0692: * key(s) generated by the execution of this <code>DbStatement</code>
0693: * object
0694: * @exception DatabaseException if a database access error occurs
0695: * @since 1.0
0696: */
0697: public DbResultSet getGeneratedKeys() throws DatabaseException {
0698: try {
0699: return wrapWithDbResultSet(mStatement.getGeneratedKeys());
0700: } catch (SQLException e) {
0701: handleException();
0702: throw new DatabaseException(e);
0703: }
0704: }
0705:
0706: /**
0707: * Retrieves the first auto-generated key created as a result of executing
0708: * this <code>DbStatement</code> object as an integer. If this
0709: * <code>DbStatement</code> object did not generate any keys, a exception
0710: * is thrown.
0711: * <p>If an exception is thrown, this <code>DbStatement</code> is
0712: * automatically closed and an ongoing transaction will be automatically
0713: * rolled back if it belongs to the executing thread.
0714: *
0715: * @return the first auto-generated key as an integer
0716: * @exception DatabaseException if a database access error occurs
0717: * @since 1.0
0718: */
0719: public int getFirstGeneratedIntKey() throws DatabaseException {
0720: try {
0721: DbResultSet resultset = getGeneratedKeys();
0722: resultset.next();
0723: return resultset.getInt(1);
0724: } catch (SQLException e) {
0725: handleException();
0726: throw new DatabaseException(e);
0727: }
0728: }
0729:
0730: /**
0731: * Retrieves the maximum number of bytes that can be returned for
0732: * character and binary column values in a <code>ResultSet</code> object
0733: * produced by this <code>Statement</code> object. This limit applies only
0734: * to <code>BINARY</code>, <code>VARBINARY</code>,
0735: * <code>LONGVARBINARY</code>, <code>CHAR</code>, <code>VARCHAR</code>,
0736: * and <code>LONGVARCHAR</code> columns. If the limit is exceeded, the
0737: * excess data is silently discarded.
0738: * <p>If an exception is thrown, this <code>DbStatement</code> is
0739: * automatically closed and an ongoing transaction will be automatically
0740: * rolled back if it belongs to the executing thread.
0741: *
0742: * @return the current column size limit for columns storing character and
0743: * binary values; or
0744: * <p><code>0</code> if there's no limit
0745: * @exception DatabaseException if a database access error occurs
0746: * @see #setMaxFieldSize
0747: * @since 1.0
0748: */
0749: public int getMaxFieldSize() throws DatabaseException {
0750: try {
0751: return mStatement.getMaxFieldSize();
0752: } catch (SQLException e) {
0753: handleException();
0754: throw new DatabaseException(e);
0755: }
0756: }
0757:
0758: /**
0759: * Retrieves the maximum number of rows that a <code>ResultSet</code>
0760: * object produced by this <code>DbStatement</code> object can contain. If
0761: * this limit is exceeded, the excess rows are silently dropped.
0762: * <p>If an exception is thrown, this <code>DbStatement</code> is
0763: * automatically closed and an ongoing transaction will be automatically
0764: * rolled back if it belongs to the executing thread.
0765: *
0766: * @return the current maximum number of rows for a <code>ResultSet</code>
0767: * object produced by this <code>Statement</code> object; or
0768: * <p><code>0</code> if there's no limit
0769: * @exception DatabaseException if a database access error occurs
0770: * @see #setMaxRows
0771: * @since 1.0
0772: */
0773: public int getMaxRows() throws DatabaseException {
0774: try {
0775: return mStatement.getMaxRows();
0776: } catch (SQLException e) {
0777: handleException();
0778: throw new DatabaseException(e);
0779: }
0780: }
0781:
0782: /**
0783: * Moves to this <code>DbStatement</code> object's next result, returns
0784: * <code>true</code> if it is a <code>ResultSet</code> object, and
0785: * implicitly closes any current <code>ResultSet</code> object(s) obtained
0786: * with the method <code>getResultSet</code>.
0787: * <p>There are no more results when the following is true:
0788: * <pre>
0789: * <code>(!getMoreResults() && (getUpdateCount() == -1)</code>
0790: * </pre>
0791: * <p>If an exception is thrown, this <code>DbStatement</code> is
0792: * automatically closed and an ongoing transaction will be automatically
0793: * rolled back if it belongs to the executing thread.
0794: *
0795: * @return <code>true</code> if the next result is a
0796: * <code>ResultSet</code> object; or
0797: * <p><code>false</code> if it is an update count or there are no more
0798: * results
0799: * @exception DatabaseException if a database access error occurs
0800: * @see #execute
0801: * @since 1.0
0802: */
0803: public boolean getMoreResults() throws DatabaseException {
0804: try {
0805: cleanResultSet();
0806:
0807: boolean result = mStatement.getMoreResults();
0808: setResultset(mStatement.getResultSet());
0809: return result;
0810: } catch (SQLException e) {
0811: handleException();
0812: throw new DatabaseException(e);
0813: }
0814: }
0815:
0816: /**
0817: * Moves to this <code>DbStatement</code> object's next result, deals with
0818: * any current <code>ResultSet</code> object(s) according to the
0819: * instructions specified by the given flag, and returns <code>true</code>
0820: * if the next result is a <code>ResultSet</code> object.
0821: * <p>There are no more results when the following is true:
0822: * <pre>
0823: * <code>(!getMoreResults() && (getUpdateCount() == -1)</code>
0824: * </pre>
0825: * <p>If an exception is thrown, this <code>DbStatement</code> is
0826: * automatically closed and an ongoing transaction will be automatically
0827: * rolled back if it belongs to the executing thread.
0828: *
0829: * @param current one of the following <code>Statement</code> constants
0830: * indicating what should happen to current <code>ResultSet</code> objects
0831: * obtained using the method <code>getResultSet</code>:
0832: * <code>CLOSE_CURRENT_RESULT</code>, <code>KEEP_CURRENT_RESULT</code>, or
0833: * <code>CLOSE_ALL_RESULTS</code>
0834: * @return <code>true</code> if the next result is a
0835: * <code>ResultSet</code> object; or
0836: * <p><code>false</code> if it is an update count or there are no more
0837: * results
0838: * @exception DatabaseException if a database access error occurs
0839: * @see Statement
0840: * @see #execute
0841: * @since 1.0
0842: */
0843: public boolean getMoreResults(int current) throws DatabaseException {
0844: try {
0845: cleanResultSet();
0846:
0847: boolean result = mStatement.getMoreResults(current);
0848: setResultset(mStatement.getResultSet());
0849: return result;
0850: } catch (SQLException e) {
0851: handleException();
0852: throw new DatabaseException(e);
0853: }
0854: }
0855:
0856: /**
0857: * Retrieves the number of seconds the driver will wait for a
0858: * <code>DbStatement</code> object to execute. If the limit is exceeded, a
0859: * <code>DatabaseException</code> is thrown.
0860: * <p>If an exception is thrown, this <code>DbStatement</code> is
0861: * automatically closed and an ongoing transaction will be automatically
0862: * rolled back if it belongs to the executing thread.
0863: *
0864: * @return the current query timeout limit in seconds; or
0865: * <p><code>0</code> if there's no limit
0866: * @exception DatabaseException if a database access error occurs
0867: * @see #setQueryTimeout
0868: * @since 1.0
0869: */
0870: public int getQueryTimeout() throws DatabaseException {
0871: try {
0872: return mStatement.getQueryTimeout();
0873: } catch (SQLException e) {
0874: handleException();
0875: throw new DatabaseException(e);
0876: }
0877: }
0878:
0879: /**
0880: * Reports whether the last column read had a value of SQL
0881: * <code>NULL</code>. Note that you must first call one of the getter
0882: * methods on a column to try to read its value and then call the method
0883: * <code>wasNull</code> to see if the value read was SQL <code>NULL</code>.
0884: * <p>If an exception is thrown, this <code>DbStatement</code> is
0885: * automatically closed and an ongoing transaction will be automatically
0886: * rolled back if it belongs to the executing thread.
0887: *
0888: * @return <code>true</code> if the last column value read was SQL
0889: * <code>NULL</code>; or
0890: * <p><code>false</code> otherwise
0891: * @exception DatabaseException if a database access error occurs
0892: * @since 1.0
0893: */
0894: public boolean wasNull() throws DatabaseException {
0895: if (null == mResultSet) {
0896: throw new MissingResultsException(getConnection()
0897: .getDatasource());
0898: }
0899:
0900: try {
0901: return mResultSet.wasNull();
0902: } catch (SQLException e) {
0903: handleException();
0904: throw new DatabaseException(e);
0905: }
0906: }
0907:
0908: /**
0909: * Retrieves the result set concurrency for <code>ResultSet</code> objects
0910: * generated by this <code>DbStatement</code> object.
0911: * <p>If an exception is thrown, this <code>DbStatement</code> is
0912: * automatically closed and an ongoing transaction will be automatically
0913: * rolled back if it belongs to the executing thread.
0914: *
0915: * @return either <code>ResultSet.CONCUR_READ_ONLY</code> or
0916: * <code>ResultSet.CONCUR_UPDATABLE</code>
0917: * @exception DatabaseException if a database access error occurs
0918: * @see ResultSet
0919: * @since 1.0
0920: */
0921: public int getResultSetConcurrency() throws DatabaseException {
0922: try {
0923: return mStatement.getResultSetConcurrency();
0924: } catch (SQLException e) {
0925: handleException();
0926: throw new DatabaseException(e);
0927: }
0928: }
0929:
0930: /**
0931: * Retrieves the result set holdability for <code>ResultSet</code> objects
0932: * generated by this <code>DbStatement</code> object.
0933: * <p>If an exception is thrown, this <code>DbStatement</code> is
0934: * automatically closed and an ongoing transaction will be automatically
0935: * rolled back if it belongs to the executing thread.
0936: *
0937: * @return either <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or
0938: * <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>
0939: * @exception DatabaseException if a database access error occurs
0940: * @see ResultSet
0941: * @since 1.0
0942: */
0943: public int getResultSetHoldability() throws DatabaseException {
0944: try {
0945: return mStatement.getResultSetHoldability();
0946: } catch (SQLException e) {
0947: handleException();
0948: throw new DatabaseException(e);
0949: }
0950: }
0951:
0952: /**
0953: * Retrieves the result set type for <code>ResultSet</code> objects
0954: * generated by this <code>DbStatement</code> object.
0955: * <p>If an exception is thrown, this <code>DbStatement</code> is
0956: * automatically closed and an ongoing transaction will be automatically
0957: * rolled back if it belongs to the executing thread.
0958: *
0959: * @return one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
0960: * <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or
0961: * <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>
0962: * @exception DatabaseException if a database access error occurs
0963: * @see ResultSet
0964: * @since 1.0
0965: */
0966: public int getResultSetType() throws DatabaseException {
0967: try {
0968: return mStatement.getResultSetType();
0969: } catch (SQLException e) {
0970: handleException();
0971: throw new DatabaseException(e);
0972: }
0973: }
0974:
0975: /**
0976: * Retrieves the current result as an update count; if the result is a
0977: * <code>ResultSet</code> object or there are no more results, -1 is
0978: * returned. This method should be called only once per result.
0979: * <p>If an exception is thrown, this <code>DbStatement</code> is
0980: * automatically closed and an ongoing transaction will be automatically
0981: * rolled back if it belongs to the executing thread.
0982: *
0983: * @return the current result as an update count; or
0984: * <p><code>-1</code> if the current result is a <code>ResultSet</code>
0985: * object or there are no more results
0986: * @exception DatabaseException if a database access error occurs
0987: * @see #execute
0988: * @since 1.0
0989: */
0990: public int getUpdateCount() throws DatabaseException {
0991: try {
0992: return mStatement.getUpdateCount();
0993: } catch (SQLException e) {
0994: handleException();
0995: throw new DatabaseException(e);
0996: }
0997: }
0998:
0999: /**
1000: * Retrieves the first warning reported by calls on this
1001: * <code>Statement</code> object. Subsequent <code>DbStatement</code>
1002: * object warnings will be chained to this <code>SQLWarning</code> object.
1003: * <p>The warning chain is automatically cleared each time a statement is
1004: * (re)executed. This method may not be called on a closed
1005: * <code>DbStatement</code> object; doing so will cause an
1006: * <code>SQLException</code> to be thrown.
1007: * <p>If an exception is thrown, this <code>DbStatement</code> is
1008: * automatically closed and an ongoing transaction will be automatically
1009: * rolled back if it belongs to the executing thread.
1010: * <p><b>Note:</b> If you are processing a <code>ResultSet</code> object,
1011: * any warnings associated with reads on that <code>ResultSet</code>
1012: * object will be chained on it rather than on the
1013: * <code>DbStatement</code> object that produced it.
1014: *
1015: * @return the first <code>SQLWarning</code> object; or
1016: * <p><code>null</code> if there are no warnings
1017: * @exception DatabaseException if a database access error occurs or this
1018: * method is called on a closed statement
1019: * @since 1.0
1020: */
1021: public SQLWarning getWarnings() throws DatabaseException {
1022: try {
1023: return mStatement.getWarnings();
1024: } catch (SQLException e) {
1025: handleException();
1026: throw new DatabaseException(e);
1027: }
1028: }
1029:
1030: /**
1031: * Retrieves the current result as a <code>ResultSet</code> object. This
1032: * method returns the internally stored result and can be called as many
1033: * times as wanted, contrary to the regular JDBC counterpart.
1034: *
1035: * @return the current result as a <code>ResultSet</code> object; or
1036: * <p><code>NULL</code> if the result is an update count.
1037: * @see #execute
1038: * @since 1.0
1039: */
1040: public DbResultSet getResultSet() {
1041: return mResultSet;
1042: }
1043:
1044: /**
1045: * Gives the driver a hint as to the direction in which rows will be
1046: * processed in <code>ResultSet</code> objects created using this
1047: * <code>DbStatement</code> object. The default value is
1048: * <code>ResultSet.FETCH_FORWARD</code>.
1049: * <p>Note that this method sets the default fetch direction for result
1050: * sets generated by this <code>DbStatement</code> object. Each result set
1051: * has its own methods for getting and setting its own fetch direction.
1052: * <p>If an exception is thrown, this <code>DbStatement</code> is
1053: * automatically closed and an ongoing transaction will be automatically
1054: * rolled back if it belongs to the executing thread.
1055: *
1056: * @param direction the initial direction for processing rows
1057: * @exception DatabaseException if a database access error occurs or the
1058: * given direction is not one of <code>ResultSet.FETCH_FORWARD</code>,
1059: * <code>ResultSet.FETCH_REVERSE</code>, or
1060: * <code>ResultSet.FETCH_UNKNOWN</code>
1061: * @see #getFetchDirection
1062: * @see ResultSet
1063: * @since 1.0
1064: */
1065: public void setFetchDirection(int direction)
1066: throws DatabaseException {
1067: try {
1068: mStatement.setFetchDirection(direction);
1069: } catch (SQLException e) {
1070: handleException();
1071: throw new DatabaseException(e);
1072: }
1073: }
1074:
1075: /**
1076: * Gives the JDBC driver a hint as to the number of rows that should be
1077: * fetched from the database when more rows are needed. The number of rows
1078: * specified affects only result sets created using this statement. If the
1079: * value specified is zero, then the hint is ignored. The default value is
1080: * zero.
1081: * <p>If an exception is thrown, this <code>DbStatement</code> is
1082: * automatically closed and an ongoing transaction will be automatically
1083: * rolled back if it belongs to the executing thread.
1084: *
1085: * @param rows the number of rows to fetch
1086: * @exception DatabaseException if a database access error occurs, or the
1087: * condition 0 <= <code>rows</code> <=
1088: * <code>this.getMaxRows()</code> is not satisfied.
1089: * @see #getFetchSize
1090: * @see #getMaxRows
1091: * @since 1.0
1092: */
1093: public void setFetchSize(int rows) throws DatabaseException {
1094: try {
1095: mStatement.setFetchSize(rows);
1096: } catch (SQLException e) {
1097: handleException();
1098: throw new DatabaseException(e);
1099: }
1100: }
1101:
1102: /**
1103: * Sets the limit for the maximum number of bytes in a
1104: * <code>ResultSet</code> column storing character or binary values to the
1105: * given number of bytes. This limit applies only to <code>BINARY</code>,
1106: * <code>VARBINARY</code>, <code>LONGVARBINARY</code>, <code>CHAR</code>,
1107: * <code>VARCHAR</code>, and <code>LONGVARCHAR</code> fields. If the limit
1108: * is exceeded, the excess data is silently discarded. For maximum
1109: * portability, use values greater than 256.
1110: * <p>If an exception is thrown, this <code>DbStatement</code> is
1111: * automatically closed and an ongoing transaction will be automatically
1112: * rolled back if it belongs to the executing thread.
1113: *
1114: * @param max the new column size limit in bytes; zero means there is no
1115: * limit
1116: * @exception DatabaseException if a database access error occurs or the
1117: * condition max >= 0 is not satisfied
1118: * @see #getMaxFieldSize
1119: * @since 1.0
1120: */
1121: public void setMaxFieldSize(int max) throws DatabaseException {
1122: try {
1123: mStatement.setMaxFieldSize(max);
1124: } catch (SQLException e) {
1125: handleException();
1126: throw new DatabaseException(e);
1127: }
1128: }
1129:
1130: /**
1131: * Sets the limit for the maximum number of rows that any
1132: * <code>ResultSet</code> object can contain to the given number. If the
1133: * limit is exceeded, the excess rows are silently dropped.
1134: * <p>If an exception is thrown, this <code>DbStatement</code> is
1135: * automatically closed and an ongoing transaction will be automatically
1136: * rolled back if it belongs to the executing thread.
1137: *
1138: * @param max the new max rows limit; zero means there is no limit
1139: * @exception DatabaseException if a database access error occurs or the
1140: * condition max >= 0 is not satisfied
1141: * @see #getMaxRows
1142: * @since 1.0
1143: */
1144: public void setMaxRows(int max) throws DatabaseException {
1145: try {
1146: mStatement.setMaxRows(max);
1147: } catch (SQLException e) {
1148: handleException();
1149: throw new DatabaseException(e);
1150: }
1151: }
1152:
1153: /**
1154: * Sets the number of seconds the driver will wait for a
1155: * <code>DbStatement</code> object to execute to the given number of
1156: * seconds. If the limit is exceeded, an <code>DatabaseException</code> is
1157: * thrown.
1158: *
1159: * @param max the new query timeout limit in seconds; zero means there is
1160: * no limit
1161: * @exception DatabaseException if a database access error occurs or the
1162: * condition seconds >= 0 is not satisfied
1163: * @see #getQueryTimeout
1164: * @since 1.0
1165: */
1166: public void setQueryTimeout(int max) throws DatabaseException {
1167: try {
1168: mStatement.setQueryTimeout(max);
1169: } catch (SQLException e) {
1170: handleException();
1171: throw new DatabaseException(e);
1172: }
1173: }
1174:
1175: /**
1176: * Returns the <code>DbConnection</code> object from which this
1177: * <code>DbStatement</code> object has been instantiated.
1178: *
1179: * @return the instantiating <code>DbConnection</code> object.
1180: * @since 1.0
1181: */
1182: public DbConnection getConnection() {
1183: return mConnection;
1184: }
1185:
1186: /**
1187: * Waits until the <code>DbConnection</code> method is available for use.
1188: * This method is used by all the execution methods to effectively
1189: * integrate with the connection pool.
1190: *
1191: * @exception DatabaseException when a database access error occurs or the
1192: * connection isn't open or has timed-out
1193: * @since 1.0
1194: */
1195: void waitForConnection() throws DatabaseException {
1196: if (mConnection.isClosed()) {
1197: mConnection.handleException();
1198: throw new DatabaseException("The connection is not open.");
1199: }
1200:
1201: while (true) {
1202: if (!mConnection.isFree()) {
1203: try {
1204: synchronized (mConnection) {
1205: mConnection.wait();
1206: }
1207: } catch (InterruptedException e) {
1208: throw new DatabaseException(
1209: "Timeout while waiting for the connection to become available.");
1210: }
1211: } else {
1212: break;
1213: }
1214: }
1215: }
1216:
1217: /**
1218: * Checks if there's a <code>ResultSet</code> object present.
1219: *
1220: * @return <code>true</code> if a <code>ResultSet</code> object is
1221: * available; or
1222: * <p><code>false</code> otherwise.
1223: * @since 1.0
1224: */
1225: boolean hasResultset() {
1226: return null != mResultSet;
1227: }
1228:
1229: /**
1230: * Set the current <code>ResultSet</code> object and cleans up the
1231: * previous <code>ResultSet</code> object automatically.
1232: *
1233: * @param resultSet the new current <code>ResultSet</code> object
1234: * @exception DatabaseException if a database access error occurred.
1235: * @since 1.0
1236: */
1237: protected void setResultset(ResultSet resultSet)
1238: throws DatabaseException {
1239: if (null == resultSet) {
1240: mResultSet = null;
1241: } else {
1242: mResultSet = wrapWithDbResultSet(resultSet);
1243: }
1244: }
1245:
1246: private DbResultSet wrapWithDbResultSet(ResultSet resultSet)
1247: throws DatabaseException {
1248: Class resulset_class = null;
1249: try {
1250: if (JavaSpecificationUtils.isAtLeastJdk16()) {
1251: resulset_class = Class.forName(DbResultSet.class
1252: .getName()
1253: + "40");
1254: } else {
1255: resulset_class = Class.forName(DbResultSet.class
1256: .getName()
1257: + "30");
1258: }
1259:
1260: Constructor constructor = resulset_class
1261: .getDeclaredConstructor(new Class[] {
1262: DbStatement.class, ResultSet.class });
1263: return (DbResultSet) constructor.newInstance(new Object[] {
1264: this , resultSet });
1265: } catch (Exception e) {
1266: handleException();
1267: throw new DatabaseException(e);
1268: }
1269: }
1270:
1271: /**
1272: * Cleans up and closes the current <code>ResultSet</code> object.
1273: *
1274: * @exception DatabaseException if a database access error occurred.
1275: * @since 1.0
1276: */
1277: void cleanResultSet() throws DatabaseException {
1278: if (null != mResultSet) {
1279: try {
1280: mResultSet.close();
1281: mResultSet = null;
1282: } catch (SQLException e) {
1283: mResultSet = null;
1284: close();
1285: throw new DatabaseException(e);
1286: }
1287: }
1288: }
1289:
1290: /**
1291: * Performs the cleanup logic in case an exeception is thrown during
1292: * execution. The statement will be closed and if a transaction is active,
1293: * it will be automatically rolled back.
1294: *
1295: * @exception DatabaseException when an error occurs during the cleanup of
1296: * the connection, or when an error occurs during the roll-back.
1297: */
1298: protected void handleException() throws DatabaseException {
1299: synchronized (this ) {
1300: try {
1301: close();
1302: } catch (DatabaseException e) {
1303: // this is a defensive close, if it can't be closed again, it
1304: // probably already is
1305: }
1306:
1307: if (mConnection.isTransactionValidForThread()) {
1308: mConnection.rollback();
1309: } else {
1310: synchronized (mConnection) {
1311: mConnection.notifyAll();
1312: }
1313: }
1314: }
1315: }
1316:
1317: /**
1318: * Ensures that this <code>DbStatement</code> is correctly closed when
1319: * it's garbage collected.
1320: *
1321: * @exception Throwable if an error occurred during the finalization
1322: * @since 1.0
1323: */
1324: protected void finalize() throws Throwable {
1325: close();
1326: super .finalize();
1327: }
1328:
1329: /**
1330: * Simply clones the instance with the default clone method. This creates
1331: * a shallow copy of all fields and the clone will in fact just be another
1332: * reference to the same underlying data. The independence of each cloned
1333: * instance is consciously not respected since they rely on resources that
1334: * can't be cloned.
1335: *
1336: * @since 1.0
1337: */
1338: public Object clone() {
1339: try {
1340: return super .clone();
1341: } catch (CloneNotSupportedException e) {
1342: // this should never happen
1343: Logger.getLogger("com.uwyn.rife.database").severe(
1344: ExceptionUtils.getExceptionStackTrace(e));
1345: return null;
1346: }
1347: }
1348: }
|