From 203f1b95db5e434ddc82a12d519aa16ede459ebb Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Tue, 6 Apr 2021 09:14:21 +0200 Subject: [PATCH] [jOOQ/jOOQ#11755] Extract AttachableQueryPart from Query --- .../java/org/jooq/AttachableQueryPart.java | 169 ++++++++++++++++++ jOOQ/src/main/java/org/jooq/Queries.java | 2 +- jOOQ/src/main/java/org/jooq/Query.java | 113 +----------- .../impl/AbstractAttachableQueryPart.java | 136 ++++++++++++++ .../java/org/jooq/impl/AbstractQuery.java | 64 +------ .../main/java/org/jooq/impl/QueriesImpl.java | 38 ++-- jOOQ/src/main/java/org/jooq/impl/Tools.java | 26 +-- 7 files changed, 336 insertions(+), 212 deletions(-) create mode 100644 jOOQ/src/main/java/org/jooq/AttachableQueryPart.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/AbstractAttachableQueryPart.java diff --git a/jOOQ/src/main/java/org/jooq/AttachableQueryPart.java b/jOOQ/src/main/java/org/jooq/AttachableQueryPart.java new file mode 100644 index 0000000000..177674d9a6 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/AttachableQueryPart.java @@ -0,0 +1,169 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq; + +import java.sql.PreparedStatement; +import java.util.List; +import java.util.Map; + +import org.jooq.conf.ParamType; +import org.jooq.conf.Settings; +import org.jooq.conf.StatementType; +import org.jooq.impl.DSL; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A type that is both {@link Attachable} and a {@link QueryPart}. + *

+ * In addition to being able to render SQL of possibly some unspecified dialect + * and with inlined bind values for debugging purposes via the + * {@link QueryPart#toString()} method, this type offers more fine-grained + * control over the SQL generation via {@link ParamType}, + * {@link Configuration#dialect()} and various {@link Configuration#settings()}. + * + * @author Lukas Eder + */ +public interface AttachableQueryPart extends Attachable, QueryPart { + + /** + * Retrieve the SQL code rendered by this Query. + *

+ * Use this method, when you want to use jOOQ for object oriented query + * creation, but execute the query with some other technology, such as + *

+ *

+ * Note, this is the same as calling {@link #getSQL(ParamType)}. The + * parameter will depend on your {@link DSLContext}'s {@link Settings}: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
StatementTypeboolean parametereffect
{@link StatementType#PREPARED_STATEMENT}false (default)This will render bind variables to be used with a JDBC + * {@link PreparedStatement}. You can extract bind values from this + * Query using {@link #getBindValues()}
{@link StatementType#STATIC_STATEMENT}trueThis will inline all bind variables in a statement to be used with a + * JDBC {@link Statement}
+ *

+ * [#1520] Note that the query actually being executed might not contain any + * bind variables, in case the number of bind variables exceeds your SQL + * dialect's maximum number of supported bind variables. This is not + * reflected by this method, which will only use the {@link Settings} to + * decide whether to render bind values. + * + * @see #getSQL(ParamType) + */ + @NotNull + String getSQL(); + + /** + * Retrieve the SQL code rendered by this Query. + *

+ * [#1520] Note that the query actually being executed might not contain any + * bind variables, in case the number of bind variables exceeds your SQL + * dialect's maximum number of supported bind variables. This is not + * reflected by this method, which will only use paramType + * argument to decide whether to render bind values. + *

+ * See {@link #getSQL()} for more details. + * + * @param paramType How to render parameters. This overrides values in + * {@link Settings#getStatementType()} + * @return The generated SQL + */ + @NotNull + String getSQL(ParamType paramType); + + /** + * Retrieve the bind values that will be bound by this Query. + *

+ * Unlike {@link #getParams()}, which returns also inlined parameters, this + * returns only actual bind values that will render an actual bind value as + * a question mark "?" + * + * @see DSLContext#extractBindValues(QueryPart) + */ + @NotNull + List getBindValues(); + + /** + * Get a Map of named parameters. The Map itself + * cannot be modified, but the {@link Param} elements allow for modifying + * bind values on an existing {@link Query}. + *

+ * Bind values created with {@link DSL#val(Object)} will have their bind + * index as name. + * + * @see Param + * @see DSL#param(String, Object) + * @see DSLContext#extractParams(QueryPart) + */ + @NotNull + Map> getParams(); + + /** + * Get a named parameter from the {@link Query}, provided its name. + *

+ * Bind values created with {@link DSL#val(Object)} will have their bind + * index as name. + * + * @see Param + * @see DSL#param(String, Object) + * @see DSLContext#extractParam(QueryPart, String) + */ + @Nullable + Param getParam(String name); + +} diff --git a/jOOQ/src/main/java/org/jooq/Queries.java b/jOOQ/src/main/java/org/jooq/Queries.java index 278d8868d6..06952554b6 100644 --- a/jOOQ/src/main/java/org/jooq/Queries.java +++ b/jOOQ/src/main/java/org/jooq/Queries.java @@ -52,7 +52,7 @@ import org.jetbrains.annotations.NotNull; * * @author Lukas Eder */ -public interface Queries extends QueryPart, Attachable, Iterable { +public interface Queries extends AttachableQueryPart, Iterable { // ------------------------------------------------------------------------ // Access API diff --git a/jOOQ/src/main/java/org/jooq/Query.java b/jOOQ/src/main/java/org/jooq/Query.java index 9ee09c4159..a7bc2627d3 100644 --- a/jOOQ/src/main/java/org/jooq/Query.java +++ b/jOOQ/src/main/java/org/jooq/Query.java @@ -39,20 +39,15 @@ package org.jooq; import java.sql.PreparedStatement; -import java.util.List; -import java.util.Map; import java.util.concurrent.CompletionStage; import java.util.concurrent.Executor; -import org.jooq.conf.ParamType; -import org.jooq.conf.Settings; import org.jooq.conf.StatementType; import org.jooq.exception.DataAccessException; import org.jooq.exception.DataTypeException; import org.jooq.impl.DSL; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; /** * Any query. @@ -62,7 +57,7 @@ import org.jetbrains.annotations.Nullable; * * @author Lukas Eder */ -public interface Query extends Statement, Attachable, AutoCloseable { +public interface Query extends Statement, AttachableQueryPart, AutoCloseable { /** * Execute the query, if it has been created with a proper configuration. @@ -129,112 +124,6 @@ public interface Query extends Statement, Attachable, AutoCloseable { */ boolean isExecutable(); - /** - * Retrieve the SQL code rendered by this Query. - *

- * Use this method, when you want to use jOOQ for object oriented query - * creation, but execute the query with some other technology, such as - *

    - *
  • JDBC
  • - *
  • Spring Templates
  • - *
  • JPA native queries
  • - *
  • etc...
  • - *
- *

- * Note, this is the same as calling {@link #getSQL(boolean)}. The boolean - * parameter will depend on your {@link DSLContext}'s {@link Settings}: - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
StatementTypeboolean parametereffect
{@link StatementType#PREPARED_STATEMENT}false (default)This will render bind variables to be used with a JDBC - * {@link PreparedStatement}. You can extract bind values from this - * Query using {@link #getBindValues()}
{@link StatementType#STATIC_STATEMENT}trueThis will inline all bind variables in a statement to be used with a - * JDBC {@link Statement}
- *

- * [#1520] Note that the query actually being executed might not contain any - * bind variables, in case the number of bind variables exceeds your SQL - * dialect's maximum number of supported bind variables. This is not - * reflected by this method, which will only use the {@link Settings} to - * decide whether to render bind values. - * - * @see #getSQL(boolean) - */ - @NotNull - String getSQL(); - - /** - * Retrieve the SQL code rendered by this Query. - *

- * [#1520] Note that the query actually being executed might not contain any - * bind variables, in case the number of bind variables exceeds your SQL - * dialect's maximum number of supported bind variables. This is not - * reflected by this method, which will only use paramType - * argument to decide whether to render bind values. - *

- * See {@link #getSQL()} for more details. - * - * @param paramType How to render parameters. This overrides values in - * {@link Settings#getStatementType()} - * @return The generated SQL - */ - @NotNull - String getSQL(ParamType paramType); - - /** - * Retrieve the bind values that will be bound by this Query. This - * List cannot be modified. To modify bind values, use - * {@link #getParams()} instead. - *

- * Unlike {@link #getParams()}, which returns also inlined parameters, this - * returns only actual bind values that will render an actual bind value as - * a question mark "?" - * - * @see DSLContext#extractBindValues(QueryPart) - */ - @NotNull - List getBindValues(); - - /** - * Get a Map of named parameters. The Map itself - * cannot be modified, but the {@link Param} elements allow for modifying - * bind values on an existing {@link Query}. - *

- * Bind values created with {@link DSL#val(Object)} will have their bind - * index as name. - * - * @see Param - * @see DSL#param(String, Object) - * @see DSLContext#extractParams(QueryPart) - */ - @NotNull - Map> getParams(); - - /** - * Get a named parameter from the {@link Query}, provided its name. - *

- * Bind values created with {@link DSL#val(Object)} will have their bind - * index as name. - * - * @see Param - * @see DSL#param(String, Object) - * @see DSLContext#extractParam(QueryPart, String) - */ - @Nullable - Param getParam(String name); - /** * Bind a new value to a named parameter. *

diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractAttachableQueryPart.java b/jOOQ/src/main/java/org/jooq/impl/AbstractAttachableQueryPart.java new file mode 100644 index 0000000000..ef89bd3223 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractAttachableQueryPart.java @@ -0,0 +1,136 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + +package org.jooq.impl; + +import static org.jooq.conf.SettingsTools.getParamType; + +import java.util.List; +import java.util.Map; + +import org.jooq.AttachableQueryPart; +import org.jooq.Configuration; +import org.jooq.Param; +import org.jooq.conf.ParamType; + +import org.jetbrains.annotations.NotNull; + +/** + * @author Lukas Eder + */ +abstract class AbstractAttachableQueryPart extends AbstractQueryPart implements AttachableQueryPart { + + private static final long serialVersionUID = -8046199737354507547L; + + private Configuration configuration; + + AbstractAttachableQueryPart(Configuration configuration) { + this.configuration = configuration; + } + + // ------------------------------------------------------------------------- + // The Attachable and Attachable internal API + // ------------------------------------------------------------------------- + + @Override + public final void attach(Configuration c) { + configuration = c; + } + + @Override + public final void detach() { + attach(null); + } + + @Override + public final Configuration configuration() { + return configuration; + } + + @NotNull + final Configuration configurationOrDefault() { + return Tools.configuration(this); + } + + @NotNull + final Configuration configurationOrThrow() { + return Tools.configurationOrThrow(this); + } + + // ------------------------------------------------------------------------- + // The AttachableQueryPart API + // ------------------------------------------------------------------------- + + @Override + public final List getBindValues() { + return create().extractBindValues(this); + } + + @Override + public final Map> getParams() { + return create().extractParams(this); + } + + @Override + public final Param getParam(String name) { + return create().extractParam(this, name); + } + + @Override + public final String getSQL() { + return getSQL(getParamType(Tools.settings(configuration()))); + } + + @Override + public final String getSQL(ParamType paramType) { + switch (paramType) { + case INDEXED: + return create().render(this); + case INLINED: + return create().renderInlined(this); + case NAMED: + return create().renderNamedParams(this); + case NAMED_OR_INLINED: + return create().renderNamedOrInlinedParams(this); + case FORCE_INDEXED: + return create().renderContext().paramType(paramType).visit(this).render(); + } + + throw new IllegalArgumentException("ParamType not supported: " + paramType); + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractQuery.java b/jOOQ/src/main/java/org/jooq/impl/AbstractQuery.java index f6d827bf4a..87e6258bd5 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractQuery.java @@ -90,12 +90,11 @@ import org.jooq.tools.JooqLogger; /** * @author Lukas Eder */ -abstract class AbstractQuery extends AbstractQueryPart implements Query { +abstract class AbstractQuery extends AbstractAttachableQueryPart implements Query { private static final long serialVersionUID = -8046199737354507547L; private static final JooqLogger log = JooqLogger.getLogger(AbstractQuery.class); - private Configuration configuration; private int timeout; private QueryPoolable poolable = QueryPoolable.DEFAULT; private boolean keepStatement; @@ -104,26 +103,7 @@ abstract class AbstractQuery extends AbstractQueryPart impleme transient Rendered rendered; AbstractQuery(Configuration configuration) { - this.configuration = configuration; - } - - // ------------------------------------------------------------------------- - // The Attachable and Attachable internal API - // ------------------------------------------------------------------------- - - @Override - public final void attach(Configuration c) { - configuration = c; - } - - @Override - public final void detach() { - attach(null); - } - - @Override - public final Configuration configuration() { - return configuration; + super(configuration); } // ------------------------------------------------------------------------- @@ -142,21 +122,6 @@ abstract class AbstractQuery extends AbstractQueryPart impleme // The Query API // ------------------------------------------------------------------------- - @Override - public final List getBindValues() { - return create().extractBindValues(this); - } - - @Override - public final Map> getParams() { - return create().extractParams(this); - } - - @Override - public final Param getParam(String name) { - return create().extractParam(this, name); - } - /** * Subclasses may override this for covariant result types *

@@ -492,7 +457,7 @@ abstract class AbstractQuery extends AbstractQueryPart impleme private final Rendered getSQL0(ExecuteContext ctx) { Rendered result; DefaultRenderContext render; - Configuration c = configuration; + Configuration c = configurationOrThrow(); // [#3542] [#4977] Some dialects do not support bind values in DDL statements // [#6474] [#6929] Can this be communicated in a leaner way? @@ -574,27 +539,4 @@ abstract class AbstractQuery extends AbstractQueryPart impleme - - @Override - public final String getSQL() { - return getSQL(getParamType(Tools.settings(configuration()))); - } - - @Override - public final String getSQL(ParamType paramType) { - switch (paramType) { - case INDEXED: - return create().render(this); - case INLINED: - return create().renderInlined(this); - case NAMED: - return create().renderNamedParams(this); - case NAMED_OR_INLINED: - return create().renderNamedOrInlinedParams(this); - case FORCE_INDEXED: - return create().renderContext().paramType(paramType).visit(this).render(); - } - - throw new IllegalArgumentException("ParamType not supported: " + paramType); - } } diff --git a/jOOQ/src/main/java/org/jooq/impl/QueriesImpl.java b/jOOQ/src/main/java/org/jooq/impl/QueriesImpl.java index a17efc8ed1..fc2b827518 100644 --- a/jOOQ/src/main/java/org/jooq/impl/QueriesImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/QueriesImpl.java @@ -56,10 +56,12 @@ import org.jooq.ResultQuery; import org.jooq.Results; import org.jooq.impl.ResultsImpl.ResultOrRowsImpl; +import org.jetbrains.annotations.NotNull; + /** * @author Lukas Eder */ -final class QueriesImpl extends AbstractQueryPart implements Queries { +final class QueriesImpl extends AbstractAttachableQueryPart implements Queries { /** * Generated UID @@ -67,10 +69,10 @@ final class QueriesImpl extends AbstractQueryPart implements Queries { private static final long serialVersionUID = 261452207127914269L; private final Collection queries; - private Configuration configuration; QueriesImpl(Configuration configuration, Collection queries) { - this.configuration = configuration; + super(configuration); + this.queries = queries; } @@ -84,7 +86,7 @@ final class QueriesImpl extends AbstractQueryPart implements Queries { List list = new ArrayList<>(queries.size() + array.length); list.addAll(queries); list.addAll(Arrays.asList(array)); - return new QueriesImpl(configuration, list); + return new QueriesImpl(configuration(), list); } @Override @@ -94,7 +96,7 @@ final class QueriesImpl extends AbstractQueryPart implements Queries { @Override public final Block block() { - return configuration.dsl().begin(queries); + return configurationOrDefault().dsl().begin(queries); } @SuppressWarnings({ "unchecked", "rawtypes" }) @@ -120,8 +122,9 @@ final class QueriesImpl extends AbstractQueryPart implements Queries { @Override public final Results fetchMany() { - ResultsImpl results = new ResultsImpl(configuration()); - DSLContext ctx = configuration().dsl(); + Configuration c = configurationOrThrow(); + ResultsImpl results = new ResultsImpl(c); + DSLContext ctx = c.dsl(); for (Query query : this) if (query instanceof ResultQuery) @@ -134,26 +137,7 @@ final class QueriesImpl extends AbstractQueryPart implements Queries { @Override public final int[] executeBatch() { - return configuration().dsl().batch(this).execute(); - } - - // ------------------------------------------------------------------------ - // Attachable API - // ------------------------------------------------------------------------ - - @Override - public final void attach(Configuration c) { - configuration = c; - } - - @Override - public final void detach() { - attach(null); - } - - @Override - public final Configuration configuration() { - return configuration; + return configurationOrThrow().dsl().batch(this).execute(); } // ------------------------------------------------------------------------ diff --git a/jOOQ/src/main/java/org/jooq/impl/Tools.java b/jOOQ/src/main/java/org/jooq/impl/Tools.java index 3e165feadb..c6aba7d656 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Tools.java +++ b/jOOQ/src/main/java/org/jooq/impl/Tools.java @@ -74,10 +74,16 @@ import static org.jooq.conf.ParamType.NAMED_OR_INLINED; import static org.jooq.conf.RenderDefaultNullability.IMPLICIT_NULL; import static org.jooq.conf.RenderQuotedNames.EXPLICIT_DEFAULT_QUOTED; import static org.jooq.conf.SettingsTools.getBackslashEscaping; -import static org.jooq.conf.SettingsTools.reflectionCaching; import static org.jooq.conf.SettingsTools.updatablePrimaryKeys; import static org.jooq.conf.ThrowExceptions.THROW_FIRST; import static org.jooq.conf.ThrowExceptions.THROW_NONE; +import static org.jooq.impl.CacheType.REFLECTION_CACHE_GET_ANNOTATED_GETTER; +import static org.jooq.impl.CacheType.REFLECTION_CACHE_GET_ANNOTATED_MEMBERS; +import static org.jooq.impl.CacheType.REFLECTION_CACHE_GET_ANNOTATED_SETTERS; +import static org.jooq.impl.CacheType.REFLECTION_CACHE_GET_MATCHING_GETTER; +import static org.jooq.impl.CacheType.REFLECTION_CACHE_GET_MATCHING_MEMBERS; +import static org.jooq.impl.CacheType.REFLECTION_CACHE_GET_MATCHING_SETTERS; +import static org.jooq.impl.CacheType.REFLECTION_CACHE_HAS_COLUMN_ANNOTATIONS; import static org.jooq.impl.DDLStatementType.ALTER_SCHEMA; import static org.jooq.impl.DDLStatementType.ALTER_TABLE; import static org.jooq.impl.DDLStatementType.ALTER_VIEW; @@ -104,13 +110,6 @@ import static org.jooq.impl.DSL.noCondition; import static org.jooq.impl.DSL.row; import static org.jooq.impl.DSL.select; import static org.jooq.impl.DSL.val; -import static org.jooq.impl.CacheType.REFLECTION_CACHE_GET_ANNOTATED_GETTER; -import static org.jooq.impl.CacheType.REFLECTION_CACHE_GET_ANNOTATED_MEMBERS; -import static org.jooq.impl.CacheType.REFLECTION_CACHE_GET_ANNOTATED_SETTERS; -import static org.jooq.impl.CacheType.REFLECTION_CACHE_GET_MATCHING_GETTER; -import static org.jooq.impl.CacheType.REFLECTION_CACHE_GET_MATCHING_MEMBERS; -import static org.jooq.impl.CacheType.REFLECTION_CACHE_GET_MATCHING_SETTERS; -import static org.jooq.impl.CacheType.REFLECTION_CACHE_HAS_COLUMN_ANNOTATIONS; import static org.jooq.impl.DefaultExecuteContext.localConnection; import static org.jooq.impl.DefaultParseContext.SUPPORTS_HASH_COMMENT_SYNTAX; import static org.jooq.impl.Identifiers.QUOTES; @@ -291,6 +290,7 @@ import org.jooq.conf.SettingsTools; import org.jooq.conf.ThrowExceptions; import org.jooq.exception.DataAccessException; import org.jooq.exception.DataTypeException; +import org.jooq.exception.DetachedException; import org.jooq.exception.MappingException; import org.jooq.exception.NoDataFoundException; import org.jooq.exception.TemplatingException; @@ -1103,10 +1103,14 @@ final class Tools { } /** - * Extract the configuration from an attachable. + * Get an attachable's configuration or a new {@link DefaultConfiguration} + * if null. */ - static final Configuration getConfiguration(Attachable attachable) { - return attachable.configuration(); + static final Configuration configurationOrThrow(Attachable attachable) { + if (attachable.configuration() == null) + throw new DetachedException("No configuration attached: " + attachable); + else + return configuration(attachable.configuration()); } /**