parent
16c8676e5b
commit
41247d49ba
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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() + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -36,7 +36,6 @@ public interface BindMarkers {
|
|||
|
||||
/**
|
||||
* Create a new {@link BindMarker}.
|
||||
* @return a new {@link BindMarker}
|
||||
*/
|
||||
BindMarker next();
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue