diff --git a/jOOQ-test/src/org/jooq/test/_/PrettyPrinter.java b/jOOQ-test/src/org/jooq/test/_/PrettyPrinter.java index cc453903e3..7e180dc7fd 100644 --- a/jOOQ-test/src/org/jooq/test/_/PrettyPrinter.java +++ b/jOOQ-test/src/org/jooq/test/_/PrettyPrinter.java @@ -65,12 +65,12 @@ public class PrettyPrinter extends DefaultExecuteListener { // Create a new factory for logging rendering purposes // This factory doesn't need a connection, only the SQLDialect... - Executor pretty = new Executor(ctx.getDialect(), + Executor pretty = new Executor(ctx.configuration().getDialect(), // ... and the flag for pretty-printing - SettingsTools.clone(ctx.getSettings()).withRenderFormatted(true)); + SettingsTools.clone(ctx.configuration().getSettings()).withRenderFormatted(true)); - Executor normal = new Executor(ctx.getDialect()); + Executor normal = new Executor(ctx.configuration().getDialect()); String n = "" + count.incrementAndGet(); diff --git a/jOOQ-test/src/org/jooq/test/_/testcases/ExecuteListenerTests.java b/jOOQ-test/src/org/jooq/test/_/testcases/ExecuteListenerTests.java index 8951d31760..2d992235bd 100644 --- a/jOOQ-test/src/org/jooq/test/_/testcases/ExecuteListenerTests.java +++ b/jOOQ-test/src/org/jooq/test/_/testcases/ExecuteListenerTests.java @@ -37,6 +37,7 @@ package org.jooq.test._.testcases; import static java.util.Arrays.asList; import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; @@ -97,6 +98,116 @@ extends BaseTest() {{ put("Foo", "Bar"); put("Bar", "Baz"); - }}, ctx.getData()); + }}, ctx.configuration().getData()); assertNull(ctx.routine()); assertEquals(ExecuteType.READ, ctx.type()); @@ -503,23 +614,23 @@ extends BaseTest bindStart = new ArrayList(); - public static List bindEnd = new ArrayList(); - public static int executeStart; - public static int executeEnd; - public static int end; + public static int start; + public static int renderStart; + public static int renderEnd; + public static int prepareStart; + public static int prepareEnd; + public static List bindStart = new ArrayList(); + public static List bindEnd = new ArrayList(); + public static int executeStart; + public static int executeEnd; + public static int end; @SuppressWarnings("serial") private void checkBase(ExecuteContext ctx) { @@ -528,12 +639,12 @@ extends BaseTest() {{ put("Foo", "Bar"); put("Bar", "Baz"); - }}, ctx.getData()); + }}, ctx.configuration().getData()); assertNull(ctx.routine()); assertNull(ctx.resultSet()); @@ -760,12 +871,12 @@ extends BaseTest() {{ put("Foo", "Bar"); put("Bar", "Baz"); - }}, ctx.getData()); + }}, ctx.configuration().getData()); assertNull(ctx.routine()); assertNull(ctx.resultSet()); diff --git a/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java b/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java index 594fb89dca..193cee0811 100644 --- a/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java +++ b/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java @@ -2041,6 +2041,11 @@ public abstract class jOOQAbstractTest< new ExecuteListenerTests(this).testExecuteListenerOnResultQuery(); } + @Test + public void testExecuteListenerWithData() throws Exception { + new ExecuteListenerTests(this).testExecuteListenerWithData(); + } + @Test public void testExecuteListenerCustomException() throws Exception { new ExecuteListenerTests(this).testExecuteListenerCustomException(); diff --git a/jOOQ/src/main/java/org/jooq/ExecuteContext.java b/jOOQ/src/main/java/org/jooq/ExecuteContext.java index d293fb5359..68d69a1c2b 100644 --- a/jOOQ/src/main/java/org/jooq/ExecuteContext.java +++ b/jOOQ/src/main/java/org/jooq/ExecuteContext.java @@ -39,6 +39,7 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.Map; import org.jooq.conf.Settings; import org.jooq.conf.StatementType; @@ -53,7 +54,58 @@ import org.jooq.exception.DataAccessException; * @author Lukas Eder * @see ExecuteListener */ -public interface ExecuteContext extends Configuration { +public interface ExecuteContext { + + /** + * Get all custom data from this ExecuteContext. + *

+ * This is custom data that was previously set to the execute context using + * {@link #data(Object, Object)}. Use custom data if you want to pass data + * between events received by an {@link ExecuteListener}. + *

+ * Unlike {@link Configuration#getData()}, these data's lifecycle only + * matches that of a single query execution. + * + * @return The custom data. This is never null + * @see ExecuteListener + */ + Map data(); + + /** + * Get some custom data from this ExecuteContext. + *

+ * This is custom data that was previously set to the execute context using + * {@link #data(Object, Object)}. Use custom data if you want to pass + * data between events received by an {@link ExecuteListener}. + *

+ * Unlike {@link Configuration#getData()}, these data's lifecycle only + * matches that of a single query execution. + * + * @param key A key to identify the custom data + * @return The custom data or null if no such data is contained + * in this ExecuteContext + * @see ExecuteListener + */ + Object data(Object key); + + /** + * Set some custom data to this ExecuteContext. + *

+ * This is custom data that was previously set to the execute context using + * {@link #data(Object, Object)}. Use custom data if you want to pass + * data between events received by an {@link ExecuteListener}. + *

+ * Unlike {@link Configuration#getData()}, these data's lifecycle only + * matches that of a single query execution. + * + * @param key A key to identify the custom data + * @param value The custom data or null to unset the custom + * data + * @return The previously set custom data or null if no data + * was previously set for the given key + * @see ExecuteListener + */ + Object data(Object key, Object value); /** * The configuration wrapped by this context @@ -63,8 +115,8 @@ public interface ExecuteContext extends Configuration { /** * The connection to be used in this execute context *

- * This returns a proxy to the {@link #getConnectionProvider()}'s supplied - * connection. This proxy takes care of two things: + * This returns a proxy to the {@link Configuration#getConnectionProvider()} + * 's supplied connection. This proxy takes care of two things: *

    *
  • It takes care of properly implementing * {@link Settings#getStatementType()}
  • diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java b/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java index 50474ee8e8..ea42c04abe 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java @@ -175,7 +175,7 @@ abstract class AbstractResultQuery extends AbstractQuery imple // [#1296] These dialects do not implement FOR UPDATE. But the same // effect can be achieved using ResultSet.CONCUR_UPDATABLE - if (isForUpdate() && asList(CUBRID, SQLSERVER).contains(ctx.getDialect())) { + if (isForUpdate() && asList(CUBRID, SQLSERVER).contains(ctx.configuration().getDialect())) { ctx.statement(ctx.connection().prepareStatement(ctx.sql(), TYPE_SCROLL_SENSITIVE, CONCUR_UPDATABLE)); } @@ -206,7 +206,7 @@ abstract class AbstractResultQuery extends AbstractQuery imple // [#706] Postgres requires two separate queries running in the same // transaction to be executed when fetching refcursor types - if (ctx.getDialect() == SQLDialect.POSTGRES && isSelectingRefCursor()) { + if (ctx.configuration().getDialect() == SQLDialect.POSTGRES && isSelectingRefCursor()) { autoCommit = connection.getAutoCommit(); if (autoCommit) { @@ -222,7 +222,7 @@ abstract class AbstractResultQuery extends AbstractQuery imple // JTDS doesn't seem to implement PreparedStatement.execute() // correctly, at least not for sp_help - if (ctx.getDialect() == ASE) { + if (ctx.configuration().getDialect() == ASE) { ctx.resultSet(ctx.statement().executeQuery()); } @@ -246,7 +246,7 @@ abstract class AbstractResultQuery extends AbstractQuery imple } } else { - result = new ResultImpl(ctx); + result = new ResultImpl(ctx.configuration()); } } @@ -258,7 +258,7 @@ abstract class AbstractResultQuery extends AbstractQuery imple while (ctx.resultSet() != null) { anyResults = true; - Field[] fields = new MetaDataFieldProvider(ctx, ctx.resultSet().getMetaData()).getFields(); + Field[] fields = new MetaDataFieldProvider(ctx.configuration(), ctx.resultSet().getMetaData()).getFields(); Cursor c = new CursorImpl(ctx, listener, fields, internIndexes(fields), true); results.add(c.fetch()); diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractStoreQuery.java b/jOOQ/src/main/java/org/jooq/impl/AbstractStoreQuery.java index 9a58fde893..0d58b8f10b 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractStoreQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractStoreQuery.java @@ -183,7 +183,7 @@ abstract class AbstractStoreQuery extends AbstractQuery implem // Just in case, always set Sybase ASE statement mode to return // Generated keys if client code wants to SELECT @@identity afterwards - if (ctx.getDialect() == SQLDialect.ASE) { + if (ctx.configuration().getDialect() == SQLDialect.ASE) { ctx.statement(connection.prepareStatement(ctx.sql(), Statement.RETURN_GENERATED_KEYS)); return; } @@ -196,7 +196,7 @@ abstract class AbstractStoreQuery extends AbstractQuery implem // Values should be returned from the INSERT else { - switch (ctx.getDialect()) { + switch (ctx.configuration().getDialect()) { // Postgres uses the RETURNING clause in SQL case FIREBIRD: @@ -247,7 +247,7 @@ abstract class AbstractStoreQuery extends AbstractQuery implem else { int result = 1; ResultSet rs; - switch (ctx.getDialect()) { + switch (ctx.configuration().getDialect()) { // SQLite can select _rowid_ after the insert case SQLITE: { @@ -255,7 +255,7 @@ abstract class AbstractStoreQuery extends AbstractQuery implem result = ctx.statement().executeUpdate(); listener.executeEnd(ctx); - Executor create = new Executor(ctx.connection(), SQLDialect.SQLITE, ctx.getSettings()); + Executor create = new Executor(ctx.connection(), SQLDialect.SQLITE, ctx.configuration().getSettings()); returned = create.select(returning) .from(getInto()) @@ -275,7 +275,7 @@ abstract class AbstractStoreQuery extends AbstractQuery implem result = ctx.statement().executeUpdate(); listener.executeEnd(ctx); - selectReturning(ctx.configuration(), create(ctx).lastID()); + selectReturning(ctx.configuration(), create(ctx.configuration()).lastID()); return result; } @@ -305,7 +305,7 @@ abstract class AbstractStoreQuery extends AbstractQuery implem } } - selectReturning(ctx, list.toArray()); + selectReturning(ctx.configuration(), list.toArray()); return result; } finally { diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultExecuteContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultExecuteContext.java index 6c95d38038..5913dec156 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultExecuteContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultExecuteContext.java @@ -43,7 +43,9 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.SQLOutput; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.jooq.Configuration; import org.jooq.ConnectionProvider; @@ -66,14 +68,11 @@ import org.jooq.tools.jdbc.JDBCUtils; * * @author Lukas Eder */ -class DefaultExecuteContext extends AbstractConfiguration implements ExecuteContext { - - /** - * Generated UID - */ - private static final long serialVersionUID = -6653474082935089963L; +class DefaultExecuteContext implements ExecuteContext { // Persistent attributes (repeatable) + private final Configuration configuration; + private final Map data; private final Query query; private final Routine routine; private String sql; @@ -224,8 +223,8 @@ class DefaultExecuteContext extends AbstractConfiguration implements ExecuteCont } private DefaultExecuteContext(Configuration configuration, Query query, Query[] batchQueries, Routine routine) { - super(configuration); - + this.configuration = configuration; + this.data = new HashMap(); this.query = query; this.batchQueries = (batchQueries == null ? new Query[0] : batchQueries); this.routine = routine; @@ -246,6 +245,21 @@ class DefaultExecuteContext extends AbstractConfiguration implements ExecuteCont LOCAL_CONFIGURATION.set(configuration); } + @Override + public final Map data() { + return data; + } + + @Override + public final Object data(Object key) { + return data.get(key); + } + + @Override + public final Object data(Object key, Object value) { + return data.put(key, value); + } + @Override public final ExecuteType type() { @@ -393,14 +407,14 @@ class DefaultExecuteContext extends AbstractConfiguration implements ExecuteCont // wrapped by a ConnectionProxy, transparently, in order to implement // Settings.getStatementType() correctly. - ConnectionProvider provider = connectionProvider != null ? connectionProvider : getConnectionProvider(); + ConnectionProvider provider = connectionProvider != null ? connectionProvider : configuration.getConnectionProvider(); if (connection == null && provider != null) { Connection c = provider.acquire(); if (c != null) { LOCAL_CONNECTION.set(c); - connection = new SettingsEnabledConnection(new ProviderEnabledConnection(provider, c), getSettings()); + connection = new SettingsEnabledConnection(new ProviderEnabledConnection(provider, c), configuration.getSettings()); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/Utils.java b/jOOQ/src/main/java/org/jooq/impl/Utils.java index 4a92923a01..9c3d550d14 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Utils.java +++ b/jOOQ/src/main/java/org/jooq/impl/Utils.java @@ -1074,15 +1074,15 @@ final class Utils { * Get a list of ExecuteListener instances (including defaults) from a * configuration */ - static final List getListeners(Configuration configuration) { + static final List getListeners(ExecuteContext ctx) { List result = new ArrayList(); - if (!FALSE.equals(configuration.getSettings().isExecuteLogging())) { + if (!FALSE.equals(ctx.configuration().getSettings().isExecuteLogging())) { result.add(new StopWatchListener()); result.add(new LoggerListener()); } - result.addAll(configuration.getExecuteListeners()); + result.addAll(ctx.configuration().getExecuteListeners()); return result; } @@ -1745,7 +1745,7 @@ final class Utils { } else if (type == BigInteger.class) { // The SQLite JDBC driver doesn't support BigDecimals - if (ctx.getDialect() == SQLDialect.SQLITE) { + if (ctx.configuration().getDialect() == SQLDialect.SQLITE) { return Convert.convert(rs.getString(index), (Class) BigInteger.class); } else { @@ -1755,7 +1755,7 @@ final class Utils { } else if (type == BigDecimal.class) { // The SQLite JDBC driver doesn't support BigDecimals - if (ctx.getDialect() == SQLDialect.SQLITE) { + if (ctx.configuration().getDialect() == SQLDialect.SQLITE) { return Convert.convert(rs.getString(index), (Class) BigDecimal.class); } else { @@ -1772,7 +1772,7 @@ final class Utils { return (T) rs.getClob(index); } else if (type == Date.class) { - return (T) getDate(ctx.getDialect(), rs, index); + return (T) getDate(ctx.configuration().getDialect(), rs, index); } else if (type == Double.class) { return (T) wasNull(rs, Double.valueOf(rs.getDouble(index))); @@ -1793,13 +1793,13 @@ final class Utils { return (T) rs.getString(index); } else if (type == Time.class) { - return (T) getTime(ctx.getDialect(), rs, index); + return (T) getTime(ctx.configuration().getDialect(), rs, index); } else if (type == Timestamp.class) { - return (T) getTimestamp(ctx.getDialect(), rs, index); + return (T) getTimestamp(ctx.configuration().getDialect(), rs, index); } else if (type == YearToMonth.class) { - if (ctx.getDialect() == POSTGRES) { + if (ctx.configuration().getDialect() == POSTGRES) { Object object = rs.getObject(index); return (T) (object == null ? null : PostgresUtils.toYearToMonth(object)); } @@ -1809,7 +1809,7 @@ final class Utils { } } else if (type == DayToSecond.class) { - if (ctx.getDialect() == POSTGRES) { + if (ctx.configuration().getDialect() == POSTGRES) { Object object = rs.getObject(index); return (T) (object == null ? null : PostgresUtils.toDayToSecond(object)); } @@ -1835,7 +1835,7 @@ final class Utils { return (T) (string == null ? null : ULong.valueOf(string)); } else if (type == UUID.class) { - switch (ctx.getDialect()) { + switch (ctx.configuration().getDialect()) { // [#1624] Some JDBC drivers natively support the // java.util.UUID data type @@ -1859,7 +1859,7 @@ final class Utils { // The type byte[] is handled earlier. byte[][] can be handled here else if (type.isArray()) { - switch (ctx.getDialect()) { + switch (ctx.configuration().getDialect()) { case POSTGRES: { return pgGetArray(ctx, type, index); } @@ -1871,13 +1871,13 @@ final class Utils { } } else if (ArrayRecord.class.isAssignableFrom(type)) { - return (T) getArrayRecord(ctx, rs.getArray(index), (Class>) type); + return (T) getArrayRecord(ctx.configuration(), rs.getArray(index), (Class>) type); } else if (EnumType.class.isAssignableFrom(type)) { return getEnumType(type, rs.getString(index)); } else if (UDTRecord.class.isAssignableFrom(type)) { - switch (ctx.getDialect()) { + switch (ctx.configuration().getDialect()) { case POSTGRES: return (T) pgNewUDTRecord(type, rs.getObject(index)); } @@ -1886,7 +1886,7 @@ final class Utils { } else if (Result.class.isAssignableFrom(type)) { ResultSet nested = (ResultSet) rs.getObject(index); - return (T) new Executor(ctx).fetch(nested); + return (T) new Executor(ctx.configuration()).fetch(nested); } else { return (T) rs.getObject(index); @@ -2100,7 +2100,7 @@ final class Utils { return (T) stmt.getTimestamp(index); } else if (type == YearToMonth.class) { - if (ctx.getDialect() == POSTGRES) { + if (ctx.configuration().getDialect() == POSTGRES) { Object object = stmt.getObject(index); return (T) (object == null ? null : PostgresUtils.toYearToMonth(object)); } @@ -2110,7 +2110,7 @@ final class Utils { } } else if (type == DayToSecond.class) { - if (ctx.getDialect() == POSTGRES) { + if (ctx.configuration().getDialect() == POSTGRES) { Object object = stmt.getObject(index); return (T) (object == null ? null : PostgresUtils.toDayToSecond(object)); } @@ -2136,7 +2136,7 @@ final class Utils { return (T) (string == null ? null : ULong.valueOf(string)); } else if (type == UUID.class) { - switch (ctx.getDialect()) { + switch (ctx.configuration().getDialect()) { // [#1624] Some JDBC drivers natively support the // java.util.UUID data type @@ -2163,13 +2163,13 @@ final class Utils { return (T) convertArray(stmt.getObject(index), (Class)type); } else if (ArrayRecord.class.isAssignableFrom(type)) { - return (T) getArrayRecord(ctx, stmt.getArray(index), (Class>) type); + return (T) getArrayRecord(ctx.configuration(), stmt.getArray(index), (Class>) type); } else if (EnumType.class.isAssignableFrom(type)) { return getEnumType(type, stmt.getString(index)); } else if (UDTRecord.class.isAssignableFrom(type)) { - switch (ctx.getDialect()) { + switch (ctx.configuration().getDialect()) { case POSTGRES: return (T) pgNewUDTRecord(type, stmt.getObject(index)); } @@ -2178,7 +2178,7 @@ final class Utils { } else if (Result.class.isAssignableFrom(type)) { ResultSet nested = (ResultSet) stmt.getObject(index); - return (T) new Executor(ctx).fetch(nested); + return (T) new Executor(ctx.configuration()).fetch(nested); } else { return (T) stmt.getObject(index);