/*
 * 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.
 */
package org.jdbi.v3.core.statement;

import java.io.Closeable;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collector;

import javax.annotation.Nullable;

import org.jdbi.v3.core.CloseException;
import org.jdbi.v3.core.Handle;
import org.jdbi.v3.core.argument.Argument;
import org.jdbi.v3.core.argument.Arguments;
import org.jdbi.v3.core.array.SqlArrayArgumentStrategy;
import org.jdbi.v3.core.array.SqlArrayType;
import org.jdbi.v3.core.array.SqlArrayTypes;
import org.jdbi.v3.core.collector.JdbiCollectors;
import org.jdbi.v3.core.config.ConfigRegistry;
import org.jdbi.v3.core.config.JdbiConfig;
import org.jdbi.v3.core.extension.ExtensionMethod;
import org.jdbi.v3.core.generic.GenericType;
import org.jdbi.v3.core.mapper.ColumnMapper;
import org.jdbi.v3.core.mapper.ColumnMappers;
import org.jdbi.v3.core.mapper.Mappers;
import org.jdbi.v3.core.mapper.RowMapper;
import org.jdbi.v3.core.mapper.RowMappers;
import org.jdbi.v3.core.qualifier.QualifiedType;
import org.jdbi.v3.meta.Beta;

import static java.util.Objects.requireNonNull;

The statement context provides access to statement-local configuration. Context is inherited from the parent Handle initially and changes will not outlive the statement. The context will be passed through most major jdbi APIs. DISCLAIMER: The class is not intended to be extended. The final modifier is absent to allow mock tools to create a mock object of this class in the user code.
/** * The statement context provides access to statement-local configuration. * Context is inherited from the parent {@link Handle} initially and changes will * not outlive the statement. * The context will be passed through most major jdbi APIs. * * DISCLAIMER: The class is not intended to be extended. The final modifier is absent to allow * mock tools to create a mock object of this class in the user code. */
public class StatementContext implements Closeable { private final ConfigRegistry config; private final ExtensionMethod extensionMethod; private final Set<Cleanable> cleanables = new LinkedHashSet<>(); private String rawSql; private String renderedSql; private ParsedSql parsedSql; private PreparedStatement statement; private Connection connection; private Binding binding = new Binding(); private boolean returningGeneratedKeys = false; private String[] generatedKeysColumnNames = new String[0]; private boolean concurrentUpdatable = false; private Instant executionMoment; private Instant completionMoment; private Instant exceptionMoment; StatementContext() { this(new ConfigRegistry()); } StatementContext(ConfigRegistry config) { this(config, null); } StatementContext(ConfigRegistry config, ExtensionMethod extensionMethod) { this.config = requireNonNull(config); this.extensionMethod = extensionMethod; }
Gets the configuration object of the given type, associated with this context.
Params:
  • configClass – the configuration type
Type parameters:
  • <C> – the configuration type
Returns:the configuration object of the given type, associated with this context.
/** * Gets the configuration object of the given type, associated with this context. * * @param configClass the configuration type * @param <C> the configuration type * @return the configuration object of the given type, associated with this context. */
public <C extends JdbiConfig<C>> C getConfig(Class<C> configClass) { return config.get(configClass); }
Returns:the ConfigRegistry this context owns
/** * @return the {@code ConfigRegistry} this context owns */
public ConfigRegistry getConfig() { return config; }
Returns the attributes applied in this context.
Returns:the defined attributes.
/** * Returns the attributes applied in this context. * * @return the defined attributes. */
public Map<String, Object> getAttributes() { return getConfig(SqlStatements.class).getAttributes(); }
Obtain the value of an attribute
Params:
  • key – the name of the attribute
Returns:the value of the attribute
/** * Obtain the value of an attribute * * @param key the name of the attribute * @return the value of the attribute */
public Object getAttribute(String key) { return getConfig(SqlStatements.class).getAttribute(key); }
Define an attribute for in this context.
Params:
  • key – the key for the attribute
  • value – the value for the attribute
/** * Define an attribute for in this context. * * @param key the key for the attribute * @param value the value for the attribute */
public void define(String key, Object value) { getConfig(SqlStatements.class).define(key, value); }
Obtain an argument for given value in this context
Params:
  • type – the type of the argument.
  • value – the argument value.
Returns:an Argument for the given value.
/** * Obtain an argument for given value in this context * * @param type the type of the argument. * @param value the argument value. * @return an Argument for the given value. */
public Optional<Argument> findArgumentFor(Type type, Object value) { return getConfig(Arguments.class).findFor(type, value); }
Obtain an argument for given value in this context
Params:
  • type – the type of the argument.
  • value – the argument value.
Returns:an Argument for the given value.
/** * Obtain an argument for given value in this context * * @param type the type of the argument. * @param value the argument value. * @return an Argument for the given value. */
@Beta public Optional<Argument> findArgumentFor(QualifiedType<?> type, Object value) { return getConfig(Arguments.class).findFor(type, value); }
Returns:the strategy used to bind array-type arguments to SQL statements
/** * @return the strategy used to bind array-type arguments to SQL statements */
public SqlArrayArgumentStrategy getSqlArrayArgumentStrategy() { return getConfig(SqlArrayTypes.class).getArgumentStrategy(); }
Obtain an SqlArrayType for the given array element type in this context
Params:
  • elementType – the array element type.
Returns:an SqlArrayType for the given element type.
/** * Obtain an {@link SqlArrayType} for the given array element type in this context * * @param elementType the array element type. * @return an {@link SqlArrayType} for the given element type. */
public Optional<SqlArrayType<?>> findSqlArrayTypeFor(Type elementType) { return getConfig(SqlArrayTypes.class).findFor(elementType); }
Obtain a mapper for the given type in this context.
Params:
  • type – the target type to map to
Type parameters:
  • <T> – the type to map
Returns:a mapper for the given type, or empty if no row or column mappers is registered for the given type.
/** * Obtain a mapper for the given type in this context. * * @param <T> the type to map * @param type the target type to map to * @return a mapper for the given type, or empty if no row or column mappers * is registered for the given type. */
public <T> Optional<RowMapper<T>> findMapperFor(Class<T> type) { return getConfig(Mappers.class).findFor(type); }
Obtain a mapper for the given type in this context.
Params:
  • type – the target type to map to
Type parameters:
  • <T> – the type to map
Returns:a mapper for the given type, or empty if no row or column mappers is registered for the given type.
/** * Obtain a mapper for the given type in this context. * * @param <T> the type to map * @param type the target type to map to * @return a mapper for the given type, or empty if no row or column mappers * is registered for the given type. */
public <T> Optional<RowMapper<T>> findMapperFor(GenericType<T> type) { return getConfig(Mappers.class).findFor(type); }
Obtain a mapper for the given type in this context.
Params:
  • type – the target type to map to
Returns:a mapper for the given type, or empty if no row or column mappers is registered for the given type.
/** * Obtain a mapper for the given type in this context. * * @param type the target type to map to * @return a mapper for the given type, or empty if no row or column mappers * is registered for the given type. */
public Optional<RowMapper<?>> findMapperFor(Type type) { return getConfig(Mappers.class).findFor(type); }
Obtain a mapper for the given qualified type in this context.
Params:
  • type – the target qualified type to map to
Returns:a mapper for the given qualified type, or empty if no row or column mappers is registered for the given type.
/** * Obtain a mapper for the given qualified type in this context. * * @param type the target qualified type to map to * @return a mapper for the given qualified type, or empty if no row or column mappers * is registered for the given type. */
@Beta public <T> Optional<RowMapper<T>> findMapperFor(QualifiedType<T> type) { return getConfig(Mappers.class).findFor(type); }
Obtain a column mapper for the given type in this context.
Params:
  • type – the target type to map to
Type parameters:
  • <T> – the type to map
Returns:a ColumnMapper for the given type, or empty if no column mapper is registered for the given type.
/** * Obtain a column mapper for the given type in this context. * * @param <T> the type to map * @param type the target type to map to * @return a ColumnMapper for the given type, or empty if no column mapper is registered for the given type. */
public <T> Optional<ColumnMapper<T>> findColumnMapperFor(Class<T> type) { return getConfig(ColumnMappers.class).findFor(type); }
Obtain a column mapper for the given type in this context.
Params:
  • type – the target type to map to
Type parameters:
  • <T> – the type to map
Returns:a ColumnMapper for the given type, or empty if no column mapper is registered for the given type.
/** * Obtain a column mapper for the given type in this context. * * @param <T> the type to map * @param type the target type to map to * @return a ColumnMapper for the given type, or empty if no column mapper is registered for the given type. */
public <T> Optional<ColumnMapper<T>> findColumnMapperFor(GenericType<T> type) { return getConfig(ColumnMappers.class).findFor(type); }
Obtain a column mapper for the given type in this context.
Params:
  • type – the target type to map to
Returns:a ColumnMapper for the given type, or empty if no column mapper is registered for the given type.
/** * Obtain a column mapper for the given type in this context. * * @param type the target type to map to * @return a ColumnMapper for the given type, or empty if no column mapper is registered for the given type. */
public Optional<ColumnMapper<?>> findColumnMapperFor(Type type) { return getConfig(ColumnMappers.class).findFor(type); }
Obtain a column mapper for the given qualified type in this context.
Params:
  • type – the qualified target type to map to
Returns:a ColumnMapper for the given type, or empty if no column mapper is registered for the given type.
/** * Obtain a column mapper for the given qualified type in this context. * * @param type the qualified target type to map to * @return a ColumnMapper for the given type, or empty if no column mapper is registered for the given type. */
@Beta public <T> Optional<ColumnMapper<T>> findColumnMapperFor(QualifiedType<T> type) { return getConfig(ColumnMappers.class).findFor(type); }
Obtain a row mapper for the given type in this context.
Params:
  • type – the target type to map to
Returns:a RowMapper for the given type, or empty if no row mapper is registered for the given type.
/** * Obtain a row mapper for the given type in this context. * * @param type the target type to map to * @return a RowMapper for the given type, or empty if no row mapper is registered for the given type. */
public Optional<RowMapper<?>> findRowMapperFor(Type type) { return getConfig(RowMappers.class).findFor(type); }
Obtain a row mapper for the given type in this context.
Params:
  • type – the target type to map to
Type parameters:
  • <T> – the type to map
Returns:a RowMapper for the given type, or empty if no row mapper is registered for the given type.
/** * Obtain a row mapper for the given type in this context. * * @param <T> the type to map * @param type the target type to map to * @return a RowMapper for the given type, or empty if no row mapper is registered for the given type. */
public <T> Optional<RowMapper<T>> findRowMapperFor(Class<T> type) { return getConfig(RowMappers.class).findFor(type); }
Obtain a row mapper for the given type in this context.
Params:
  • type – the target type to map to
Type parameters:
  • <T> – the type to map
Returns:a RowMapper for the given type, or empty if no row mapper is registered for the given type.
/** * Obtain a row mapper for the given type in this context. * * @param <T> the type to map * @param type the target type to map to * @return a RowMapper for the given type, or empty if no row mapper is registered for the given type. */
public <T> Optional<RowMapper<T>> findRowMapperFor(GenericType<T> type) { return getConfig(RowMappers.class).findFor(type); }
Obtain a collector for the given type.
Params:
  • containerType – the container type.
Returns:a Collector for the given container type, or empty null if no collector is registered for the given type.
/** * Obtain a collector for the given type. * * @param containerType the container type. * @return a Collector for the given container type, or empty null if no collector is registered for the given type. */
public Optional<Collector<?, ?, ?>> findCollectorFor(Type containerType) { return getConfig(JdbiCollectors.class).findFor(containerType); }
Returns the element type for the given container type.
Params:
  • containerType – the container type.
Returns:the element type for the given container type, if available.
/** * Returns the element type for the given container type. * * @param containerType the container type. * @return the element type for the given container type, if available. */
public Optional<Type> findElementTypeFor(Type containerType) { return getConfig(JdbiCollectors.class).findElementTypeFor(containerType); } StatementContext setRawSql(String rawSql) { this.rawSql = rawSql; return this; }
Obtain the initial sql for the statement used to create the statement
Returns:the initial sql
/** * Obtain the initial sql for the statement used to create the statement * * @return the initial sql */
public String getRawSql() { return rawSql; } void setRenderedSql(String renderedSql) { this.renderedSql = renderedSql; }
Obtain the rendered SQL statement

Not available until until statement execution time

Returns:the sql statement after processing template directives.
/** * Obtain the rendered SQL statement * <p> * Not available until until statement execution time * </p> * * @return the sql statement after processing template directives. */
public String getRenderedSql() { return renderedSql; } void setParsedSql(ParsedSql parsedSql) { this.parsedSql = parsedSql; }
Obtain the parsed SQL statement

Not available until until statement execution time

Returns:the sql statement as it will be executed against the database
/** * Obtain the parsed SQL statement * <p> * Not available until until statement execution time * </p> * * @return the sql statement as it will be executed against the database */
public ParsedSql getParsedSql() { return parsedSql; } void setStatement(PreparedStatement stmt) { statement = stmt; }
Obtain the actual prepared statement being used.

Not available until execution time

Returns:Obtain the actual prepared statement being used.
/** * Obtain the actual prepared statement being used. * <p> * Not available until execution time * </p> * * @return Obtain the actual prepared statement being used. */
public PreparedStatement getStatement() { return statement; } StatementContext setConnection(Connection connection) { this.connection = connection; return this; }
Obtain the JDBC connection being used for this statement
Returns:the JDBC connection
/** * Obtain the JDBC connection being used for this statement * * @return the JDBC connection */
public Connection getConnection() { return connection; } StatementContext setBinding(Binding b) { this.binding = b; return this; }
Returns:the statement binding
/** * @return the statement binding */
public Binding getBinding() { return binding; }
Sets whether the current statement returns generated keys.
Params:
  • b – return generated keys?
/** * Sets whether the current statement returns generated keys. * @param b return generated keys? */
public void setReturningGeneratedKeys(boolean b) { if (isConcurrentUpdatable() && b) { throw new IllegalArgumentException("Cannot create a result set that is concurrent " + "updatable and is returning generated keys."); } this.returningGeneratedKeys = b; }
Returns:whether the statement being generated is expected to return generated keys.
/** * @return whether the statement being generated is expected to return generated keys. */
public boolean isReturningGeneratedKeys() { return returningGeneratedKeys || generatedKeysColumnNames.length > 0; }
Returns:the generated key column names, if any
/** * @return the generated key column names, if any */
public String[] getGeneratedKeysColumnNames() { return Arrays.copyOf(generatedKeysColumnNames, generatedKeysColumnNames.length); }
Set the generated key column names.
Params:
  • generatedKeysColumnNames – the generated key column names
/** * Set the generated key column names. * @param generatedKeysColumnNames the generated key column names */
public void setGeneratedKeysColumnNames(String[] generatedKeysColumnNames) { this.generatedKeysColumnNames = Arrays.copyOf(generatedKeysColumnNames, generatedKeysColumnNames.length); }
Return if the statement should be concurrent updatable. If this returns true, the concurrency level of the created ResultSet will be ResultSet.CONCUR_UPDATABLE, otherwise the result set is not updatable, and will have concurrency level ResultSet.CONCUR_READ_ONLY.
Returns:if the statement generated should be concurrent updatable.
/** * Return if the statement should be concurrent updatable. * * If this returns true, the concurrency level of the created ResultSet will be * {@link java.sql.ResultSet#CONCUR_UPDATABLE}, otherwise the result set is not updatable, * and will have concurrency level {@link java.sql.ResultSet#CONCUR_READ_ONLY}. * * @return if the statement generated should be concurrent updatable. */
public boolean isConcurrentUpdatable() { return concurrentUpdatable; }
Set the context to create a concurrent updatable result set. This cannot be combined with isReturningGeneratedKeys(), only one option may be selected. It does not make sense to combine these either, as one applies to queries, and the other applies to updates.
Params:
  • concurrentUpdatable – if the result set should be concurrent updatable.
/** * Set the context to create a concurrent updatable result set. * * This cannot be combined with {@link #isReturningGeneratedKeys()}, only * one option may be selected. It does not make sense to combine these either, as one * applies to queries, and the other applies to updates. * * @param concurrentUpdatable if the result set should be concurrent updatable. */
public void setConcurrentUpdatable(final boolean concurrentUpdatable) { if (concurrentUpdatable && isReturningGeneratedKeys()) { throw new IllegalArgumentException("Cannot create a result set that is concurrent " + "updatable and is returning generated keys."); } this.concurrentUpdatable = concurrentUpdatable; }
Returns:the Instant at which query execution began
/** * @return the {@link Instant} at which query execution began */
@Nullable public Instant getExecutionMoment() { return executionMoment; }
for jdbi-internal use only
/** * for jdbi-internal use only */
public void setExecutionMoment(Instant executionMoment) { this.executionMoment = executionMoment; }
Returns:the Instant at which query execution ended, if it did so successfully
/** * @return the {@link Instant} at which query execution ended, if it did so successfully */
@Nullable public Instant getCompletionMoment() { return completionMoment; }
for jdbi-internal use only
/** * for jdbi-internal use only */
public void setCompletionMoment(Instant completionMoment) { this.completionMoment = completionMoment; }
Returns:the Instant at which query execution ended, if it did so with an exception
/** * @return the {@link Instant} at which query execution ended, if it did so with an exception */
@Nullable public Instant getExceptionMoment() { return exceptionMoment; }
for jdbi-internal use only
/** * for jdbi-internal use only */
public void setExceptionMoment(Instant exceptionMoment) { this.exceptionMoment = exceptionMoment; }
Convenience method to measure elapsed time between start of query execution and completion or exception as appropriate. Do not call with a null argument or before a query has executed/exploded.
Params:
  • unit – the time unit to convert to
Returns:the elapsed time in the given unit
/** * Convenience method to measure elapsed time between start of query execution and completion or exception as appropriate. Do not call with a null argument or before a query has executed/exploded. * @param unit the time unit to convert to * @return the elapsed time in the given unit */
public long getElapsedTime(ChronoUnit unit) { return unit.between(executionMoment, completionMoment == null ? exceptionMoment : completionMoment); }
Registers a Cleanable to be invoked when the statement context is closed. Cleanables can be registered on a statement context, which will be cleaned up when the statement finishes or (in the case of a ResultIterator), the object representing the results is closed.

Resources cleaned up by Jdbi include ResultSet, Statement, Handle, Array, and StatementBuilder.

Params:
  • cleanable – the Cleanable to clean on close
/** * Registers a {@code Cleanable} to be invoked when the statement context is closed. Cleanables can be registered * on a statement context, which will be cleaned up when * the statement finishes or (in the case of a ResultIterator), the object representing the results is closed. * <p> * Resources cleaned up by Jdbi include {@link ResultSet}, {@link Statement}, {@link Handle}, * {@link java.sql.Array}, and {@link StatementBuilder}. * * @param cleanable the Cleanable to clean on close */
public void addCleanable(Cleanable cleanable) { cleanables.add(cleanable); } @Override @SuppressWarnings("PMD.DoNotThrowExceptionInFinally") public void close() { SQLException exception = null; try { List<Cleanable> cleanables = new ArrayList<>(this.cleanables); this.cleanables.clear(); Collections.reverse(cleanables); for (Cleanable cleanable : cleanables) { try { cleanable.close(); } catch (SQLException e) { if (exception == null) { exception = e; } else { exception.addSuppressed(e); } } } } finally { if (exception != null) { throw new CloseException("Exception thrown while cleaning StatementContext", exception); } } } public ExtensionMethod getExtensionMethod() { return extensionMethod; } }