R2DBC javadoc and code style revision

See gh-25065
This commit is contained in:
Juergen Hoeller 2020-10-26 17:47:20 +01:00
parent 16c8676e5b
commit 41247d49ba
32 changed files with 219 additions and 375 deletions

View File

@ -29,8 +29,6 @@ import io.r2dbc.spi.R2dbcTimeoutException;
import io.r2dbc.spi.R2dbcTransientException;
import io.r2dbc.spi.R2dbcTransientResourceException;
import io.r2dbc.spi.Wrapped;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import reactor.core.publisher.Mono;
import org.springframework.core.Ordered;
@ -69,11 +67,6 @@ public abstract class ConnectionFactoryUtils {
*/
public static final int CONNECTION_SYNCHRONIZATION_ORDER = 1000;
private static final Log logger = LogFactory.getLog(ConnectionFactoryUtils.class);
private ConnectionFactoryUtils() {}
/**
* Obtain a {@link Connection} from the given {@link ConnectionFactory}.
@ -112,48 +105,34 @@ public abstract class ConnectionFactoryUtils {
if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
conHolder.requested();
if (!conHolder.hasConnection()) {
if (logger.isDebugEnabled()) {
logger.debug("Fetching resumed R2DBC Connection from ConnectionFactory");
}
return fetchConnection(connectionFactory).doOnNext(conHolder::setConnection);
}
return Mono.just(conHolder.getConnection());
}
// Else we either got no holder or an empty thread-bound holder here.
if (logger.isDebugEnabled()) {
logger.debug("Fetching R2DBC Connection from ConnectionFactory");
}
Mono<Connection> con = fetchConnection(connectionFactory);
if (synchronizationManager.isSynchronizationActive()) {
return con.flatMap(connection -> {
return Mono.just(connection).doOnNext(conn -> {
// Use same Connection for further R2DBC actions within the transaction.
// Thread-bound object will get removed by synchronization at transaction completion.
ConnectionHolder holderToUse = conHolder;
if (holderToUse == null) {
holderToUse = new ConnectionHolder(conn);
}
else {
holderToUse.setConnection(conn);
}
holderToUse.requested();
synchronizationManager
.registerSynchronization(new ConnectionSynchronization(holderToUse, connectionFactory));
holderToUse.setSynchronizedWithTransaction(true);
if (holderToUse != conHolder) {
synchronizationManager.bindResource(connectionFactory, holderToUse);
}
}) // Unexpected exception from external delegation call -> close Connection and rethrow.
.onErrorResume(e -> releaseConnection(connection, connectionFactory).then(Mono.error(e)));
});
return con.flatMap(connection -> Mono.just(connection).doOnNext(conn -> {
// Use same Connection for further R2DBC actions within the transaction.
// Thread-bound object will get removed by synchronization at transaction completion.
ConnectionHolder holderToUse = conHolder;
if (holderToUse == null) {
holderToUse = new ConnectionHolder(conn);
}
else {
holderToUse.setConnection(conn);
}
holderToUse.requested();
synchronizationManager
.registerSynchronization(new ConnectionSynchronization(holderToUse, connectionFactory));
holderToUse.setSynchronizedWithTransaction(true);
if (holderToUse != conHolder) {
synchronizationManager.bindResource(connectionFactory, holderToUse);
}
}) // Unexpected exception from external delegation call -> close Connection and rethrow.
.onErrorResume(e -> releaseConnection(connection, connectionFactory).then(Mono.error(e))));
}
return con;
}).onErrorResume(NoTransactionException.class, e -> Mono.from(connectionFactory.create()));
}
@ -356,9 +335,7 @@ public abstract class ConnectionFactoryUtils {
@Override
public Mono<Void> suspend() {
if (this.holderActive) {
return TransactionSynchronizationManager.forCurrentTransaction()
.flatMap(synchronizationManager -> {
return TransactionSynchronizationManager.forCurrentTransaction().flatMap(synchronizationManager -> {
synchronizationManager.unbindResource(this.connectionFactory);
if (this.connectionHolder.hasConnection() && !this.connectionHolder.isOpen()) {
// Release Connection on suspend if the application doesn't keep
@ -371,7 +348,6 @@ public abstract class ConnectionFactoryUtils {
return Mono.empty();
});
}
return Mono.empty();
}
@ -388,11 +364,10 @@ public abstract class ConnectionFactoryUtils {
@Override
public Mono<Void> beforeCompletion() {
// Release Connection early if the holder is not open anymore
// (that is, not used by another resource
// that has its own cleanup via transaction synchronization),
// to avoid issues with strict transaction implementations that expect
// the close call before transaction completion.
// Release Connection early if the holder is not open anymore (that is,
// not used by another resource that has its own cleanup via transaction
// synchronization), to avoid issues with strict transaction implementations
// that expect the close call before transaction completion.
if (!this.connectionHolder.isOpen()) {
return TransactionSynchronizationManager.forCurrentTransaction().flatMap(synchronizationManager -> {
synchronizationManager.unbindResource(this.connectionFactory);
@ -414,8 +389,7 @@ public abstract class ConnectionFactoryUtils {
if (this.holderActive) {
// The bound ConnectionHolder might not be available anymore,
// since afterCompletion might get called from a different thread.
return TransactionSynchronizationManager.forCurrentTransaction()
.flatMap(synchronizationManager -> {
return TransactionSynchronizationManager.forCurrentTransaction().flatMap(synchronizationManager -> {
synchronizationManager.unbindResourceIfPossible(this.connectionFactory);
this.holderActive = false;
if (this.connectionHolder.hasConnection()) {
@ -426,7 +400,6 @@ public abstract class ConnectionFactoryUtils {
return Mono.empty();
});
}
this.connectionHolder.reset();
return Mono.empty();
}

View File

@ -107,8 +107,6 @@ public abstract class ScriptUtils {
private static final Log logger = LogFactory.getLog(ScriptUtils.class);
// utility constructor
private ScriptUtils() {}
/**
* Split an SQL script into separate statements delimited by the provided
@ -335,14 +333,11 @@ public abstract class ScriptUtils {
return DataBufferUtils.join(DataBufferUtils.read(resource.getResource(), dataBufferFactory, 8192))
.handle((it, sink) -> {
try (InputStream is = it.asInputStream()) {
InputStreamReader in = resource.getCharset() != null ? new InputStreamReader(is, resource.getCharset())
: new InputStreamReader(is);
InputStreamReader in = (resource.getCharset() != null ?
new InputStreamReader(is, resource.getCharset()) : new InputStreamReader(is));
LineNumberReader lnr = new LineNumberReader(in);
String script = readScript(lnr, commentPrefixes, separator, blockCommentEndDelimiter);
sink.next(script);
sink.complete();
}
@ -548,9 +543,9 @@ public abstract class ScriptUtils {
* @see org.springframework.r2dbc.connection.ConnectionFactoryUtils#releaseConnection
*/
public static Mono<Void> executeSqlScript(Connection connection, EncodedResource resource,
DataBufferFactory dataBufferFactory, boolean continueOnError, boolean ignoreFailedDrops, String commentPrefix,
@Nullable String separator, String blockCommentStartDelimiter, String blockCommentEndDelimiter)
throws ScriptException {
DataBufferFactory dataBufferFactory, boolean continueOnError, boolean ignoreFailedDrops,
String commentPrefix, @Nullable String separator, String blockCommentStartDelimiter,
String blockCommentEndDelimiter) throws ScriptException {
return executeSqlScript(connection, resource, dataBufferFactory, continueOnError,
ignoreFailedDrops, new String[] { commentPrefix }, separator,
@ -587,10 +582,10 @@ public abstract class ScriptUtils {
* @see org.springframework.r2dbc.connection.ConnectionFactoryUtils#getConnection
* @see org.springframework.r2dbc.connection.ConnectionFactoryUtils#releaseConnection
*/
public static Mono<Void> executeSqlScript(Connection connection, EncodedResource resource, DataBufferFactory dataBufferFactory,
boolean continueOnError,
boolean ignoreFailedDrops, String[] commentPrefixes, @Nullable String separator,
String blockCommentStartDelimiter, String blockCommentEndDelimiter) throws ScriptException {
public static Mono<Void> executeSqlScript(Connection connection, EncodedResource resource,
DataBufferFactory dataBufferFactory, boolean continueOnError, boolean ignoreFailedDrops,
String[] commentPrefixes, @Nullable String separator, String blockCommentStartDelimiter,
String blockCommentEndDelimiter) throws ScriptException {
if (logger.isDebugEnabled()) {
logger.debug("Executing SQL script from " + resource);
@ -622,17 +617,15 @@ public abstract class ScriptUtils {
});
if (logger.isDebugEnabled()) {
executeScript = executeScript.doOnComplete(() -> {
long elapsedTime = System.currentTimeMillis() - startTime;
logger.debug("Executed SQL script from " + resource + " in " + elapsedTime + " ms.");
});
}
return executeScript.onErrorMap(ex -> !(ex instanceof ScriptException),
ex -> new UncategorizedScriptException("Failed to execute database script from resource [" + resource + "]",
ex))
ex -> new UncategorizedScriptException(
"Failed to execute database script from resource [" + resource + "]", ex))
.then();
}
@ -644,22 +637,21 @@ public abstract class ScriptUtils {
.collect(Collectors.summingLong(count -> count));
if (logger.isDebugEnabled()) {
execution = execution.doOnNext(rowsAffected -> logger.debug(rowsAffected + " returned as update count for SQL: " + statement));
execution = execution.doOnNext(rowsAffected ->
logger.debug(rowsAffected + " returned as update count for SQL: " + statement));
}
return execution.onErrorResume(ex -> {
boolean dropStatement = StringUtils.startsWithIgnoreCase(statement.trim(), "drop");
if (continueOnError || (dropStatement && ignoreFailedDrops)) {
if (logger.isDebugEnabled()) {
logger.debug(ScriptStatementFailedException.buildErrorMessage(statement, statementNumber.get(), resource),
ex);
logger.debug(ScriptStatementFailedException.buildErrorMessage(
statement, statementNumber.get(), resource), ex);
}
}
else {
return Mono.error(new ScriptStatementFailedException(statement, statementNumber.get(), resource, ex));
}
return Mono.empty();
}).then();
}

View File

@ -167,6 +167,7 @@ public abstract class AbstractRoutingConnectionFactory implements ConnectionFact
*/
protected ConnectionFactory resolveSpecifiedConnectionFactory(Object connectionFactory)
throws IllegalArgumentException {
if (connectionFactory instanceof ConnectionFactory) {
return (ConnectionFactory) connectionFactory;
}
@ -174,17 +175,14 @@ public abstract class AbstractRoutingConnectionFactory implements ConnectionFact
return this.connectionFactoryLookup.getConnectionFactory((String) connectionFactory);
}
else {
throw new IllegalArgumentException(
"Illegal connection factory value - only 'io.r2dbc.spi.ConnectionFactory' and 'String' supported: "
+ connectionFactory);
throw new IllegalArgumentException("Illegal connection factory value - " +
"only 'io.r2dbc.spi.ConnectionFactory' and 'String' supported: " + connectionFactory);
}
}
@Override
public Mono<Connection> create() {
return determineTargetConnectionFactory() //
.map(ConnectionFactory::create) //
.flatMap(Mono::from);
return determineTargetConnectionFactory().map(ConnectionFactory::create).flatMap(Mono::from);
}
@Override

View File

@ -56,7 +56,6 @@ public class MapConnectionFactoryLookup implements ConnectionFactoryLookup {
/**
* Create a new instance of the {@link MapConnectionFactoryLookup} class.
*
* @param connectionFactoryName the name under which the supplied {@link ConnectionFactory} is to be added
* @param connectionFactory the {@link ConnectionFactory} to be added
*/

View File

@ -46,7 +46,7 @@ interface BindParameterSource {
/**
* Return the parameter value for the requested named parameter.
* @param paramName the name of the parameter
* @return the value of the specified parameter, can be {@code null}
* @return the value of the specified parameter (can be {@code null})
* @throws IllegalArgumentException if there is no value
* for the requested parameter
*/
@ -64,8 +64,8 @@ interface BindParameterSource {
}
/**
* Return parameter names of the underlying parameter source.
* @return parameter names of the underlying parameter source.
* Return the parameter names of the underlying parameter source.
* @return an iterator over the parameter names
*/
Iterable<String> getParameterNames();

View File

@ -48,7 +48,7 @@ import org.springframework.util.LinkedCaseInsensitiveMap;
*/
public class ColumnMapRowMapper implements BiFunction<Row, RowMetadata, Map<String, Object>> {
/** Default instance. */
/** A default {@code ColumnMapRowMapper} instance. */
public final static ColumnMapRowMapper INSTANCE = new ColumnMapRowMapper();

View File

@ -66,7 +66,7 @@ public interface DatabaseClient extends ConnectionAccessor {
* SQL call along with options leading to the execution. The SQL string can
* contain either native parameter bind markers or named parameters (e.g.
* {@literal :foo, :bar}) when {@link NamedParameterExpander} is enabled.
* @param sql must not be {@code null} or empty
* @param sql the SQL statement
* @return a new {@link GenericExecuteSpec}
* @see NamedParameterExpander
* @see DatabaseClient.Builder#namedParameters(boolean)
@ -80,7 +80,7 @@ public interface DatabaseClient extends ConnectionAccessor {
* bind markers or named parameters (e.g. {@literal :foo, :bar}) when
* {@link NamedParameterExpander} is enabled.
* <p>Accepts {@link PreparedOperation} as SQL and binding {@link Supplier}
* @param sqlSupplier must not be {@code null}
* @param sqlSupplier a supplier for the SQL statement
* @return a new {@link GenericExecuteSpec}
* @see NamedParameterExpander
* @see DatabaseClient.Builder#namedParameters(boolean)
@ -115,34 +115,31 @@ public interface DatabaseClient extends ConnectionAccessor {
/**
* Configure the {@link BindMarkersFactory BindMarkers} to be used.
* @param bindMarkers must not be {@code null}
*/
Builder bindMarkers(BindMarkersFactory bindMarkers);
/**
* Configure the {@link ConnectionFactory R2DBC connector}.
* @param factory must not be {@code null}
*/
Builder connectionFactory(ConnectionFactory factory);
/**
* Configure a {@link ExecuteFunction} to execute {@link Statement} objects.
* @param executeFunction must not be {@code null}
* @see Statement#execute()
*/
Builder executeFunction(ExecuteFunction executeFunction);
/**
* Configure whether to use named parameter expansion. Defaults to {@code true}.
* @param enabled {@code true} to use named parameter expansion.
* {@code false} to disable named parameter expansion.
* Configure whether to use named parameter expansion.
* Defaults to {@code true}.
* @param enabled {@code true} to use named parameter expansion;
* {@code false} to disable named parameter expansion
* @see NamedParameterExpander
*/
Builder namedParameters(boolean enabled);
/**
* Configures a {@link Consumer} to configure this builder.
* @param builderConsumer must not be {@code null}.
*/
Builder apply(Consumer<Builder> builderConsumer);
@ -162,28 +159,28 @@ public interface DatabaseClient extends ConnectionAccessor {
* Bind a non-{@code null} value to a parameter identified by its
* {@code index}. {@code value} can be either a scalar value or {@link Parameter}.
* @param index zero based index to bind the parameter to
* @param value must not be {@code null}. Can be either a scalar value or {@link Parameter}
* @param value either a scalar value or {@link Parameter}
*/
GenericExecuteSpec bind(int index, Object value);
/**
* Bind a {@code null} value to a parameter identified by its {@code index}.
* @param index zero based index to bind the parameter to
* @param type must not be {@code null}
* @param type the parameter type
*/
GenericExecuteSpec bindNull(int index, Class<?> type);
/**
* Bind a non-{@code null} value to a parameter identified by its {@code name}.
* @param name must not be {@code null} or empty
* @param value must not be {@code null}
* @param name the name of the parameter
* @param value the value to bind
*/
GenericExecuteSpec bind(String name, Object value);
/**
* Bind a {@code null} value to a parameter identified by its {@code name}.
* @param name must not be {@code null} or empty
* @param type must not be {@code null}
* @param name the name of the parameter
* @param type the parameter type
*/
GenericExecuteSpec bindNull(String name, Class<?> type);
@ -195,11 +192,11 @@ public interface DatabaseClient extends ConnectionAccessor {
* DatabaseClient client = ;
* client.sql("SELECT book_id FROM book").filter(statement -> statement.fetchSize(100))
* </pre>
* @param filter the filter to be added to the chain
* @param filterFunction the filter to be added to the chain
*/
default GenericExecuteSpec filter(Function<? super Statement, ? extends Statement> filter) {
Assert.notNull(filter, "Statement FilterFunction must not be null");
return filter((statement, next) -> next.execute(filter.apply(statement)));
default GenericExecuteSpec filter(Function<? super Statement, ? extends Statement> filterFunction) {
Assert.notNull(filterFunction, "Filter function must not be null");
return filter((statement, next) -> next.execute(filterFunction.apply(statement)));
}
/**
@ -216,9 +213,9 @@ public interface DatabaseClient extends ConnectionAccessor {
/**
* Configure a result mapping {@link Function function} and enter the execution stage.
* @param mappingFunction must not be {@code null}
* @param <R> result type.
* @return a {@link FetchSpec} for configuration what to fetch. Guaranteed to be not {@code null}.
* @param mappingFunction a function that maps from {@link Row} to the result type
* @param <R> the result type
* @return a {@link FetchSpec} for configuration what to fetch
*/
default <R> RowsFetchSpec<R> map(Function<Row, R> mappingFunction) {
Assert.notNull(mappingFunction, "Mapping function must not be null");
@ -226,10 +223,11 @@ public interface DatabaseClient extends ConnectionAccessor {
}
/**
* Configure a result mapping {@link BiFunction function} and enter the execution stage.
* @param mappingFunction must not be {@code null}
* @param <R> result type.
* @return a {@link FetchSpec} for configuration what to fetch. Guaranteed to be not {@code null}.
* Configure a result mapping {@link BiFunction function} and enter the execution stage.
* @param mappingFunction a function that maps from {@link Row} and {@link RowMetadata}
* to the result type
* @param <R> the result type
* @return a {@link FetchSpec} for configuration what to fetch
*/
<R> RowsFetchSpec<R> map(BiFunction<Row, RowMetadata, R> mappingFunction);
@ -239,8 +237,9 @@ public interface DatabaseClient extends ConnectionAccessor {
FetchSpec<Map<String, Object>> fetch();
/**
* Perform the SQL call and return a {@link Mono} that completes without result on statement completion.
* @return a {@link Mono} ignoring its payload (actively dropping).
* Perform the SQL call and return a {@link Mono} that completes without result
* on statement completion.
* @return a {@link Mono} ignoring its payload (actively dropping)
*/
Mono<Void> then();
}

View File

@ -104,17 +104,14 @@ class DefaultDatabaseClient implements DatabaseClient {
}
@Override
public <T> Mono<T> inConnection(Function<Connection, Mono<T>> action)
throws DataAccessException {
public <T> Mono<T> inConnection(Function<Connection, Mono<T>> action) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
Mono<ConnectionCloseHolder> connectionMono = getConnection().map(
connection -> new ConnectionCloseHolder(connection, this::closeConnection));
return Mono.usingWhen(connectionMono, connectionCloseHolder -> {
// Create close-suppressing Connection proxy
Connection connectionToUse = createConnectionProxy(connectionCloseHolder.connection);
try {
return action.apply(connectionToUse);
}
@ -129,18 +126,14 @@ class DefaultDatabaseClient implements DatabaseClient {
}
@Override
public <T> Flux<T> inConnectionMany(Function<Connection, Flux<T>> action)
throws DataAccessException {
public <T> Flux<T> inConnectionMany(Function<Connection, Flux<T>> action) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
Mono<ConnectionCloseHolder> connectionMono = getConnection().map(
connection -> new ConnectionCloseHolder(connection, this::closeConnection));
return Flux.usingWhen(connectionMono, connectionCloseHolder -> {
// Create close-suppressing Connection proxy, also preparing returned
// Statements.
// Create close-suppressing Connection proxy, also preparing returned Statements.
Connection connectionToUse = createConnectionProxy(connectionCloseHolder.connection);
try {
return action.apply(connectionToUse);
}
@ -237,7 +230,7 @@ class DefaultDatabaseClient implements DatabaseClient {
this.byIndex = Collections.emptyMap();
this.byName = Collections.emptyMap();
this.sqlSupplier = sqlSupplier;
this.filterFunction = StatementFilterFunctions.empty();
this.filterFunction = StatementFilterFunction.EMPTY_FILTER;
}
DefaultGenericExecuteSpec(Map<Integer, Parameter> byIndex, Map<String, Parameter> byName,
@ -309,7 +302,8 @@ class DefaultDatabaseClient implements DatabaseClient {
@Override
public DefaultGenericExecuteSpec filter(StatementFilterFunction filter) {
Assert.notNull(filter, "Statement FilterFunction must not be null");
return new DefaultGenericExecuteSpec(this.byIndex, this.byName, this.sqlSupplier, this.filterFunction.andThen(filter));
return new DefaultGenericExecuteSpec(
this.byIndex, this.byName, this.sqlSupplier, this.filterFunction.andThen(filter));
}
@Override
@ -458,7 +452,6 @@ class DefaultDatabaseClient implements DatabaseClient {
Assert.state(StringUtils.hasText(sql), "SQL returned by SQL supplier must not be empty!");
return sql;
}
}

View File

@ -81,7 +81,6 @@ class DefaultDatabaseClientBuilder implements DatabaseClient.Builder {
Assert.notNull(this.connectionFactory, "ConnectionFactory must not be null");
BindMarkersFactory bindMarkers = this.bindMarkers;
if (bindMarkers == null) {
if (this.namedParameters) {
bindMarkers = BindMarkersFactoryResolver.resolve(this.connectionFactory);
@ -91,13 +90,12 @@ class DefaultDatabaseClientBuilder implements DatabaseClient.Builder {
}
}
return new DefaultDatabaseClient(bindMarkers, this.connectionFactory,
this.executeFunction, this.namedParameters);
return new DefaultDatabaseClient(
bindMarkers, this.connectionFactory, this.executeFunction, this.namedParameters);
}
@Override
public DatabaseClient.Builder apply(
Consumer<DatabaseClient.Builder> builderConsumer) {
public DatabaseClient.Builder apply(Consumer<DatabaseClient.Builder> builderConsumer) {
Assert.notNull(builderConsumer, "BuilderConsumer must not be null");
builderConsumer.accept(this);
return this;

View File

@ -52,6 +52,7 @@ class DefaultFetchSpec<T> implements FetchSpec<T> {
Function<Connection, Flux<Result>> resultFunction,
Function<Connection, Mono<Integer>> updatedRowsFunction,
BiFunction<Row, RowMetadata, T> mappingFunction) {
this.sql = sql;
this.connectionAccessor = connectionAccessor;
this.resultFunction = resultFunction;
@ -64,18 +65,14 @@ class DefaultFetchSpec<T> implements FetchSpec<T> {
public Mono<T> one() {
return all().buffer(2)
.flatMap(list -> {
if (list.isEmpty()) {
return Mono.empty();
}
if (list.size() > 1) {
return Mono.error(new IncorrectResultSizeDataAccessException(
String.format("Query [%s] returned non unique result.",
this.sql),
String.format("Query [%s] returned non unique result.", this.sql),
1));
}
return Mono.just(list.get(0));
}).next();
}

View File

@ -48,7 +48,7 @@ import org.reactivestreams.Publisher;
public interface ExecuteFunction {
/**
* Execute the given {@link Statement} for a stream of {@link Result}s.
* Execute the given {@link Statement} for a stream of {@link Result} objects.
* @param statement the request to execute
* @return the delayed result stream
*/

View File

@ -25,4 +25,6 @@ package org.springframework.r2dbc.core;
* @see RowsFetchSpec
* @see UpdatedRowsFetchSpec
*/
public interface FetchSpec<T> extends RowsFetchSpec<T>, UpdatedRowsFetchSpec {}
public interface FetchSpec<T> extends RowsFetchSpec<T>, UpdatedRowsFetchSpec {
}

View File

@ -40,30 +40,23 @@ class MapBindParameterSource implements BindParameterSource {
* Create a new empty {@link MapBindParameterSource}.
*/
MapBindParameterSource() {
this(new LinkedHashMap<>());
this.values = new LinkedHashMap<>();
}
/**
* Creates a new {@link MapBindParameterSource} given {@link Map} of
* {@link Parameter}.
*
* @param values the parameter mapping.
* Creates a new {@link MapBindParameterSource} given {@link Map} of {@link Parameter}.
* @param values the parameter mapping
*/
MapBindParameterSource(Map<String, Parameter> values) {
Assert.notNull(values, "Values must not be null");
this.values = values;
}
/**
* Add a key-value pair to the {@link MapBindParameterSource}. The value must not be
* {@code null}.
*
* @param paramName must not be {@code null}.
* @param value must not be {@code null}.
* @return {@code this} {@link MapBindParameterSource}
* Add a key-value pair to the {@link MapBindParameterSource}.
* @param paramName the name of the parameter
* @param value the parameter value to add (must not be {@code null})
*/
MapBindParameterSource addValue(String paramName, Object value) {
Assert.notNull(paramName, "Parameter name must not be null");
@ -91,8 +84,7 @@ class MapBindParameterSource implements BindParameterSource {
@Override
public Object getValue(String paramName) throws IllegalArgumentException {
if (!hasValue(paramName)) {
throw new IllegalArgumentException(
"No value registered for key '" + paramName + "'");
throw new IllegalArgumentException("No value registered for key '" + paramName + "'");
}
return this.values.get(paramName).getValue();
}

View File

@ -87,10 +87,9 @@ abstract class NamedParameterUtils {
/**
* Parse the SQL statement and locate any placeholders or named parameters.
* Namedparameters are substituted for a R2DBC placeholder.
*
* Named parameters are substituted for a R2DBC placeholder.
* @param sql the SQL statement
* @return the parsed statement, represented as {@link ParsedSql} instance.
* @return the parsed statement, represented as {@link ParsedSql} instance
*/
public static ParsedSql parseSqlStatement(String sql) {
Assert.notNull(sql, "SQL must not be null");
@ -134,20 +133,22 @@ abstract class NamedParameterUtils {
while (statement[j] != '}') {
j++;
if (j >= statement.length) {
throw new InvalidDataAccessApiUsageException("Non-terminated named parameter declaration " +
"at position " + i + " in statement: " + sql);
throw new InvalidDataAccessApiUsageException(
"Non-terminated named parameter declaration at position " + i +
" in statement: " + sql);
}
if (statement[j] == ':' || statement[j] == '{') {
throw new InvalidDataAccessApiUsageException("Parameter name contains invalid character '" +
statement[j] + "' at position " + i + " in statement: " + sql);
throw new InvalidDataAccessApiUsageException(
"Parameter name contains invalid character '" + statement[j] +
"' at position " + i + " in statement: " + sql);
}
}
if (j - i > 2) {
parameter = sql.substring(i + 2, j);
namedParameterCount = addNewNamedParameter(namedParameters,
namedParameterCount, parameter);
totalParameterCount = addNamedParameter(parameterList,
totalParameterCount, escapes, i, j + 1, parameter);
namedParameterCount = addNewNamedParameter(
namedParameters, namedParameterCount, parameter);
totalParameterCount = addNamedParameter(
parameterList, totalParameterCount, escapes, i, j + 1, parameter);
}
j++;
}
@ -157,10 +158,10 @@ abstract class NamedParameterUtils {
}
if (j - i > 1) {
parameter = sql.substring(i + 1, j);
namedParameterCount = addNewNamedParameter(namedParameters,
namedParameterCount, parameter);
totalParameterCount = addNamedParameter(parameterList,
totalParameterCount, escapes, i, j, parameter);
namedParameterCount = addNewNamedParameter(
namedParameters, namedParameterCount, parameter);
totalParameterCount = addNamedParameter(
parameterList, totalParameterCount, escapes, i, j, parameter);
}
}
i = j - 1;
@ -190,8 +191,8 @@ abstract class NamedParameterUtils {
return parsedSql;
}
private static int addNamedParameter(
List<ParameterHolder> parameterList, int totalParameterCount, int escapes, int i, int j, String parameter) {
private static int addNamedParameter(List<ParameterHolder> parameterList,
int totalParameterCount, int escapes, int i, int j, String parameter) {
parameterList.add(new ParameterHolder(parameter, i - escapes, j - escapes));
totalParameterCount++;
@ -256,10 +257,9 @@ abstract class NamedParameterUtils {
/**
* Parse the SQL statement and locate any placeholders or named parameters. Named
* parameters are substituted for a R2DBC placeholder, and any select list is expanded
* to the required number of placeholders. Select lists may contain an array of
* objects, and in that case the placeholders will be grouped and enclosed with
* parentheses. This allows for the use of "expression lists" in the SQL statement
* like: <br /><br />
* to the required number of placeholders. Select lists may contain an array of objects,
* and in that case the placeholders will be grouped and enclosed with parentheses.
* This allows for the use of "expression lists" in the SQL statement like:
* {@code select id, name, state from table where (name, age) in (('John', 35), ('Ann', 50))}
* <p>The parameter values passed in are used to determine the number of placeholders to
* be used for a select list. Select lists should be limited to 100 or fewer elements.
@ -269,17 +269,19 @@ abstract class NamedParameterUtils {
* @param bindMarkersFactory the bind marker factory.
* @param paramSource the source for named parameters
* @return the expanded query that accepts bind parameters and allows for execution
* without further translation
* without further translation
* @see #parseSqlStatement
*/
public static PreparedOperation<String> substituteNamedParameters(ParsedSql parsedSql,
BindMarkersFactory bindMarkersFactory, BindParameterSource paramSource) {
NamedParameters markerHolder = new NamedParameters(bindMarkersFactory);
String originalSql = parsedSql.getOriginalSql();
List<String> paramNames = parsedSql.getParameterNames();
if (paramNames.isEmpty()) {
return new ExpandedQuery(originalSql, markerHolder, paramSource);
}
StringBuilder actualSql = new StringBuilder(originalSql.length());
int lastIndex = 0;
for (int i = 0; i < paramNames.size(); i++) {
@ -318,7 +320,6 @@ abstract class NamedParameterUtils {
actualSql.append(marker.getPlaceholder(counter));
counter++;
}
}
}
else {
@ -343,6 +344,7 @@ abstract class NamedParameterUtils {
return (c < 128 && separatorIndex[c]) || Character.isWhitespace(c);
}
// -------------------------------------------------------------------------
// Convenience methods operating on a plain SQL String
// -------------------------------------------------------------------------
@ -359,6 +361,7 @@ abstract class NamedParameterUtils {
*/
public static PreparedOperation<String> substituteNamedParameters(String sql,
BindMarkersFactory bindMarkersFactory, BindParameterSource paramSource) {
ParsedSql parsedSql = parseSqlStatement(sql);
return substituteNamedParameters(parsedSql, bindMarkersFactory, paramSource);
}
@ -407,9 +410,9 @@ abstract class NamedParameterUtils {
public int hashCode() {
return Objects.hash(this.parameterName, this.startIndex, this.endIndex);
}
}
/**
* Holder for bind markers progress.
*/
@ -426,7 +429,6 @@ abstract class NamedParameterUtils {
this.identifiable = factory.identifiablePlaceholders();
}
/**
* Get the {@link NamedParameter} identified by {@code namedParameter}.
* Parameter objects get created if they do not yet exist.
@ -434,20 +436,16 @@ abstract class NamedParameterUtils {
* @return the named parameter
*/
NamedParameter getOrCreate(String namedParameter) {
List<NamedParameter> reference = this.references.computeIfAbsent(
namedParameter, ignore -> new ArrayList<>());
namedParameter, key -> new ArrayList<>());
if (reference.isEmpty()) {
NamedParameter param = new NamedParameter(namedParameter);
reference.add(param);
return param;
}
if (this.identifiable) {
return reference.get(0);
}
NamedParameter param = new NamedParameter(namedParameter);
reference.add(param);
return param;
@ -458,13 +456,13 @@ abstract class NamedParameterUtils {
return this.references.get(name);
}
class NamedParameter {
private final String namedParameter;
private final List<BindMarker> placeholders = new ArrayList<>();
NamedParameter(String namedParameter) {
this.namedParameter = namedParameter;
}
@ -475,9 +473,7 @@ abstract class NamedParameterUtils {
* @return the placeholder to be used in the SQL statement
*/
String addPlaceholder() {
BindMarker bindMarker = NamedParameters.this.bindMarkers.next(
this.namedParameter);
BindMarker bindMarker = NamedParameters.this.bindMarkers.next(this.namedParameter);
this.placeholders.add(bindMarker);
return bindMarker.getPlaceholder();
}
@ -487,17 +483,15 @@ abstract class NamedParameterUtils {
}
String getPlaceholder(int counter) {
while (counter + 1 > this.placeholders.size()) {
addPlaceholder();
}
return this.placeholders.get(counter).getPlaceholder();
}
}
}
/**
* Expanded query that allows binding of parameters using parameter names that were
* used to expand the query. Binding unrolls {@link Collection}s and nested arrays.
@ -510,35 +504,25 @@ abstract class NamedParameterUtils {
private final BindParameterSource parameterSource;
ExpandedQuery(String expandedSql, NamedParameters parameters,
BindParameterSource parameterSource) {
ExpandedQuery(String expandedSql, NamedParameters parameters, BindParameterSource parameterSource) {
this.expandedSql = expandedSql;
this.parameters = parameters;
this.parameterSource = parameterSource;
}
@SuppressWarnings("unchecked")
public void bind(BindTarget target, String identifier, Object value) {
List<BindMarker> bindMarkers = getBindMarkers(identifier);
if (bindMarkers == null) {
target.bind(identifier, value);
return;
}
if (value instanceof Collection) {
Collection<Object> collection = (Collection<Object>) value;
Iterator<Object> iterator = collection.iterator();
Iterator<BindMarker> markers = bindMarkers.iterator();
while (iterator.hasNext()) {
Object valueToBind = iterator.next();
if (valueToBind instanceof Object[]) {
Object[] objects = (Object[]) valueToBind;
for (Object object : objects) {
@ -557,24 +541,19 @@ abstract class NamedParameterUtils {
}
}
private void bind(BindTarget target, Iterator<BindMarker> markers,
Object valueToBind) {
private void bind(BindTarget target, Iterator<BindMarker> markers, Object valueToBind) {
Assert.isTrue(markers.hasNext(), () -> String.format(
"No bind marker for value [%s] in SQL [%s]. Check that the query was expanded using the same arguments.",
valueToBind, toQuery()));
markers.next().bind(target, valueToBind);
}
public void bindNull(BindTarget target, String identifier, Class<?> valueType) {
List<BindMarker> bindMarkers = getBindMarkers(identifier);
if (bindMarkers == null) {
target.bindNull(identifier, valueType);
return;
}
for (BindMarker bindMarker : bindMarkers) {
bindMarker.bindNull(target, valueType);
}
@ -582,19 +561,14 @@ abstract class NamedParameterUtils {
@Nullable
List<BindMarker> getBindMarkers(String identifier) {
List<NamedParameters.NamedParameter> parameters = this.parameters.getMarker(
identifier);
List<NamedParameters.NamedParameter> parameters = this.parameters.getMarker(identifier);
if (parameters == null) {
return null;
}
List<BindMarker> markers = new ArrayList<>();
for (NamedParameters.NamedParameter parameter : parameters) {
markers.addAll(parameter.placeholders);
}
return markers;
}
@ -605,14 +579,10 @@ abstract class NamedParameterUtils {
@Override
public void bindTo(BindTarget target) {
for (String namedParameter : this.parameterSource.getParameterNames()) {
Object value = this.parameterSource.getValue(namedParameter);
if (value == null) {
bindNull(target, namedParameter,
this.parameterSource.getType(namedParameter));
bindNull(target, namedParameter, this.parameterSource.getType(namedParameter));
}
else {
bind(target, namedParameter, value);
@ -624,7 +594,6 @@ abstract class NamedParameterUtils {
public String toQuery() {
return this.expandedSql;
}
}
}

View File

@ -27,6 +27,7 @@ import org.springframework.util.ObjectUtils;
* A database value that can be set in a statement.
*
* @author Mark Paluch
* @author Juergen Hoeller
* @since 5.3
*/
public final class Parameter {
@ -36,6 +37,7 @@ public final class Parameter {
private final Class<?> type;
private Parameter(@Nullable Object value, Class<?> type) {
Assert.notNull(type, "Type must not be null");
this.value = value;
@ -45,7 +47,7 @@ public final class Parameter {
/**
* Create a new {@link Parameter} from {@code value}.
* @param value must not be {@code null}
* @param value the parameter value
* @return the {@link Parameter} value for {@code value}
*/
public static Parameter from(Object value) {
@ -55,12 +57,12 @@ public final class Parameter {
/**
* Create a new {@link Parameter} from {@code value} and {@code type}.
* @param value can be {@code null}
* @param type must not be {@code null}
* @param value the parameter value (can be {@code null})
* @param type the parameter type
* @return the {@link Parameter} value for {@code value}
*/
public static Parameter fromOrEmpty(@Nullable Object value, Class<?> type) {
return value == null ? empty(type) : new Parameter(value, ClassUtils.getUserClass(value));
return (value == null ? empty(type) : new Parameter(value, ClassUtils.getUserClass(value)));
}
/**
@ -74,7 +76,7 @@ public final class Parameter {
/**
* Return the column value. Can be {@code null}.
* Return the column value (can be {@code null}).
* @see #hasValue()
*/
@Nullable
@ -83,14 +85,14 @@ public final class Parameter {
}
/**
* Returns the column value type. Must be also present if the {@code value} is {@code null}.
* Return the column value type. Must be also present if the {@code value} is {@code null}.
*/
public Class<?> getType() {
return this.type;
}
/**
* Returns whether this {@link Parameter} has a value.
* Return whether this {@link Parameter} has a value.
* @return {@code false} if {@link #getValue()} is {@code null}
*/
public boolean hasValue() {
@ -98,7 +100,7 @@ public final class Parameter {
}
/**
* Returns whether this {@link Parameter} has a empty.
* Return whether this {@link Parameter} has a empty.
* @return {@code true} if {@link #getValue()} is {@code null}
*/
public boolean isEmpty() {
@ -115,7 +117,8 @@ public final class Parameter {
return false;
}
Parameter other = (Parameter) obj;
return ObjectUtils.nullSafeEquals(this.value, other.value) && ObjectUtils.nullSafeEquals(this.type, other.type);
return (ObjectUtils.nullSafeEquals(this.value, other.value) &&
ObjectUtils.nullSafeEquals(this.type, other.type));
}
@Override
@ -125,7 +128,7 @@ public final class Parameter {
@Override
public String toString() {
return "Parameter[value=" + this.value + ", type=" + this.type + ']';
return "Parameter[value=" + this.value + ",type=" + this.type.getName() + "]";
}
}

View File

@ -28,11 +28,11 @@ import java.util.List;
*/
class ParsedSql {
private String originalSql;
private final String originalSql;
private List<String> parameterNames = new ArrayList<>();
private final List<String> parameterNames = new ArrayList<>();
private List<int[]> parameterIndexes = new ArrayList<>();
private final List<int[]> parameterIndexes = new ArrayList<>();
private int namedParameterCount;

View File

@ -24,6 +24,7 @@ import org.springframework.r2dbc.core.binding.BindTarget;
* Extension to {@link QueryOperation} for a prepared SQL query
* {@link Supplier} with bound parameters. Contains parameter
* bindings that can be {@link #bindTo bound} bound to a {@link BindTarget}.
*
* <p>Can be executed with {@link org.springframework.r2dbc.core.DatabaseClient}.
*
* @author Mark Paluch
@ -35,13 +36,13 @@ public interface PreparedOperation<T> extends QueryOperation {
/**
* Return the underlying query source.
* @return the query source, such as a statement/criteria object.
* @return the query source, such as a statement/criteria object
*/
T getSource();
/**
* Apply bindings to {@link BindTarget}.
* @param target the target to apply bindings to.
* @param target the target to apply bindings to
*/
void bindTo(BindTarget target);

View File

@ -32,7 +32,7 @@ import java.util.function.Supplier;
public interface QueryOperation extends Supplier<String> {
/**
* Returns the string-representation of this operation to
* Return the string-representation of this operation to
* be used with {@link io.r2dbc.spi.Statement} creation.
* @return the operation as SQL string
* @see io.r2dbc.spi.Connection#createStatement(String)

View File

@ -30,21 +30,20 @@ public interface RowsFetchSpec<T> {
/**
* Get exactly zero or one result.
*
* @return a mono emitting one element. {@link Mono#empty()} if no match found.
* @return a Mono emitting one element, or {@link Mono#empty()} if no match found.
* Completes with {@code IncorrectResultSizeDataAccessException} if more than one match found
*/
Mono<T> one();
/**
* Get the first or no result.
* @return a mono emitting the first element. {@link Mono#empty()} if no match found
* @return a Mono emitting the first element, or {@link Mono#empty()} if no match found
*/
Mono<T> first();
/**
* Get all matching elements.
* @return a flux emitting all results
* @return a Flux emitting all results
*/
Flux<T> all();

View File

@ -33,7 +33,7 @@ public interface SqlProvider {
/**
* Return the SQL string for this object, i.e.
* typically the SQL used for creating statements.
* @return the SQL string, or {@code null}
* @return the SQL string, or {@code null} if not available
*/
@Nullable
String getSql();

View File

@ -24,18 +24,27 @@ import org.springframework.util.Assert;
/**
* Represents a function that filters an {@link ExecuteFunction execute function}.
*
* <p>The filter is executed when a {@link org.reactivestreams.Subscriber} subscribes
* to the {@link Publisher} returned by the {@link DatabaseClient}.
*
* <p>StatementFilterFunctions are typically used to specify additional details on
* the Statement objects such as {@code fetchSize} or key generation.
*
* @author Mark Paluch
* @author Juergen Hoeller
* @since 5.3
* @see ExecuteFunction
*/
@FunctionalInterface
public interface StatementFilterFunction {
/**
* An empty {@link StatementFilterFunction} that delegates to {@link ExecuteFunction}.
*/
StatementFilterFunction EMPTY_FILTER = (statement, next) -> next.execute(statement);
/**
* Apply this filter to the given {@link Statement} and {@link ExecuteFunction}.
* <p>The given {@link ExecuteFunction} represents the next entity in the chain,

View File

@ -1,47 +0,0 @@
/*
* Copyright 2002-2020 the original author or authors.
*
* 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
*
* https://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.springframework.r2dbc.core;
import io.r2dbc.spi.Result;
import io.r2dbc.spi.Statement;
import org.reactivestreams.Publisher;
/**
* Collection of default {@link StatementFilterFunction}s.
*
* @author Mark Paluch
* @since 5.3
*/
enum StatementFilterFunctions implements StatementFilterFunction {
EMPTY_FILTER;
@Override
public Publisher<? extends Result> filter(Statement statement, ExecuteFunction next) {
return next.execute(statement);
}
/**
* Return an empty {@link StatementFilterFunction} that delegates to {@link ExecuteFunction}.
* @return an empty {@link StatementFilterFunction} that delegates to {@link ExecuteFunction}.
*/
public static StatementFilterFunction empty() {
return EMPTY_FILTER;
}
}

View File

@ -28,7 +28,7 @@ public interface UpdatedRowsFetchSpec {
/**
* Get the number of updated rows.
* @return a mono emitting the number of updated rows
* @return a Mono emitting the number of updated rows
*/
Mono<Integer> rowsUpdated();

View File

@ -34,8 +34,8 @@ import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
*/
class AnonymousBindMarkers implements BindMarkers {
private static final AtomicIntegerFieldUpdater<AnonymousBindMarkers> COUNTER_INCREMENTER = AtomicIntegerFieldUpdater
.newUpdater(AnonymousBindMarkers.class, "counter");
private static final AtomicIntegerFieldUpdater<AnonymousBindMarkers> COUNTER_INCREMENTER =
AtomicIntegerFieldUpdater.newUpdater(AnonymousBindMarkers.class, "counter");
private final String placeholder;

View File

@ -39,8 +39,8 @@ public interface BindMarker {
/**
* Bind the given {@code value} to the {@link Statement} using the underlying binding strategy.
* @param bindTarget the target to bind the value to
* @param value the actual value. Must not be {@code null}
* Use {@link #bindNull(BindTarget, Class)} for {@code null} values
* @param value the actual value (must not be {@code null};
* use {@link #bindNull(BindTarget, Class)} for {@code null} values)
* @see Statement#bind
*/
void bind(BindTarget bindTarget, Object value);
@ -48,7 +48,7 @@ public interface BindMarker {
/**
* Bind a {@code null} value to the {@link Statement} using the underlying binding strategy.
* @param bindTarget the target to bind the value to
* @param valueType value type, must not be {@code null}
* @param valueType the value type (must not be {@code null})
* @see Statement#bindNull
*/
void bindNull(BindTarget bindTarget, Class<?> valueType);

View File

@ -36,7 +36,6 @@ public interface BindMarkers {
/**
* Create a new {@link BindMarker}.
* @return a new {@link BindMarker}
*/
BindMarker next();

View File

@ -40,24 +40,20 @@ public interface BindMarkersFactory {
/**
* Create a new {@link BindMarkers} instance.
* @return a new {@link BindMarkers} instance
*/
BindMarkers create();
/**
* Return whether the {@link BindMarkersFactory} uses identifiable
* placeholders.
* @return whether the {@link BindMarkersFactory} uses identifiable
* placeholders. {@code false} if multiple placeholders cannot be
* distinguished by just the {@link BindMarker#getPlaceholder() placeholder}
* identifier.
* Return whether the {@link BindMarkersFactory} uses identifiable placeholders:
* {@code false} if multiple placeholders cannot be distinguished by just the
* {@link BindMarker#getPlaceholder() placeholder} identifier.
*/
default boolean identifiablePlaceholders() {
return true;
}
// Static, factory methods
// Static factory methods
/**
* Create index-based {@link BindMarkers} using indexes to bind parameters.
@ -88,12 +84,10 @@ public interface BindMarkersFactory {
static BindMarkersFactory anonymous(String placeholder) {
Assert.hasText(placeholder, "Placeholder must not be empty!");
return new BindMarkersFactory() {
@Override
public BindMarkers create() {
return new AnonymousBindMarkers(placeholder);
}
@Override
public boolean identifiablePlaceholders() {
return false;
@ -140,6 +134,7 @@ public interface BindMarkersFactory {
*/
static BindMarkersFactory named(String prefix, String namePrefix, int maxLength,
Function<String, String> hintFilterFunction) {
Assert.notNull(prefix, "Prefix must not be null");
Assert.notNull(namePrefix, "Index prefix must not be null");
Assert.notNull(hintFilterFunction, "Hint filter function must not be null");

View File

@ -45,31 +45,25 @@ public final class BindMarkersFactoryResolver {
/**
* Retrieve a {@link BindMarkersFactory} by inspecting {@link ConnectionFactory} and
* its metadata.
*
* Retrieve a {@link BindMarkersFactory} by inspecting {@link ConnectionFactory}
* and its metadata.
* @param connectionFactory the connection factory to inspect
* @return the resolved {@link BindMarkersFactory}
* @throws NoBindMarkersFactoryException if no {@link BindMarkersFactory} can be
* resolved
* @throws NoBindMarkersFactoryException if no {@link BindMarkersFactory} can be resolved
*/
public static BindMarkersFactory resolve(ConnectionFactory connectionFactory) {
for (BindMarkerFactoryProvider detector : DETECTORS) {
BindMarkersFactory bindMarkersFactory = detector.getBindMarkers(
connectionFactory);
BindMarkersFactory bindMarkersFactory = detector.getBindMarkers(connectionFactory);
if (bindMarkersFactory != null) {
return bindMarkersFactory;
}
}
throw new NoBindMarkersFactoryException(
String.format("Cannot determine a BindMarkersFactory for %s using %s",
connectionFactory.getMetadata().getName(), connectionFactory));
throw new NoBindMarkersFactoryException(String.format(
"Cannot determine a BindMarkersFactory for %s using %s",
connectionFactory.getMetadata().getName(), connectionFactory));
}
// utility constructor.
private BindMarkersFactoryResolver() {
}
@ -84,16 +78,13 @@ public final class BindMarkersFactoryResolver {
public interface BindMarkerFactoryProvider {
/**
* Returns a {@link BindMarkersFactory} for a {@link ConnectionFactory}.
*
* @param connectionFactory the connection factory to be used with the
* {@link BindMarkersFactory}.
* Return a {@link BindMarkersFactory} for a {@link ConnectionFactory}.
* @param connectionFactory the connection factory to be used with the {@link BindMarkersFactory}
* @return the {@link BindMarkersFactory} if the {@link BindMarkerFactoryProvider}
* can provide a bind marker factory object, otherwise {@code null}
* can provide a bind marker factory object, otherwise {@code null}
*/
@Nullable
BindMarkersFactory getBindMarkers(ConnectionFactory connectionFactory);
}
@ -102,18 +93,15 @@ public final class BindMarkersFactoryResolver {
* {@link BindMarkersFactory}.
*/
@SuppressWarnings("serial")
public static class NoBindMarkersFactoryException
extends NonTransientDataAccessException {
public static class NoBindMarkersFactoryException extends NonTransientDataAccessException {
/**
* Constructor for NoBindMarkersFactoryException.
*
* @param msg the detail message
*/
public NoBindMarkersFactoryException(String msg) {
super(msg);
}
}
@ -125,13 +113,13 @@ public final class BindMarkersFactoryResolver {
*/
static class BuiltInBindMarkersFactoryProvider implements BindMarkerFactoryProvider {
private static final Map<String, BindMarkersFactory> BUILTIN = new LinkedCaseInsensitiveMap<>(
Locale.ENGLISH);
private static final Map<String, BindMarkersFactory> BUILTIN =
new LinkedCaseInsensitiveMap<>(Locale.ENGLISH);
static {
BUILTIN.put("H2", BindMarkersFactory.indexed("$", 1));
BUILTIN.put("Microsoft SQL Server", BindMarkersFactory.named("@", "P", 32,
BuiltInBindMarkersFactoryProvider::filterBindMarker));
BuiltInBindMarkersFactoryProvider::filterBindMarker));
BUILTIN.put("MySQL", BindMarkersFactory.anonymous("?"));
BUILTIN.put("MariaDB", BindMarkersFactory.anonymous("?"));
BUILTIN.put("PostgreSQL", BindMarkersFactory.indexed("$", 1));
@ -142,40 +130,31 @@ public final class BindMarkersFactoryResolver {
public BindMarkersFactory getBindMarkers(ConnectionFactory connectionFactory) {
ConnectionFactoryMetadata metadata = connectionFactory.getMetadata();
BindMarkersFactory r2dbcDialect = BUILTIN.get(metadata.getName());
if (r2dbcDialect != null) {
return r2dbcDialect;
}
for (String it : BUILTIN.keySet()) {
if (metadata.getName().contains(it)) {
return BUILTIN.get(it);
}
}
return null;
}
private static String filterBindMarker(CharSequence input) {
StringBuilder builder = new StringBuilder();
StringBuilder builder = new StringBuilder(input.length());
for (int i = 0; i < input.length(); i++) {
char ch = input.charAt(i);
// ascii letter or digit
if (Character.isLetterOrDigit(ch) && ch < 127) {
builder.append(ch);
}
}
if (builder.length() == 0) {
return "";
}
return "_" + builder.toString();
}
}
}

View File

@ -55,11 +55,11 @@ public class Bindings implements Iterable<Bindings.Binding> {
}
/**
* Create {@link Bindings} from a {@link Map}.
* @param bindings must not be {@code null}
* Create {@link Bindings} from the given collection.
* @param bindings a collection of {@link Binding} objects
*/
public Bindings(Collection<Binding> bindings) {
Assert.notNull(bindings, "Bindings must not be null");
Assert.notNull(bindings, "Collection must not be null");
Map<BindMarker, Binding> mapping = CollectionUtils.newLinkedHashMap(bindings.size());
bindings.forEach(binding -> mapping.put(binding.getBindMarker(), binding));
this.bindings = mapping;
@ -75,10 +75,10 @@ public class Bindings implements Iterable<Bindings.Binding> {
}
/**
* Merge this bindings with an other {@link Bindings} object and create a new merged
* {@link Bindings} object.
* Merge this bindings with an other {@link Bindings} object and
* create a new merged {@link Bindings} object.
* @param other the object to merge with
* @return a new, merged {@link Bindings} object
* @return a newly merged {@link Bindings} object
*/
public Bindings and(Bindings other) {
return merge(this, other);
@ -116,25 +116,23 @@ public class Bindings implements Iterable<Bindings.Binding> {
/**
* Create a new, empty {@link Bindings} object.
* @return a new, empty {@link Bindings} object.
* Return an empty {@link Bindings} object.
*/
public static Bindings empty() {
return EMPTY;
}
/**
* Merge this bindings with an other {@link Bindings} object and create a new merged
* {@link Bindings} object.
* Merge this bindings with an other {@link Bindings} object and
* create a new merged {@link Bindings} object.
* @param left the left object to merge with
* @param right the right object to merge with
* @return a new, merged {@link Bindings} object
* @return a newly merged {@link Bindings} object
*/
public static Bindings merge(Bindings left, Bindings right) {
Assert.notNull(left, "Left side Bindings must not be null");
Assert.notNull(right, "Right side Bindings must not be null");
List<Binding> result = new ArrayList<>(
left.getBindings().size() + right.getBindings().size());
List<Binding> result = new ArrayList<>(left.getBindings().size() + right.getBindings().size());
result.addAll(left.getBindings().values());
result.addAll(right.getBindings().values());
return new Bindings(result);
@ -154,7 +152,6 @@ public class Bindings implements Iterable<Bindings.Binding> {
/**
* Return the associated {@link BindMarker}.
* @return the associated {@link BindMarker}.
*/
public BindMarker getBindMarker() {
return this.marker;
@ -162,8 +159,8 @@ public class Bindings implements Iterable<Bindings.Binding> {
/**
* Return whether the binding has a value associated with it.
* @return {@code true} if there is a value present, otherwise {@code false}
* for a {@code NULL} binding.
* @return {@code true} if there is a value present,
* otherwise {@code false} for a {@code NULL} binding
*/
public abstract boolean hasValue();
@ -177,8 +174,8 @@ public class Bindings implements Iterable<Bindings.Binding> {
/**
* Return the binding value.
* @return value of this binding. Can be {@code null}
* if this is a {@code NULL} binding.
* @return the value of this binding
* (can be {@code null} if this is a {@code NULL} binding)
*/
@Nullable
public abstract Object getValue();

View File

@ -22,14 +22,15 @@ import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
* Index-based bind marker. This implementation creates indexed bind
* markers using a numeric index and an optional prefix for bind markers
* to be represented within the query string.
*
* @author Mark Paluch
* @author Jens Schauder
* @since 5.3
*/
class IndexedBindMarkers implements BindMarkers {
private static final AtomicIntegerFieldUpdater<IndexedBindMarkers> COUNTER_INCREMENTER = AtomicIntegerFieldUpdater
.newUpdater(IndexedBindMarkers.class, "counter");
private static final AtomicIntegerFieldUpdater<IndexedBindMarkers> COUNTER_INCREMENTER =
AtomicIntegerFieldUpdater.newUpdater(IndexedBindMarkers.class, "counter");
private final int offset;
@ -59,6 +60,7 @@ class IndexedBindMarkers implements BindMarkers {
return new IndexedBindMarker(this.prefix + "" + (index + this.offset), index);
}
/**
* A single indexed bind marker.
* @author Mark Paluch
@ -69,13 +71,11 @@ class IndexedBindMarkers implements BindMarkers {
private final int index;
IndexedBindMarker(String placeholder, int index) {
this.placeholder = placeholder;
this.index = index;
}
@Override
public String getPlaceholder() {
return this.placeholder;
@ -94,7 +94,6 @@ class IndexedBindMarkers implements BindMarkers {
public int getIndex() {
return this.index;
}
}
}

View File

@ -36,7 +36,7 @@ public class MutableBindings extends Bindings {
/**
* Create new {@link MutableBindings}.
* @param markers must not be {@code null}.
* @param markers the {@link BindMarkers} to wrap
*/
public MutableBindings(BindMarkers markers) {
super(new LinkedHashMap<>());
@ -66,8 +66,8 @@ public class MutableBindings extends Bindings {
/**
* Bind a value to {@link BindMarker}.
* @param marker must not be {@code null}
* @param value must not be {@code null}
* @param marker the {@link BindMarker} to bind to
* @param value the value to bind
*/
public MutableBindings bind(BindMarker marker, Object value) {
Assert.notNull(marker, "BindMarker must not be null");
@ -79,7 +79,7 @@ public class MutableBindings extends Bindings {
/**
* Bind a value and return the related {@link BindMarker}.
* Increments {@link BindMarkers} state.
* @param value must not be {@code null}
* @param value the value to bind
*/
public BindMarker bind(Object value) {
Assert.notNull(value, "Value must not be null");
@ -90,8 +90,8 @@ public class MutableBindings extends Bindings {
/**
* Bind a {@code NULL} value to {@link BindMarker}.
* @param marker must not be {@code null}
* @param valueType must not be {@code null}
* @param marker the {@link BindMarker} to bind to
* @param valueType the value type
*/
public MutableBindings bindNull(BindMarker marker, Class<?> valueType) {
Assert.notNull(marker, "BindMarker must not be null");
@ -103,7 +103,7 @@ public class MutableBindings extends Bindings {
/**
* Bind a {@code NULL} value and return the related {@link BindMarker}.
* Increments {@link BindMarkers} state.
* @param valueType must not be {@code null}
* @param valueType the value type
*/
public BindMarker bindNull(Class<?> valueType) {
Assert.notNull(valueType, "Value type must not be null");

View File

@ -29,8 +29,8 @@ import org.springframework.util.Assert;
*/
class NamedBindMarkers implements BindMarkers {
private static final AtomicIntegerFieldUpdater<NamedBindMarkers> COUNTER_INCREMENTER = AtomicIntegerFieldUpdater
.newUpdater(NamedBindMarkers.class, "counter");
private static final AtomicIntegerFieldUpdater<NamedBindMarkers> COUNTER_INCREMENTER =
AtomicIntegerFieldUpdater.newUpdater(NamedBindMarkers.class, "counter");
private final String prefix;
@ -88,7 +88,6 @@ class NamedBindMarkers implements BindMarkers {
private final String identifier;
NamedBindMarker(String placeholder, String identifier) {
this.placeholder = placeholder;
this.identifier = identifier;
}
@ -107,7 +106,6 @@ class NamedBindMarkers implements BindMarkers {
public void bindNull(BindTarget target, Class<?> valueType) {
target.bindNull(this.identifier, valueType);
}
}
}