[jOOQ/jOOQ#11755] Extract AttachableQueryPart from Query

This commit is contained in:
Lukas Eder 2021-04-06 09:14:21 +02:00
parent 6fa0c35287
commit 203f1b95db
7 changed files with 336 additions and 212 deletions

View File

@ -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}.
* <p>
* 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.
* <p>
* Use this method, when you want to use jOOQ for object oriented query
* creation, but execute the query with some other technology, such as
* <ul>
* <li>JDBC</li>
* <li>Spring Templates</li>
* <li>JPA native queries</li>
* <li>etc...</li>
* </ul>
* <p>
* Note, this is the same as calling {@link #getSQL(ParamType)}. The
* parameter will depend on your {@link DSLContext}'s {@link Settings}:
* <table border="1">
* <tr>
* <th><code>StatementType</code></th>
* <th>boolean parameter</th>
* <th>effect</th>
* </tr>
* <tr>
* <td>{@link StatementType#PREPARED_STATEMENT}</td>
* <td><code>false</code> (default)</td>
* <td>This will render bind variables to be used with a JDBC
* {@link PreparedStatement}. You can extract bind values from this
* <code>Query</code> using {@link #getBindValues()}</td>
* </tr>
* <tr>
* <td>{@link StatementType#STATIC_STATEMENT}</td>
* <td><code>true</code></td>
* <td>This will inline all bind variables in a statement to be used with a
* JDBC {@link Statement}</td>
* </tr>
* </table>
* <p>
* [#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.
* <p>
* [#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 <code>paramType</code>
* argument to decide whether to render bind values.
* <p>
* 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.
* <p>
* 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 <code>"?"</code>
*
* @see DSLContext#extractBindValues(QueryPart)
*/
@NotNull
List<Object> getBindValues();
/**
* Get a <code>Map</code> of named parameters. The <code>Map</code> itself
* cannot be modified, but the {@link Param} elements allow for modifying
* bind values on an existing {@link Query}.
* <p>
* 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<String, Param<?>> getParams();
/**
* Get a named parameter from the {@link Query}, provided its name.
* <p>
* 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);
}

View File

@ -52,7 +52,7 @@ import org.jetbrains.annotations.NotNull;
*
* @author Lukas Eder
*/
public interface Queries extends QueryPart, Attachable, Iterable<Query> {
public interface Queries extends AttachableQueryPart, Iterable<Query> {
// ------------------------------------------------------------------------
// Access API

View File

@ -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.
* <p>
* Use this method, when you want to use jOOQ for object oriented query
* creation, but execute the query with some other technology, such as
* <ul>
* <li>JDBC</li>
* <li>Spring Templates</li>
* <li>JPA native queries</li>
* <li>etc...</li>
* </ul>
* <p>
* Note, this is the same as calling {@link #getSQL(boolean)}. The boolean
* parameter will depend on your {@link DSLContext}'s {@link Settings}:
* <table border="1">
* <tr>
* <th><code>StatementType</code></th>
* <th>boolean parameter</th>
* <th>effect</th>
* </tr>
* <tr>
* <td> {@link StatementType#PREPARED_STATEMENT}</td>
* <td><code>false</code> (default)</td>
* <td>This will render bind variables to be used with a JDBC
* {@link PreparedStatement}. You can extract bind values from this
* <code>Query</code> using {@link #getBindValues()}</td>
* </tr>
* <tr>
* <td> {@link StatementType#STATIC_STATEMENT}</td>
* <td><code>true</code></td>
* <td>This will inline all bind variables in a statement to be used with a
* JDBC {@link Statement}</td>
* </tr>
* </table>
* <p>
* [#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.
* <p>
* [#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 <code>paramType</code>
* argument to decide whether to render bind values.
* <p>
* 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
* <code>List</code> cannot be modified. To modify bind values, use
* {@link #getParams()} instead.
* <p>
* 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 <code>"?"</code>
*
* @see DSLContext#extractBindValues(QueryPart)
*/
@NotNull
List<Object> getBindValues();
/**
* Get a <code>Map</code> of named parameters. The <code>Map</code> itself
* cannot be modified, but the {@link Param} elements allow for modifying
* bind values on an existing {@link Query}.
* <p>
* 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<String, Param<?>> getParams();
/**
* Get a named parameter from the {@link Query}, provided its name.
* <p>
* 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.
* <p>

View File

@ -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<Object> getBindValues() {
return create().extractBindValues(this);
}
@Override
public final Map<String, Param<?>> 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);
}
}

View File

@ -90,12 +90,11 @@ import org.jooq.tools.JooqLogger;
/**
* @author Lukas Eder
*/
abstract class AbstractQuery<R extends Record> extends AbstractQueryPart implements Query {
abstract class AbstractQuery<R extends Record> 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<R extends Record> 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<R extends Record> extends AbstractQueryPart impleme
// The Query API
// -------------------------------------------------------------------------
@Override
public final List<Object> getBindValues() {
return create().extractBindValues(this);
}
@Override
public final Map<String, Param<?>> 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
* <p>
@ -492,7 +457,7 @@ abstract class AbstractQuery<R extends Record> 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<R extends Record> 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);
}
}

View File

@ -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<? extends Query> queries;
private Configuration configuration;
QueriesImpl(Configuration configuration, Collection<? extends Query> queries) {
this.configuration = configuration;
super(configuration);
this.queries = queries;
}
@ -84,7 +86,7 @@ final class QueriesImpl extends AbstractQueryPart implements Queries {
List<Query> 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();
}
// ------------------------------------------------------------------------

View File

@ -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 <code>null</code>.
*/
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());
}
/**