diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultBindContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultBindContext.java index a885565259..9085bde167 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultBindContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultBindContext.java @@ -54,7 +54,6 @@ import java.sql.Connection; import java.sql.Date; import java.sql.PreparedStatement; import java.sql.SQLException; -import java.sql.SQLOutput; import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; @@ -81,22 +80,10 @@ class DefaultBindContext extends AbstractBindContext { /** * Generated UID */ - private static final long serialVersionUID = -5457385919209241505L; - private static final JooqLogger log = JooqLogger.getLogger(DefaultBindContext.class); + private static final long serialVersionUID = -5457385919209241505L; + private static final JooqLogger log = JooqLogger.getLogger(DefaultBindContext.class); - /** - * The localConfiguration is used to communicate a Configuration to an - * {@link ArrayRecord}, in case that ArrayRecord is serialised to a - * {@link SQLOutput} object. - *

- * This is probably the only solution to circumvent this bad JDBC design. - * See also http://stackoverflow - * .com/q/11439543/521799 - */ - static final ThreadLocal LOCAL_CONFIGURATION = new ThreadLocal(); - - private final PreparedStatement stmt; + private final PreparedStatement stmt; DefaultBindContext(Configuration configuration, PreparedStatement stmt) { super(configuration); @@ -305,15 +292,7 @@ class DefaultBindContext extends AbstractBindContext { stmt.setString(nextIndex(), ((EnumType) value).getLiteral()); } else { - try { - // [#1544] Set the local configuration, in case an array needs - // to be serialised to SQLOutput - LOCAL_CONFIGURATION.set(this); - stmt.setObject(nextIndex(), value); - } - finally { - LOCAL_CONFIGURATION.remove(); - } + stmt.setObject(nextIndex(), value); } return this; diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultExecuteContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultExecuteContext.java index 1568a2a37d..e9f088a6f4 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultExecuteContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultExecuteContext.java @@ -37,9 +37,11 @@ package org.jooq.impl; import java.sql.Blob; import java.sql.Clob; +import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.SQLOutput; import java.util.ArrayList; import java.util.List; @@ -93,7 +95,9 @@ class DefaultExecuteContext extends AbstractConfiguration implements ExecuteCont private static final ThreadLocal> CLOBS = new ThreadLocal>(); /** - * Clean up blobs and clobs. + * Clean up blobs, clobs and the local configuration. + *

+ *

BLOBS and CLOBS

*

* [#1326] This is necessary in those dialects that have long-lived * temporary lob objects, which can cause memory leaks in certain contexts, @@ -106,6 +110,22 @@ class DefaultExecuteContext extends AbstractConfiguration implements ExecuteCont *

  • Not freeing the lob after execution will cause an * {@link OutOfMemoryError}
  • * + *

    + *

    Local configuration

    + *

    + * [#1544] There exist some corner-cases regarding the {@link SQLOutput} + * API, used for UDT serialisation / deserialisation, which have no elegant + * solutions of obtaining a {@link Configuration} and thus a JDBC + * {@link Connection} object short of: + *

    + * + * @see http://stackoverflow.com/q/11439543/521799 */ static final void clean() { List blobs = BLOBS.get(); @@ -126,6 +146,8 @@ class DefaultExecuteContext extends AbstractConfiguration implements ExecuteCont CLOBS.remove(); } + + LOCAL_CONFIGURATION.remove(); } /** @@ -142,6 +164,30 @@ class DefaultExecuteContext extends AbstractConfiguration implements ExecuteCont CLOBS.get().add(clob); } + // ------------------------------------------------------------------------ + // XXX: Static utility methods for handling Configuration lifecycle + // ------------------------------------------------------------------------ + + private static final ThreadLocal LOCAL_CONFIGURATION = new ThreadLocal(); + + /** + * Register a configuration for later cleanup with {@link #clean()} + */ + static final void register(Configuration configuration) { + LOCAL_CONFIGURATION.set(configuration); + } + + /** + * Get the registered configuration + *

    + * It can be safely assumed that such a configuration is available once the + * {@link ExecuteContext} has been established, until the statement is + * closed. + */ + static final Configuration registeredConfiguration() { + return LOCAL_CONFIGURATION.get(); + } + // ------------------------------------------------------------------------ // XXX: Constructors // ------------------------------------------------------------------------ @@ -182,6 +228,7 @@ class DefaultExecuteContext extends AbstractConfiguration implements ExecuteCont clean(); BLOBS.set(new ArrayList()); CLOBS.set(new ArrayList()); + LOCAL_CONFIGURATION.set(configuration); } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/Executor.java b/jOOQ/src/main/java/org/jooq/impl/Executor.java index 091afd3874..77dbd9cfcf 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Executor.java +++ b/jOOQ/src/main/java/org/jooq/impl/Executor.java @@ -160,7 +160,6 @@ public class Executor implements FactoryOperations { private static final long serialVersionUID = 2681360188806309513L; private static final JooqLogger log = JooqLogger.getLogger(Factory.class); - private static final Executor[] DEFAULT_INSTANCES = new Executor[SQLDialect.values().length]; private final Configuration configuration; // ------------------------------------------------------------------------- @@ -1558,19 +1557,6 @@ public class Executor implements FactoryOperations { return configuration.toString(); } - static { - for (SQLDialect dialect : SQLDialect.values()) { - DEFAULT_INSTANCES[dialect.ordinal()] = new Executor(dialect); - } - } - - /** - * Get a default Factory without a {@link Connection} - */ - final static Executor getNewFactory(SQLDialect dialect) { - return getNewFactory(DEFAULT_INSTANCES[dialect.ordinal()]); - } - /** * Get a default Factory with a {@link Connection} */ diff --git a/jOOQ/src/main/java/org/jooq/impl/FieldTypeHelper.java b/jOOQ/src/main/java/org/jooq/impl/FieldTypeHelper.java index 00f04e89ff..426bce24c0 100644 --- a/jOOQ/src/main/java/org/jooq/impl/FieldTypeHelper.java +++ b/jOOQ/src/main/java/org/jooq/impl/FieldTypeHelper.java @@ -346,7 +346,7 @@ public final class FieldTypeHelper { // [#1544] We can safely assume that localConfiguration has been // set on DefaultBindContext, prior to serialising arrays to SQLOut - Connection connection = getDriverConnection(DefaultBindContext.LOCAL_CONFIGURATION.get()); + Connection connection = getDriverConnection(DefaultExecuteContext.registeredConfiguration()); ArrayRecord arrayRecord = (ArrayRecord) value; stream.writeArray(on(connection).call("createARRAY", arrayRecord.getName(), arrayRecord.get()).get()); } diff --git a/jOOQ/src/main/java/org/jooq/impl/UDTRecordImpl.java b/jOOQ/src/main/java/org/jooq/impl/UDTRecordImpl.java index 650277f578..c6e812d4ba 100644 --- a/jOOQ/src/main/java/org/jooq/impl/UDTRecordImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/UDTRecordImpl.java @@ -76,7 +76,7 @@ public class UDTRecordImpl> extends AbstractRecord implem // [#1693] This needs to return the fully qualified SQL type name, in // case the connected user is not the owner of the UDT - Configuration configuration = DefaultBindContext.LOCAL_CONFIGURATION.get(); + Configuration configuration = DefaultExecuteContext.registeredConfiguration(); if (configuration != null) { Schema schema = Util.getMappedSchema(configuration, getUDT().getSchema()); @@ -92,10 +92,8 @@ public class UDTRecordImpl> extends AbstractRecord implem @Override public final void readSQL(SQLInput stream, String typeName) throws SQLException { - Executor configuration = Executor.getNewFactory(getUDT().getDataType().getDialect()); - for (Field field : getUDT().getFields()) { - setValue(configuration, stream, field); + setValue(DefaultExecuteContext.registeredConfiguration(), stream, field); } }