diff --git a/jOOQ-test/src/org/jooq/test/_/testcases/ExecuteListenerTests.java b/jOOQ-test/src/org/jooq/test/_/testcases/ExecuteListenerTests.java index beeee09b06..36fccb9b75 100644 --- a/jOOQ-test/src/org/jooq/test/_/testcases/ExecuteListenerTests.java +++ b/jOOQ-test/src/org/jooq/test/_/testcases/ExecuteListenerTests.java @@ -53,7 +53,6 @@ import java.util.List; import java.util.Queue; import org.jooq.ExecuteContext; -import org.jooq.ExecuteListener; import org.jooq.ExecuteType; import org.jooq.Field; import org.jooq.Result; @@ -61,6 +60,7 @@ import org.jooq.TableRecord; import org.jooq.UpdatableRecord; import org.jooq.conf.Settings; import org.jooq.conf.SettingsTools; +import org.jooq.impl.DefaultExecuteListener; import org.jooq.impl.Factory; import org.jooq.test.BaseTest; import org.jooq.test.jOOQAbstractTest; @@ -92,6 +92,26 @@ extends BaseTest + * This may have no effect, if called at the wrong moment. * * @see ExecuteListener#renderEnd(ExecuteContext) * @see ExecuteListener#prepareStart(ExecuteContext) @@ -133,8 +136,9 @@ public interface ExecuteContext extends Configuration { Connection getConnection(); /** - * Override the {@link Connection} that is being used for execution. This - * may have no effect, if called at the wrong moment. + * Override the {@link Connection} that is being used for execution. + *

+ * This may have no effect, if called at the wrong moment. * * @see ExecuteListener#start(ExecuteContext) */ @@ -161,8 +165,9 @@ public interface ExecuteContext extends Configuration { PreparedStatement statement(); /** - * Override the {@link PreparedStatement} that is being executed. This may - * have no effect, if called at the wrong moment. + * Override the {@link PreparedStatement} that is being executed. + *

+ * This may have no effect, if called at the wrong moment. * * @see ExecuteListener#prepareEnd(ExecuteContext) * @see ExecuteListener#bindStart(ExecuteContext) @@ -176,8 +181,9 @@ public interface ExecuteContext extends Configuration { ResultSet resultSet(); /** - * Override the {@link ResultSet} that is being fetched. This may have no - * effect, if called at the wrong moment. + * Override the {@link ResultSet} that is being fetched. + *

+ * This may have no effect, if called at the wrong moment. * * @see ExecuteListener#executeEnd(ExecuteContext) * @see ExecuteListener#fetchStart(ExecuteContext) @@ -205,4 +211,31 @@ public interface ExecuteContext extends Configuration { * Calling this has no effect. It is being used by jOOQ internally. */ void result(Result result); + + /** + * The {@link RuntimeException} being thrown. + */ + RuntimeException exception(); + + /** + * Override the {@link RuntimeException} being thrown. + *

+ * This may have no effect, if called at the wrong moment. + */ + void exception(RuntimeException e); + + /** + * The {@link SQLException} that was thrown by the database. + */ + SQLException sqlException(); + + /** + * Override the {@link SQLException} being thrown. + *

+ * Any SQLException will be wrapped by jOOQ using an unchecked + * {@link DataAccessException}. To have jOOQ throw your own custom + * {@link RuntimeException}, use {@link #exception(RuntimeException)} + * instead. This may have no effect, if called at the wrong moment. + */ + void sqlException(SQLException e); } diff --git a/jOOQ/src/main/java/org/jooq/ExecuteListener.java b/jOOQ/src/main/java/org/jooq/ExecuteListener.java index 696787b08c..d4a0099482 100644 --- a/jOOQ/src/main/java/org/jooq/ExecuteListener.java +++ b/jOOQ/src/main/java/org/jooq/ExecuteListener.java @@ -38,6 +38,7 @@ package org.jooq; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; +import java.sql.SQLException; import org.jooq.conf.Settings; import org.jooq.conf.StatementType; @@ -56,11 +57,11 @@ import org.jooq.tools.StopWatchListener; * Settings to * {@link Factory#Factory(java.sql.Connection, SQLDialect, Settings)}. Advanced * ExecuteListeners can also provide custom implementations of - * {@link Connection}, {@link PreparedStatement} and {@link ResultSet} to jOOQ - * in apropriate methods. For convenience, consider extending - * {@link DefaultExecuteListener} instead of implementing this interface. This - * will prevent compilation errors in future versions of jOOQ, when this - * interface might get new methods. + * {@link Connection}, {@link PreparedStatement}, {@link ResultSet}, + * {@link SQLException} or {@link RuntimeException} to jOOQ in apropriate + * methods. For convenience, consider extending {@link DefaultExecuteListener} + * instead of implementing this interface. This will prevent compilation errors + * in future versions of jOOQ, when this interface might get new methods. *

* The following table explains how every type of statement / operation invokes * callback methods in the correct order for all registered @@ -220,6 +221,15 @@ import org.jooq.tools.StopWatchListener; * Yes, 1x * Yes, 1x * + * + * {@link #exception(ExecuteContext)} + * Maybe, 1x + * Maybe, 1x + * Maybe, 1x + * Maybe, 1x + * Maybe, 1x + * Maybe, 1x + * * *
*

Legend:

@@ -236,7 +246,8 @@ import org.jooq.tools.StopWatchListener; * *

* If nothing is specified, the default is to use {@link LoggerListener} and - * {@link StopWatchListener} as the only event listeners. + * {@link StopWatchListener} as the only event listeners, as configured in + * {@link Settings#isExecuteLogging()} * * @author Lukas Eder */ @@ -808,7 +819,7 @@ public interface ExecuteListener { void fetchEnd(ExecuteContext ctx); /** - * Called at the end of the execution lifecycle.. + * Called at the end of the execution lifecycle. *

* Available attributes from ExecuteContext: *

*/ void end(ExecuteContext ctx); + + /** + * Called in the event of an exception at any moment of the execution + * lifecycle. + *

+ * Available attributes from ExecuteContext: + *

+ */ + void exception(ExecuteContext ctx); } diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractBindContext.java b/jOOQ/src/main/java/org/jooq/impl/AbstractBindContext.java index fdf661ce9c..6d8541aa3c 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractBindContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractBindContext.java @@ -146,7 +146,7 @@ abstract class AbstractBindContext extends AbstractContext implemen return bindValue0(value, type); } catch (SQLException e) { - throw Util.translate("DefaultBindContext.bindValue", null, e); + throw Util.translate(null, e); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractQuery.java b/jOOQ/src/main/java/org/jooq/impl/AbstractQuery.java index f150fda5f5..5f224ca249 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractQuery.java @@ -147,7 +147,9 @@ abstract class AbstractQuery extends AbstractQueryPart implements Query { return result; } catch (SQLException e) { - throw Util.translate("AbstractQuery.execute", ctx.sql(), e); + ctx.sqlException(e); + listener.exception(ctx); + throw ctx.exception(); } finally { if (!keepStatementOpen()) { diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractQueryPart.java b/jOOQ/src/main/java/org/jooq/impl/AbstractQueryPart.java index 146a4c78cf..0c418c4ed3 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractQueryPart.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractQueryPart.java @@ -281,8 +281,19 @@ abstract class AbstractQueryPart implements QueryPartInternal, AttachableInterna /** * Internal convenience method + * + * @deprecated - 2.3.0 - Do not reuse */ + @SuppressWarnings("unused") + @Deprecated protected final DataAccessException translate(String task, String sql, SQLException e) { - return Util.translate(task, sql, e); + return translate(sql, e); + } + + /** + * Internal convenience method + */ + protected final DataAccessException translate(String sql, SQLException e) { + return Util.translate(sql, e); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java b/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java index f44021d9a1..a31f3f9c35 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java @@ -329,7 +329,9 @@ public abstract class AbstractRoutine extends AbstractSchemaProviderQueryPart return 0; } catch (SQLException e) { - throw translate("AbstractRoutine.executeCallableStatement", ctx.sql(), e); + ctx.sqlException(e); + listener.exception(ctx); + throw ctx.exception(); } finally { Util.safeClose(listener, ctx); diff --git a/jOOQ/src/main/java/org/jooq/impl/BatchMultiple.java b/jOOQ/src/main/java/org/jooq/impl/BatchMultiple.java index b3fd289f6f..56fd31df3b 100644 --- a/jOOQ/src/main/java/org/jooq/impl/BatchMultiple.java +++ b/jOOQ/src/main/java/org/jooq/impl/BatchMultiple.java @@ -93,7 +93,9 @@ class BatchMultiple implements Batch { return result; } catch (SQLException e) { - throw Util.translate("BatchMultiple.execute", ctx.sql(), e); + ctx.sqlException(e); + listener.exception(ctx); + throw ctx.exception(); } finally { Util.safeClose(listener, ctx); diff --git a/jOOQ/src/main/java/org/jooq/impl/BatchSingle.java b/jOOQ/src/main/java/org/jooq/impl/BatchSingle.java index 83e580e037..4f968a3bee 100644 --- a/jOOQ/src/main/java/org/jooq/impl/BatchSingle.java +++ b/jOOQ/src/main/java/org/jooq/impl/BatchSingle.java @@ -131,7 +131,9 @@ class BatchSingle implements BatchBindStep { return result; } catch (SQLException e) { - throw Util.translate("BatchSingle.execute", ctx.sql(), e); + ctx.sqlException(e); + listener.exception(ctx); + throw ctx.exception(); } finally { Util.safeClose(listener, ctx); diff --git a/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java b/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java index 6ab870f25e..63b124f8fd 100644 --- a/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java @@ -1267,7 +1267,9 @@ class CursorImpl implements Cursor { } } catch (SQLException e) { - throw Util.translate("Cursor.fetch", ctx.sql(), e); + ctx.sqlException(e); + listener.exception(ctx); + throw ctx.exception(); } // Conveniently close cursors and underlying objects after the last diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultExecuteContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultExecuteContext.java index 9832bfbeeb..9016765549 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultExecuteContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultExecuteContext.java @@ -39,6 +39,7 @@ import java.sql.Blob; import java.sql.Clob; import java.sql.PreparedStatement; import java.sql.ResultSet; +import java.sql.SQLException; import java.util.ArrayList; import java.util.List; @@ -68,7 +69,7 @@ class DefaultExecuteContext extends AbstractConfiguration implements ExecuteCont */ private static final long serialVersionUID = -6653474082935089963L; - // Persistent attributes + // Persistent attributes (repeatable) private final Query query; private final Routine routine; private String sql; @@ -76,16 +77,18 @@ class DefaultExecuteContext extends AbstractConfiguration implements ExecuteCont private final Query[] batchQueries; private final String[] batchSQL; - // Transient attributes + // Transient attributes (created afresh per execution) private transient PreparedStatement statement; private transient ResultSet resultSet; private transient Record record; private transient Result result; + private transient SQLException sqlException; + private transient RuntimeException exception; // ------------------------------------------------------------------------ // XXX: Static utility methods for handling blob / clob lifecycle // ------------------------------------------------------------------------ - + private static final ThreadLocal> BLOBS = new ThreadLocal>(); private static final ThreadLocal> CLOBS = new ThreadLocal>(); @@ -335,4 +338,25 @@ class DefaultExecuteContext extends AbstractConfiguration implements ExecuteCont public final Result result() { return result; } + + @Override + public final RuntimeException exception() { + return exception; + } + + @Override + public final void exception(RuntimeException e) { + this.exception = e; + } + + @Override + public final SQLException sqlException() { + return sqlException; + } + + @Override + public final void sqlException(SQLException e) { + this.sqlException = e; + exception(Util.translate(sql(), e)); + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultExecuteListener.java b/jOOQ/src/main/java/org/jooq/impl/DefaultExecuteListener.java index 5871aa7066..1892d4f6f6 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultExecuteListener.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultExecuteListener.java @@ -96,4 +96,7 @@ public class DefaultExecuteListener implements ExecuteListener { @Override public void end(ExecuteContext ctx) {} + @Override + public void exception(ExecuteContext ctx) {} + } diff --git a/jOOQ/src/main/java/org/jooq/impl/ExecuteListeners.java b/jOOQ/src/main/java/org/jooq/impl/ExecuteListeners.java index 50a0c020ec..6213885704 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ExecuteListeners.java +++ b/jOOQ/src/main/java/org/jooq/impl/ExecuteListeners.java @@ -187,4 +187,11 @@ class ExecuteListeners implements ExecuteListener { listener.end(ctx); } } + + @Override + public final void exception(ExecuteContext ctx) { + for (ExecuteListener listener : listeners) { + listener.exception(ctx); + } + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/Factory.java b/jOOQ/src/main/java/org/jooq/impl/Factory.java index 9a3e575505..02e13cfc2d 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Factory.java +++ b/jOOQ/src/main/java/org/jooq/impl/Factory.java @@ -1238,7 +1238,9 @@ public class Factory implements FactoryOperations { return new CursorImpl(ctx, listener, fields).fetch(); } catch (SQLException e) { - throw Util.translate("Factory.fetch", ctx.sql(), e); + ctx.sqlException(e); + listener.exception(ctx); + throw ctx.exception(); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/LoaderImpl.java b/jOOQ/src/main/java/org/jooq/impl/LoaderImpl.java index 63f8b0e89c..9e75a373ae 100644 --- a/jOOQ/src/main/java/org/jooq/impl/LoaderImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/LoaderImpl.java @@ -434,7 +434,7 @@ class LoaderImpl> implements // SQLExceptions originating from rollbacks or commits are always fatal // They are propagated, and not swallowed catch (SQLException e) { - throw Util.translate("LoaderImpl.executeCSV", null, e); + throw Util.translate(null, e); } finally { reader.close(); diff --git a/jOOQ/src/main/java/org/jooq/impl/MetaDataFieldProvider.java b/jOOQ/src/main/java/org/jooq/impl/MetaDataFieldProvider.java index 184ca9eb46..80927b02bb 100644 --- a/jOOQ/src/main/java/org/jooq/impl/MetaDataFieldProvider.java +++ b/jOOQ/src/main/java/org/jooq/impl/MetaDataFieldProvider.java @@ -119,7 +119,7 @@ class MetaDataFieldProvider implements FieldProvider, Serializable { } } catch (SQLException e) { - throw Util.translate("MetaFieldProvider.init", null, e); + throw Util.translate(null, e); } meta = null; diff --git a/jOOQ/src/main/java/org/jooq/impl/Util.java b/jOOQ/src/main/java/org/jooq/impl/Util.java index bcd8afa2cd..745d2763d3 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Util.java +++ b/jOOQ/src/main/java/org/jooq/impl/Util.java @@ -491,8 +491,8 @@ final class Util { /** * Translate a {@link SQLException} to a {@link DataAccessException} */ - static final DataAccessException translate(String task, String sql, SQLException e) { - String message = task + "; SQL [" + sql + "]; " + e.getMessage(); + static final DataAccessException translate(String sql, SQLException e) { + String message = "SQL [" + sql + "]; " + e.getMessage(); return new DataAccessException(message, e); } diff --git a/jOOQ/src/main/java/org/jooq/tools/StopWatchListener.java b/jOOQ/src/main/java/org/jooq/tools/StopWatchListener.java index 2a496beede..882ca08b7b 100644 --- a/jOOQ/src/main/java/org/jooq/tools/StopWatchListener.java +++ b/jOOQ/src/main/java/org/jooq/tools/StopWatchListener.java @@ -127,4 +127,9 @@ public class StopWatchListener implements ExecuteListener { public void end(ExecuteContext ctx) { watch.splitDebug("Finishing"); } + + @Override + public void exception(ExecuteContext ctx) { + watch.splitDebug("Exception"); + } }