diff --git a/jOOQ-test/src/org/jooq/test/_/testcases/RecordListenerTests.java b/jOOQ-test/src/org/jooq/test/_/testcases/RecordListenerTests.java index 02f934d865..16f0065eeb 100644 --- a/jOOQ-test/src/org/jooq/test/_/testcases/RecordListenerTests.java +++ b/jOOQ-test/src/org/jooq/test/_/testcases/RecordListenerTests.java @@ -186,6 +186,7 @@ extends BaseTest events = new ArrayList(); + List exceptions = new ArrayList(); @Override public void storeStart(RecordContext ctx) { @@ -264,5 +266,10 @@ extends BaseTestRecord(s) that are being manipulated in batch - * mode. + * The Record(s) that are being manipulated in batch mode. *

- * If a single Record is being manipulated in - * non-batch mode, this will return an array of length 1, - * containing that Record. + * If a single Record is being manipulated in non-batch mode, + * this will return an array of length 1, containing that + * Record. * - * @return The Record(s) being manipulated. This is - * never null + * @return The Record(s) being manipulated. This is never + * null */ Record[] batchRecords(); + + /** + * The {@link Exception} being thrown or null. + */ + Exception exception(); + } diff --git a/jOOQ/src/main/java/org/jooq/RecordListener.java b/jOOQ/src/main/java/org/jooq/RecordListener.java index 01c129ef3f..6e9a0b3894 100644 --- a/jOOQ/src/main/java/org/jooq/RecordListener.java +++ b/jOOQ/src/main/java/org/jooq/RecordListener.java @@ -191,4 +191,9 @@ public interface RecordListener extends EventListener { * @see UpdatableRecord#refresh() */ void refreshEnd(RecordContext ctx); + + /** + * Called when an exception occurs. + */ + void exception(RecordContext ctx); } diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultRecordContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultRecordContext.java index 8ba7ba3ae6..a928e6f9c3 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultRecordContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultRecordContext.java @@ -59,6 +59,7 @@ class DefaultRecordContext implements RecordContext { private final HashMap data; private final ExecuteType type; private final Record[] records; + Exception exception; DefaultRecordContext(Configuration configuration, ExecuteType type, Record... records) { this.configuration = configuration; @@ -101,4 +102,9 @@ class DefaultRecordContext implements RecordContext { public final Record[] batchRecords() { return records; } + + @Override + public final Exception exception() { + return exception; + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultRecordListener.java b/jOOQ/src/main/java/org/jooq/impl/DefaultRecordListener.java index b641a80863..15badf2158 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultRecordListener.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultRecordListener.java @@ -89,4 +89,7 @@ public class DefaultRecordListener implements RecordListener { @Override public void refreshEnd(RecordContext ctx) {} + @Override + public void exception(RecordContext ctx) {} + } diff --git a/jOOQ/src/main/java/org/jooq/impl/RecordDelegate.java b/jOOQ/src/main/java/org/jooq/impl/RecordDelegate.java index f19b95194b..8f8a906139 100644 --- a/jOOQ/src/main/java/org/jooq/impl/RecordDelegate.java +++ b/jOOQ/src/main/java/org/jooq/impl/RecordDelegate.java @@ -48,9 +48,9 @@ import static org.jooq.impl.RecordDelegate.RecordLifecycleType.REFRESH; import org.jooq.Configuration; import org.jooq.ExecuteType; import org.jooq.Record; -import org.jooq.RecordContext; import org.jooq.RecordListener; import org.jooq.RecordListenerProvider; +import org.jooq.exception.ControlFlowSignal; /** * A stub for {@link Record} objects, abstracting {@link RecordListener} @@ -86,7 +86,7 @@ class RecordDelegate { final R operate(RecordOperation operation) throws E { RecordListenerProvider[] providers = null; RecordListener[] listeners = null; - RecordContext ctx = null; + DefaultRecordContext ctx = null; E exception = null; if (configuration != null) { @@ -103,7 +103,7 @@ class RecordDelegate { } if (listeners != null) { - for (RecordListener listener : listeners) { + for (RecordListener listener : listeners) { switch (type) { case LOAD: listener.loadStart(ctx); break; case REFRESH: listener.refreshStart(ctx); break; @@ -125,11 +125,21 @@ class RecordDelegate { // [#2770][#3036] Exceptions must not propagate before listeners receive "end" events catch (Exception e) { exception = (E) e; + + // Do not propagate these exception types to client code as they're not really "exceptions" + if (!(e instanceof ControlFlowSignal)) { + if (ctx != null) + ctx.exception = e; + + if (listeners != null) + for (RecordListener listener : listeners) + listener.exception(ctx); + } } } if (listeners != null) { - for (RecordListener listener : listeners) { + for (RecordListener listener : listeners) { switch (type) { case LOAD: listener.loadEnd(ctx); break; case REFRESH: listener.refreshEnd(ctx); break;