parent
16c8676e5b
commit
41247d49ba
|
@ -29,8 +29,6 @@ import io.r2dbc.spi.R2dbcTimeoutException;
|
||||||
import io.r2dbc.spi.R2dbcTransientException;
|
import io.r2dbc.spi.R2dbcTransientException;
|
||||||
import io.r2dbc.spi.R2dbcTransientResourceException;
|
import io.r2dbc.spi.R2dbcTransientResourceException;
|
||||||
import io.r2dbc.spi.Wrapped;
|
import io.r2dbc.spi.Wrapped;
|
||||||
import org.apache.commons.logging.Log;
|
|
||||||
import org.apache.commons.logging.LogFactory;
|
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
|
@ -69,11 +67,6 @@ public abstract class ConnectionFactoryUtils {
|
||||||
*/
|
*/
|
||||||
public static final int CONNECTION_SYNCHRONIZATION_ORDER = 1000;
|
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}.
|
* Obtain a {@link Connection} from the given {@link ConnectionFactory}.
|
||||||
|
@ -112,48 +105,34 @@ public abstract class ConnectionFactoryUtils {
|
||||||
if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
|
if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
|
||||||
conHolder.requested();
|
conHolder.requested();
|
||||||
if (!conHolder.hasConnection()) {
|
if (!conHolder.hasConnection()) {
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug("Fetching resumed R2DBC Connection from ConnectionFactory");
|
|
||||||
}
|
|
||||||
return fetchConnection(connectionFactory).doOnNext(conHolder::setConnection);
|
return fetchConnection(connectionFactory).doOnNext(conHolder::setConnection);
|
||||||
}
|
}
|
||||||
return Mono.just(conHolder.getConnection());
|
return Mono.just(conHolder.getConnection());
|
||||||
}
|
}
|
||||||
// Else we either got no holder or an empty thread-bound holder here.
|
// 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);
|
Mono<Connection> con = fetchConnection(connectionFactory);
|
||||||
|
|
||||||
if (synchronizationManager.isSynchronizationActive()) {
|
if (synchronizationManager.isSynchronizationActive()) {
|
||||||
|
return con.flatMap(connection -> Mono.just(connection).doOnNext(conn -> {
|
||||||
return con.flatMap(connection -> {
|
// Use same Connection for further R2DBC actions within the transaction.
|
||||||
return Mono.just(connection).doOnNext(conn -> {
|
// Thread-bound object will get removed by synchronization at transaction completion.
|
||||||
|
ConnectionHolder holderToUse = conHolder;
|
||||||
// Use same Connection for further R2DBC actions within the transaction.
|
if (holderToUse == null) {
|
||||||
// Thread-bound object will get removed by synchronization at transaction completion.
|
holderToUse = new ConnectionHolder(conn);
|
||||||
ConnectionHolder holderToUse = conHolder;
|
}
|
||||||
if (holderToUse == null) {
|
else {
|
||||||
holderToUse = new ConnectionHolder(conn);
|
holderToUse.setConnection(conn);
|
||||||
}
|
}
|
||||||
else {
|
holderToUse.requested();
|
||||||
holderToUse.setConnection(conn);
|
synchronizationManager
|
||||||
}
|
.registerSynchronization(new ConnectionSynchronization(holderToUse, connectionFactory));
|
||||||
holderToUse.requested();
|
holderToUse.setSynchronizedWithTransaction(true);
|
||||||
synchronizationManager
|
if (holderToUse != conHolder) {
|
||||||
.registerSynchronization(new ConnectionSynchronization(holderToUse, connectionFactory));
|
synchronizationManager.bindResource(connectionFactory, holderToUse);
|
||||||
holderToUse.setSynchronizedWithTransaction(true);
|
}
|
||||||
if (holderToUse != conHolder) {
|
}) // Unexpected exception from external delegation call -> close Connection and rethrow.
|
||||||
synchronizationManager.bindResource(connectionFactory, holderToUse);
|
.onErrorResume(e -> releaseConnection(connection, connectionFactory).then(Mono.error(e))));
|
||||||
}
|
|
||||||
}) // Unexpected exception from external delegation call -> close Connection and rethrow.
|
|
||||||
.onErrorResume(e -> releaseConnection(connection, connectionFactory).then(Mono.error(e)));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return con;
|
return con;
|
||||||
}).onErrorResume(NoTransactionException.class, e -> Mono.from(connectionFactory.create()));
|
}).onErrorResume(NoTransactionException.class, e -> Mono.from(connectionFactory.create()));
|
||||||
}
|
}
|
||||||
|
@ -356,9 +335,7 @@ public abstract class ConnectionFactoryUtils {
|
||||||
@Override
|
@Override
|
||||||
public Mono<Void> suspend() {
|
public Mono<Void> suspend() {
|
||||||
if (this.holderActive) {
|
if (this.holderActive) {
|
||||||
return TransactionSynchronizationManager.forCurrentTransaction()
|
return TransactionSynchronizationManager.forCurrentTransaction().flatMap(synchronizationManager -> {
|
||||||
.flatMap(synchronizationManager -> {
|
|
||||||
|
|
||||||
synchronizationManager.unbindResource(this.connectionFactory);
|
synchronizationManager.unbindResource(this.connectionFactory);
|
||||||
if (this.connectionHolder.hasConnection() && !this.connectionHolder.isOpen()) {
|
if (this.connectionHolder.hasConnection() && !this.connectionHolder.isOpen()) {
|
||||||
// Release Connection on suspend if the application doesn't keep
|
// Release Connection on suspend if the application doesn't keep
|
||||||
|
@ -371,7 +348,6 @@ public abstract class ConnectionFactoryUtils {
|
||||||
return Mono.empty();
|
return Mono.empty();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return Mono.empty();
|
return Mono.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,11 +364,10 @@ public abstract class ConnectionFactoryUtils {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Void> beforeCompletion() {
|
public Mono<Void> beforeCompletion() {
|
||||||
// Release Connection early if the holder is not open anymore
|
// Release Connection early if the holder is not open anymore (that is,
|
||||||
// (that is, not used by another resource
|
// not used by another resource that has its own cleanup via transaction
|
||||||
// that has its own cleanup via transaction synchronization),
|
// synchronization), to avoid issues with strict transaction implementations
|
||||||
// to avoid issues with strict transaction implementations that expect
|
// that expect the close call before transaction completion.
|
||||||
// the close call before transaction completion.
|
|
||||||
if (!this.connectionHolder.isOpen()) {
|
if (!this.connectionHolder.isOpen()) {
|
||||||
return TransactionSynchronizationManager.forCurrentTransaction().flatMap(synchronizationManager -> {
|
return TransactionSynchronizationManager.forCurrentTransaction().flatMap(synchronizationManager -> {
|
||||||
synchronizationManager.unbindResource(this.connectionFactory);
|
synchronizationManager.unbindResource(this.connectionFactory);
|
||||||
|
@ -414,8 +389,7 @@ public abstract class ConnectionFactoryUtils {
|
||||||
if (this.holderActive) {
|
if (this.holderActive) {
|
||||||
// The bound ConnectionHolder might not be available anymore,
|
// The bound ConnectionHolder might not be available anymore,
|
||||||
// since afterCompletion might get called from a different thread.
|
// since afterCompletion might get called from a different thread.
|
||||||
return TransactionSynchronizationManager.forCurrentTransaction()
|
return TransactionSynchronizationManager.forCurrentTransaction().flatMap(synchronizationManager -> {
|
||||||
.flatMap(synchronizationManager -> {
|
|
||||||
synchronizationManager.unbindResourceIfPossible(this.connectionFactory);
|
synchronizationManager.unbindResourceIfPossible(this.connectionFactory);
|
||||||
this.holderActive = false;
|
this.holderActive = false;
|
||||||
if (this.connectionHolder.hasConnection()) {
|
if (this.connectionHolder.hasConnection()) {
|
||||||
|
@ -426,7 +400,6 @@ public abstract class ConnectionFactoryUtils {
|
||||||
return Mono.empty();
|
return Mono.empty();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.connectionHolder.reset();
|
this.connectionHolder.reset();
|
||||||
return Mono.empty();
|
return Mono.empty();
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,8 +107,6 @@ public abstract class ScriptUtils {
|
||||||
|
|
||||||
private static final Log logger = LogFactory.getLog(ScriptUtils.class);
|
private static final Log logger = LogFactory.getLog(ScriptUtils.class);
|
||||||
|
|
||||||
// utility constructor
|
|
||||||
private ScriptUtils() {}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Split an SQL script into separate statements delimited by the provided
|
* 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))
|
return DataBufferUtils.join(DataBufferUtils.read(resource.getResource(), dataBufferFactory, 8192))
|
||||||
.handle((it, sink) -> {
|
.handle((it, sink) -> {
|
||||||
|
|
||||||
try (InputStream is = it.asInputStream()) {
|
try (InputStream is = it.asInputStream()) {
|
||||||
|
InputStreamReader in = (resource.getCharset() != null ?
|
||||||
InputStreamReader in = resource.getCharset() != null ? new InputStreamReader(is, resource.getCharset())
|
new InputStreamReader(is, resource.getCharset()) : new InputStreamReader(is));
|
||||||
: new InputStreamReader(is);
|
|
||||||
LineNumberReader lnr = new LineNumberReader(in);
|
LineNumberReader lnr = new LineNumberReader(in);
|
||||||
String script = readScript(lnr, commentPrefixes, separator, blockCommentEndDelimiter);
|
String script = readScript(lnr, commentPrefixes, separator, blockCommentEndDelimiter);
|
||||||
|
|
||||||
sink.next(script);
|
sink.next(script);
|
||||||
sink.complete();
|
sink.complete();
|
||||||
}
|
}
|
||||||
|
@ -548,9 +543,9 @@ public abstract class ScriptUtils {
|
||||||
* @see org.springframework.r2dbc.connection.ConnectionFactoryUtils#releaseConnection
|
* @see org.springframework.r2dbc.connection.ConnectionFactoryUtils#releaseConnection
|
||||||
*/
|
*/
|
||||||
public static Mono<Void> executeSqlScript(Connection connection, EncodedResource resource,
|
public static Mono<Void> executeSqlScript(Connection connection, EncodedResource resource,
|
||||||
DataBufferFactory dataBufferFactory, boolean continueOnError, boolean ignoreFailedDrops, String commentPrefix,
|
DataBufferFactory dataBufferFactory, boolean continueOnError, boolean ignoreFailedDrops,
|
||||||
@Nullable String separator, String blockCommentStartDelimiter, String blockCommentEndDelimiter)
|
String commentPrefix, @Nullable String separator, String blockCommentStartDelimiter,
|
||||||
throws ScriptException {
|
String blockCommentEndDelimiter) throws ScriptException {
|
||||||
|
|
||||||
return executeSqlScript(connection, resource, dataBufferFactory, continueOnError,
|
return executeSqlScript(connection, resource, dataBufferFactory, continueOnError,
|
||||||
ignoreFailedDrops, new String[] { commentPrefix }, separator,
|
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#getConnection
|
||||||
* @see org.springframework.r2dbc.connection.ConnectionFactoryUtils#releaseConnection
|
* @see org.springframework.r2dbc.connection.ConnectionFactoryUtils#releaseConnection
|
||||||
*/
|
*/
|
||||||
public static Mono<Void> executeSqlScript(Connection connection, EncodedResource resource, DataBufferFactory dataBufferFactory,
|
public static Mono<Void> executeSqlScript(Connection connection, EncodedResource resource,
|
||||||
boolean continueOnError,
|
DataBufferFactory dataBufferFactory, boolean continueOnError, boolean ignoreFailedDrops,
|
||||||
boolean ignoreFailedDrops, String[] commentPrefixes, @Nullable String separator,
|
String[] commentPrefixes, @Nullable String separator, String blockCommentStartDelimiter,
|
||||||
String blockCommentStartDelimiter, String blockCommentEndDelimiter) throws ScriptException {
|
String blockCommentEndDelimiter) throws ScriptException {
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("Executing SQL script from " + resource);
|
logger.debug("Executing SQL script from " + resource);
|
||||||
|
@ -622,17 +617,15 @@ public abstract class ScriptUtils {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
|
|
||||||
executeScript = executeScript.doOnComplete(() -> {
|
executeScript = executeScript.doOnComplete(() -> {
|
||||||
|
|
||||||
long elapsedTime = System.currentTimeMillis() - startTime;
|
long elapsedTime = System.currentTimeMillis() - startTime;
|
||||||
logger.debug("Executed SQL script from " + resource + " in " + elapsedTime + " ms.");
|
logger.debug("Executed SQL script from " + resource + " in " + elapsedTime + " ms.");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return executeScript.onErrorMap(ex -> !(ex instanceof ScriptException),
|
return executeScript.onErrorMap(ex -> !(ex instanceof ScriptException),
|
||||||
ex -> new UncategorizedScriptException("Failed to execute database script from resource [" + resource + "]",
|
ex -> new UncategorizedScriptException(
|
||||||
ex))
|
"Failed to execute database script from resource [" + resource + "]", ex))
|
||||||
.then();
|
.then();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -644,22 +637,21 @@ public abstract class ScriptUtils {
|
||||||
.collect(Collectors.summingLong(count -> count));
|
.collect(Collectors.summingLong(count -> count));
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
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 -> {
|
return execution.onErrorResume(ex -> {
|
||||||
|
|
||||||
boolean dropStatement = StringUtils.startsWithIgnoreCase(statement.trim(), "drop");
|
boolean dropStatement = StringUtils.startsWithIgnoreCase(statement.trim(), "drop");
|
||||||
if (continueOnError || (dropStatement && ignoreFailedDrops)) {
|
if (continueOnError || (dropStatement && ignoreFailedDrops)) {
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug(ScriptStatementFailedException.buildErrorMessage(statement, statementNumber.get(), resource),
|
logger.debug(ScriptStatementFailedException.buildErrorMessage(
|
||||||
ex);
|
statement, statementNumber.get(), resource), ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return Mono.error(new ScriptStatementFailedException(statement, statementNumber.get(), resource, ex));
|
return Mono.error(new ScriptStatementFailedException(statement, statementNumber.get(), resource, ex));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Mono.empty();
|
return Mono.empty();
|
||||||
}).then();
|
}).then();
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,6 +167,7 @@ public abstract class AbstractRoutingConnectionFactory implements ConnectionFact
|
||||||
*/
|
*/
|
||||||
protected ConnectionFactory resolveSpecifiedConnectionFactory(Object connectionFactory)
|
protected ConnectionFactory resolveSpecifiedConnectionFactory(Object connectionFactory)
|
||||||
throws IllegalArgumentException {
|
throws IllegalArgumentException {
|
||||||
|
|
||||||
if (connectionFactory instanceof ConnectionFactory) {
|
if (connectionFactory instanceof ConnectionFactory) {
|
||||||
return (ConnectionFactory) connectionFactory;
|
return (ConnectionFactory) connectionFactory;
|
||||||
}
|
}
|
||||||
|
@ -174,17 +175,14 @@ public abstract class AbstractRoutingConnectionFactory implements ConnectionFact
|
||||||
return this.connectionFactoryLookup.getConnectionFactory((String) connectionFactory);
|
return this.connectionFactoryLookup.getConnectionFactory((String) connectionFactory);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException("Illegal connection factory value - " +
|
||||||
"Illegal connection factory value - only 'io.r2dbc.spi.ConnectionFactory' and 'String' supported: "
|
"only 'io.r2dbc.spi.ConnectionFactory' and 'String' supported: " + connectionFactory);
|
||||||
+ connectionFactory);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Connection> create() {
|
public Mono<Connection> create() {
|
||||||
return determineTargetConnectionFactory() //
|
return determineTargetConnectionFactory().map(ConnectionFactory::create).flatMap(Mono::from);
|
||||||
.map(ConnectionFactory::create) //
|
|
||||||
.flatMap(Mono::from);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -56,7 +56,6 @@ public class MapConnectionFactoryLookup implements ConnectionFactoryLookup {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new instance of the {@link MapConnectionFactoryLookup} class.
|
* Create a new instance of the {@link MapConnectionFactoryLookup} class.
|
||||||
*
|
|
||||||
* @param connectionFactoryName the name under which the supplied {@link ConnectionFactory} is to be added
|
* @param connectionFactoryName the name under which the supplied {@link ConnectionFactory} is to be added
|
||||||
* @param connectionFactory the {@link ConnectionFactory} 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.
|
* Return the parameter value for the requested named parameter.
|
||||||
* @param paramName the name of the 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
|
* @throws IllegalArgumentException if there is no value
|
||||||
* for the requested parameter
|
* for the requested parameter
|
||||||
*/
|
*/
|
||||||
|
@ -64,8 +64,8 @@ interface BindParameterSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return parameter names of the underlying parameter source.
|
* Return the parameter names of the underlying parameter source.
|
||||||
* @return parameter names of the underlying parameter source.
|
* @return an iterator over the parameter names
|
||||||
*/
|
*/
|
||||||
Iterable<String> getParameterNames();
|
Iterable<String> getParameterNames();
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ import org.springframework.util.LinkedCaseInsensitiveMap;
|
||||||
*/
|
*/
|
||||||
public class ColumnMapRowMapper implements BiFunction<Row, RowMetadata, Map<String, Object>> {
|
public class ColumnMapRowMapper implements BiFunction<Row, RowMetadata, Map<String, Object>> {
|
||||||
|
|
||||||
/** Default instance. */
|
/** A default {@code ColumnMapRowMapper} instance. */
|
||||||
public final static ColumnMapRowMapper INSTANCE = new ColumnMapRowMapper();
|
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
|
* SQL call along with options leading to the execution. The SQL string can
|
||||||
* contain either native parameter bind markers or named parameters (e.g.
|
* contain either native parameter bind markers or named parameters (e.g.
|
||||||
* {@literal :foo, :bar}) when {@link NamedParameterExpander} is enabled.
|
* {@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}
|
* @return a new {@link GenericExecuteSpec}
|
||||||
* @see NamedParameterExpander
|
* @see NamedParameterExpander
|
||||||
* @see DatabaseClient.Builder#namedParameters(boolean)
|
* @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
|
* bind markers or named parameters (e.g. {@literal :foo, :bar}) when
|
||||||
* {@link NamedParameterExpander} is enabled.
|
* {@link NamedParameterExpander} is enabled.
|
||||||
* <p>Accepts {@link PreparedOperation} as SQL and binding {@link Supplier}
|
* <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}
|
* @return a new {@link GenericExecuteSpec}
|
||||||
* @see NamedParameterExpander
|
* @see NamedParameterExpander
|
||||||
* @see DatabaseClient.Builder#namedParameters(boolean)
|
* @see DatabaseClient.Builder#namedParameters(boolean)
|
||||||
|
@ -115,34 +115,31 @@ public interface DatabaseClient extends ConnectionAccessor {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure the {@link BindMarkersFactory BindMarkers} to be used.
|
* Configure the {@link BindMarkersFactory BindMarkers} to be used.
|
||||||
* @param bindMarkers must not be {@code null}
|
|
||||||
*/
|
*/
|
||||||
Builder bindMarkers(BindMarkersFactory bindMarkers);
|
Builder bindMarkers(BindMarkersFactory bindMarkers);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure the {@link ConnectionFactory R2DBC connector}.
|
* Configure the {@link ConnectionFactory R2DBC connector}.
|
||||||
* @param factory must not be {@code null}
|
|
||||||
*/
|
*/
|
||||||
Builder connectionFactory(ConnectionFactory factory);
|
Builder connectionFactory(ConnectionFactory factory);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure a {@link ExecuteFunction} to execute {@link Statement} objects.
|
* Configure a {@link ExecuteFunction} to execute {@link Statement} objects.
|
||||||
* @param executeFunction must not be {@code null}
|
|
||||||
* @see Statement#execute()
|
* @see Statement#execute()
|
||||||
*/
|
*/
|
||||||
Builder executeFunction(ExecuteFunction executeFunction);
|
Builder executeFunction(ExecuteFunction executeFunction);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure whether to use named parameter expansion. Defaults to {@code true}.
|
* Configure whether to use named parameter expansion.
|
||||||
* @param enabled {@code true} to use named parameter expansion.
|
* Defaults to {@code true}.
|
||||||
* {@code false} to disable named parameter expansion.
|
* @param enabled {@code true} to use named parameter expansion;
|
||||||
|
* {@code false} to disable named parameter expansion
|
||||||
* @see NamedParameterExpander
|
* @see NamedParameterExpander
|
||||||
*/
|
*/
|
||||||
Builder namedParameters(boolean enabled);
|
Builder namedParameters(boolean enabled);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures a {@link Consumer} to configure this builder.
|
* Configures a {@link Consumer} to configure this builder.
|
||||||
* @param builderConsumer must not be {@code null}.
|
|
||||||
*/
|
*/
|
||||||
Builder apply(Consumer<Builder> builderConsumer);
|
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
|
* 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}.
|
* {@code index}. {@code value} can be either a scalar value or {@link Parameter}.
|
||||||
* @param index zero based index to bind the parameter to
|
* @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);
|
GenericExecuteSpec bind(int index, Object value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bind a {@code null} value to a parameter identified by its {@code index}.
|
* Bind a {@code null} value to a parameter identified by its {@code index}.
|
||||||
* @param index zero based index to bind the parameter to
|
* @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);
|
GenericExecuteSpec bindNull(int index, Class<?> type);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bind a non-{@code null} value to a parameter identified by its {@code name}.
|
* Bind a non-{@code null} value to a parameter identified by its {@code name}.
|
||||||
* @param name must not be {@code null} or empty
|
* @param name the name of the parameter
|
||||||
* @param value must not be {@code null}
|
* @param value the value to bind
|
||||||
*/
|
*/
|
||||||
GenericExecuteSpec bind(String name, Object value);
|
GenericExecuteSpec bind(String name, Object value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bind a {@code null} value to a parameter identified by its {@code name}.
|
* Bind a {@code null} value to a parameter identified by its {@code name}.
|
||||||
* @param name must not be {@code null} or empty
|
* @param name the name of the parameter
|
||||||
* @param type must not be {@code null}
|
* @param type the parameter type
|
||||||
*/
|
*/
|
||||||
GenericExecuteSpec bindNull(String name, Class<?> type);
|
GenericExecuteSpec bindNull(String name, Class<?> type);
|
||||||
|
|
||||||
|
@ -195,11 +192,11 @@ public interface DatabaseClient extends ConnectionAccessor {
|
||||||
* DatabaseClient client = …;
|
* DatabaseClient client = …;
|
||||||
* client.sql("SELECT book_id FROM book").filter(statement -> statement.fetchSize(100))
|
* client.sql("SELECT book_id FROM book").filter(statement -> statement.fetchSize(100))
|
||||||
* </pre>
|
* </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) {
|
default GenericExecuteSpec filter(Function<? super Statement, ? extends Statement> filterFunction) {
|
||||||
Assert.notNull(filter, "Statement FilterFunction must not be null");
|
Assert.notNull(filterFunction, "Filter function must not be null");
|
||||||
return filter((statement, next) -> next.execute(filter.apply(statement)));
|
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.
|
* Configure a result mapping {@link Function function} and enter the execution stage.
|
||||||
* @param mappingFunction must not be {@code null}
|
* @param mappingFunction a function that maps from {@link Row} to the result type
|
||||||
* @param <R> result type.
|
* @param <R> the result type
|
||||||
* @return a {@link FetchSpec} for configuration what to fetch. Guaranteed to be not {@code null}.
|
* @return a {@link FetchSpec} for configuration what to fetch
|
||||||
*/
|
*/
|
||||||
default <R> RowsFetchSpec<R> map(Function<Row, R> mappingFunction) {
|
default <R> RowsFetchSpec<R> map(Function<Row, R> mappingFunction) {
|
||||||
Assert.notNull(mappingFunction, "Mapping function must not be null");
|
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.
|
* Configure a result mapping {@link BiFunction function} and enter the execution stage.
|
||||||
* @param mappingFunction must not be {@code null}
|
* @param mappingFunction a function that maps from {@link Row} and {@link RowMetadata}
|
||||||
* @param <R> result type.
|
* to the result type
|
||||||
* @return a {@link FetchSpec} for configuration what to fetch. Guaranteed to be not {@code null}.
|
* @param <R> the result type
|
||||||
|
* @return a {@link FetchSpec} for configuration what to fetch
|
||||||
*/
|
*/
|
||||||
<R> RowsFetchSpec<R> map(BiFunction<Row, RowMetadata, R> mappingFunction);
|
<R> RowsFetchSpec<R> map(BiFunction<Row, RowMetadata, R> mappingFunction);
|
||||||
|
|
||||||
|
@ -239,8 +237,9 @@ public interface DatabaseClient extends ConnectionAccessor {
|
||||||
FetchSpec<Map<String, Object>> fetch();
|
FetchSpec<Map<String, Object>> fetch();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform the SQL call and return a {@link Mono} that completes without result on statement completion.
|
* Perform the SQL call and return a {@link Mono} that completes without result
|
||||||
* @return a {@link Mono} ignoring its payload (actively dropping).
|
* on statement completion.
|
||||||
|
* @return a {@link Mono} ignoring its payload (actively dropping)
|
||||||
*/
|
*/
|
||||||
Mono<Void> then();
|
Mono<Void> then();
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,17 +104,14 @@ class DefaultDatabaseClient implements DatabaseClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Mono<T> inConnection(Function<Connection, Mono<T>> action)
|
public <T> Mono<T> inConnection(Function<Connection, Mono<T>> action) throws DataAccessException {
|
||||||
throws DataAccessException {
|
|
||||||
Assert.notNull(action, "Callback object must not be null");
|
Assert.notNull(action, "Callback object must not be null");
|
||||||
Mono<ConnectionCloseHolder> connectionMono = getConnection().map(
|
Mono<ConnectionCloseHolder> connectionMono = getConnection().map(
|
||||||
connection -> new ConnectionCloseHolder(connection, this::closeConnection));
|
connection -> new ConnectionCloseHolder(connection, this::closeConnection));
|
||||||
|
|
||||||
return Mono.usingWhen(connectionMono, connectionCloseHolder -> {
|
return Mono.usingWhen(connectionMono, connectionCloseHolder -> {
|
||||||
|
|
||||||
// Create close-suppressing Connection proxy
|
// Create close-suppressing Connection proxy
|
||||||
Connection connectionToUse = createConnectionProxy(connectionCloseHolder.connection);
|
Connection connectionToUse = createConnectionProxy(connectionCloseHolder.connection);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return action.apply(connectionToUse);
|
return action.apply(connectionToUse);
|
||||||
}
|
}
|
||||||
|
@ -129,18 +126,14 @@ class DefaultDatabaseClient implements DatabaseClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Flux<T> inConnectionMany(Function<Connection, Flux<T>> action)
|
public <T> Flux<T> inConnectionMany(Function<Connection, Flux<T>> action) throws DataAccessException {
|
||||||
throws DataAccessException {
|
|
||||||
Assert.notNull(action, "Callback object must not be null");
|
Assert.notNull(action, "Callback object must not be null");
|
||||||
Mono<ConnectionCloseHolder> connectionMono = getConnection().map(
|
Mono<ConnectionCloseHolder> connectionMono = getConnection().map(
|
||||||
connection -> new ConnectionCloseHolder(connection, this::closeConnection));
|
connection -> new ConnectionCloseHolder(connection, this::closeConnection));
|
||||||
|
|
||||||
return Flux.usingWhen(connectionMono, connectionCloseHolder -> {
|
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);
|
Connection connectionToUse = createConnectionProxy(connectionCloseHolder.connection);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return action.apply(connectionToUse);
|
return action.apply(connectionToUse);
|
||||||
}
|
}
|
||||||
|
@ -237,7 +230,7 @@ class DefaultDatabaseClient implements DatabaseClient {
|
||||||
this.byIndex = Collections.emptyMap();
|
this.byIndex = Collections.emptyMap();
|
||||||
this.byName = Collections.emptyMap();
|
this.byName = Collections.emptyMap();
|
||||||
this.sqlSupplier = sqlSupplier;
|
this.sqlSupplier = sqlSupplier;
|
||||||
this.filterFunction = StatementFilterFunctions.empty();
|
this.filterFunction = StatementFilterFunction.EMPTY_FILTER;
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultGenericExecuteSpec(Map<Integer, Parameter> byIndex, Map<String, Parameter> byName,
|
DefaultGenericExecuteSpec(Map<Integer, Parameter> byIndex, Map<String, Parameter> byName,
|
||||||
|
@ -309,7 +302,8 @@ class DefaultDatabaseClient implements DatabaseClient {
|
||||||
@Override
|
@Override
|
||||||
public DefaultGenericExecuteSpec filter(StatementFilterFunction filter) {
|
public DefaultGenericExecuteSpec filter(StatementFilterFunction filter) {
|
||||||
Assert.notNull(filter, "Statement FilterFunction must not be null");
|
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
|
@Override
|
||||||
|
@ -458,7 +452,6 @@ class DefaultDatabaseClient implements DatabaseClient {
|
||||||
Assert.state(StringUtils.hasText(sql), "SQL returned by SQL supplier must not be empty!");
|
Assert.state(StringUtils.hasText(sql), "SQL returned by SQL supplier must not be empty!");
|
||||||
return sql;
|
return sql;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -81,7 +81,6 @@ class DefaultDatabaseClientBuilder implements DatabaseClient.Builder {
|
||||||
Assert.notNull(this.connectionFactory, "ConnectionFactory must not be null");
|
Assert.notNull(this.connectionFactory, "ConnectionFactory must not be null");
|
||||||
|
|
||||||
BindMarkersFactory bindMarkers = this.bindMarkers;
|
BindMarkersFactory bindMarkers = this.bindMarkers;
|
||||||
|
|
||||||
if (bindMarkers == null) {
|
if (bindMarkers == null) {
|
||||||
if (this.namedParameters) {
|
if (this.namedParameters) {
|
||||||
bindMarkers = BindMarkersFactoryResolver.resolve(this.connectionFactory);
|
bindMarkers = BindMarkersFactoryResolver.resolve(this.connectionFactory);
|
||||||
|
@ -91,13 +90,12 @@ class DefaultDatabaseClientBuilder implements DatabaseClient.Builder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new DefaultDatabaseClient(bindMarkers, this.connectionFactory,
|
return new DefaultDatabaseClient(
|
||||||
this.executeFunction, this.namedParameters);
|
bindMarkers, this.connectionFactory, this.executeFunction, this.namedParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DatabaseClient.Builder apply(
|
public DatabaseClient.Builder apply(Consumer<DatabaseClient.Builder> builderConsumer) {
|
||||||
Consumer<DatabaseClient.Builder> builderConsumer) {
|
|
||||||
Assert.notNull(builderConsumer, "BuilderConsumer must not be null");
|
Assert.notNull(builderConsumer, "BuilderConsumer must not be null");
|
||||||
builderConsumer.accept(this);
|
builderConsumer.accept(this);
|
||||||
return this;
|
return this;
|
||||||
|
|
|
@ -52,6 +52,7 @@ class DefaultFetchSpec<T> implements FetchSpec<T> {
|
||||||
Function<Connection, Flux<Result>> resultFunction,
|
Function<Connection, Flux<Result>> resultFunction,
|
||||||
Function<Connection, Mono<Integer>> updatedRowsFunction,
|
Function<Connection, Mono<Integer>> updatedRowsFunction,
|
||||||
BiFunction<Row, RowMetadata, T> mappingFunction) {
|
BiFunction<Row, RowMetadata, T> mappingFunction) {
|
||||||
|
|
||||||
this.sql = sql;
|
this.sql = sql;
|
||||||
this.connectionAccessor = connectionAccessor;
|
this.connectionAccessor = connectionAccessor;
|
||||||
this.resultFunction = resultFunction;
|
this.resultFunction = resultFunction;
|
||||||
|
@ -64,18 +65,14 @@ class DefaultFetchSpec<T> implements FetchSpec<T> {
|
||||||
public Mono<T> one() {
|
public Mono<T> one() {
|
||||||
return all().buffer(2)
|
return all().buffer(2)
|
||||||
.flatMap(list -> {
|
.flatMap(list -> {
|
||||||
|
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
return Mono.empty();
|
return Mono.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (list.size() > 1) {
|
if (list.size() > 1) {
|
||||||
return Mono.error(new IncorrectResultSizeDataAccessException(
|
return Mono.error(new IncorrectResultSizeDataAccessException(
|
||||||
String.format("Query [%s] returned non unique result.",
|
String.format("Query [%s] returned non unique result.", this.sql),
|
||||||
this.sql),
|
|
||||||
1));
|
1));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Mono.just(list.get(0));
|
return Mono.just(list.get(0));
|
||||||
}).next();
|
}).next();
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ import org.reactivestreams.Publisher;
|
||||||
public interface ExecuteFunction {
|
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
|
* @param statement the request to execute
|
||||||
* @return the delayed result stream
|
* @return the delayed result stream
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -25,4 +25,6 @@ package org.springframework.r2dbc.core;
|
||||||
* @see RowsFetchSpec
|
* @see RowsFetchSpec
|
||||||
* @see UpdatedRowsFetchSpec
|
* @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}.
|
* Create a new empty {@link MapBindParameterSource}.
|
||||||
*/
|
*/
|
||||||
MapBindParameterSource() {
|
MapBindParameterSource() {
|
||||||
this(new LinkedHashMap<>());
|
this.values = new LinkedHashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link MapBindParameterSource} given {@link Map} of
|
* Creates a new {@link MapBindParameterSource} given {@link Map} of {@link Parameter}.
|
||||||
* {@link Parameter}.
|
* @param values the parameter mapping
|
||||||
*
|
|
||||||
* @param values the parameter mapping.
|
|
||||||
*/
|
*/
|
||||||
MapBindParameterSource(Map<String, Parameter> values) {
|
MapBindParameterSource(Map<String, Parameter> values) {
|
||||||
|
|
||||||
Assert.notNull(values, "Values must not be null");
|
Assert.notNull(values, "Values must not be null");
|
||||||
|
|
||||||
this.values = values;
|
this.values = values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a key-value pair to the {@link MapBindParameterSource}. The value must not be
|
* Add a key-value pair to the {@link MapBindParameterSource}.
|
||||||
* {@code null}.
|
* @param paramName the name of the parameter
|
||||||
*
|
* @param value the parameter value to add (must not be {@code null})
|
||||||
* @param paramName must not be {@code null}.
|
|
||||||
* @param value must not be {@code null}.
|
|
||||||
* @return {@code this} {@link MapBindParameterSource}
|
|
||||||
*/
|
*/
|
||||||
MapBindParameterSource addValue(String paramName, Object value) {
|
MapBindParameterSource addValue(String paramName, Object value) {
|
||||||
Assert.notNull(paramName, "Parameter name must not be null");
|
Assert.notNull(paramName, "Parameter name must not be null");
|
||||||
|
@ -91,8 +84,7 @@ class MapBindParameterSource implements BindParameterSource {
|
||||||
@Override
|
@Override
|
||||||
public Object getValue(String paramName) throws IllegalArgumentException {
|
public Object getValue(String paramName) throws IllegalArgumentException {
|
||||||
if (!hasValue(paramName)) {
|
if (!hasValue(paramName)) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException("No value registered for key '" + paramName + "'");
|
||||||
"No value registered for key '" + paramName + "'");
|
|
||||||
}
|
}
|
||||||
return this.values.get(paramName).getValue();
|
return this.values.get(paramName).getValue();
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,10 +87,9 @@ abstract class NamedParameterUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse the SQL statement and locate any placeholders or named parameters.
|
* 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
|
* @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) {
|
public static ParsedSql parseSqlStatement(String sql) {
|
||||||
Assert.notNull(sql, "SQL must not be null");
|
Assert.notNull(sql, "SQL must not be null");
|
||||||
|
@ -134,20 +133,22 @@ abstract class NamedParameterUtils {
|
||||||
while (statement[j] != '}') {
|
while (statement[j] != '}') {
|
||||||
j++;
|
j++;
|
||||||
if (j >= statement.length) {
|
if (j >= statement.length) {
|
||||||
throw new InvalidDataAccessApiUsageException("Non-terminated named parameter declaration " +
|
throw new InvalidDataAccessApiUsageException(
|
||||||
"at position " + i + " in statement: " + sql);
|
"Non-terminated named parameter declaration at position " + i +
|
||||||
|
" in statement: " + sql);
|
||||||
}
|
}
|
||||||
if (statement[j] == ':' || statement[j] == '{') {
|
if (statement[j] == ':' || statement[j] == '{') {
|
||||||
throw new InvalidDataAccessApiUsageException("Parameter name contains invalid character '" +
|
throw new InvalidDataAccessApiUsageException(
|
||||||
statement[j] + "' at position " + i + " in statement: " + sql);
|
"Parameter name contains invalid character '" + statement[j] +
|
||||||
|
"' at position " + i + " in statement: " + sql);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (j - i > 2) {
|
if (j - i > 2) {
|
||||||
parameter = sql.substring(i + 2, j);
|
parameter = sql.substring(i + 2, j);
|
||||||
namedParameterCount = addNewNamedParameter(namedParameters,
|
namedParameterCount = addNewNamedParameter(
|
||||||
namedParameterCount, parameter);
|
namedParameters, namedParameterCount, parameter);
|
||||||
totalParameterCount = addNamedParameter(parameterList,
|
totalParameterCount = addNamedParameter(
|
||||||
totalParameterCount, escapes, i, j + 1, parameter);
|
parameterList, totalParameterCount, escapes, i, j + 1, parameter);
|
||||||
}
|
}
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
|
@ -157,10 +158,10 @@ abstract class NamedParameterUtils {
|
||||||
}
|
}
|
||||||
if (j - i > 1) {
|
if (j - i > 1) {
|
||||||
parameter = sql.substring(i + 1, j);
|
parameter = sql.substring(i + 1, j);
|
||||||
namedParameterCount = addNewNamedParameter(namedParameters,
|
namedParameterCount = addNewNamedParameter(
|
||||||
namedParameterCount, parameter);
|
namedParameters, namedParameterCount, parameter);
|
||||||
totalParameterCount = addNamedParameter(parameterList,
|
totalParameterCount = addNamedParameter(
|
||||||
totalParameterCount, escapes, i, j, parameter);
|
parameterList, totalParameterCount, escapes, i, j, parameter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
i = j - 1;
|
i = j - 1;
|
||||||
|
@ -190,8 +191,8 @@ abstract class NamedParameterUtils {
|
||||||
return parsedSql;
|
return parsedSql;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int addNamedParameter(
|
private static int addNamedParameter(List<ParameterHolder> parameterList,
|
||||||
List<ParameterHolder> parameterList, int totalParameterCount, int escapes, int i, int j, String parameter) {
|
int totalParameterCount, int escapes, int i, int j, String parameter) {
|
||||||
|
|
||||||
parameterList.add(new ParameterHolder(parameter, i - escapes, j - escapes));
|
parameterList.add(new ParameterHolder(parameter, i - escapes, j - escapes));
|
||||||
totalParameterCount++;
|
totalParameterCount++;
|
||||||
|
@ -256,10 +257,9 @@ abstract class NamedParameterUtils {
|
||||||
/**
|
/**
|
||||||
* Parse the SQL statement and locate any placeholders or named parameters. Named
|
* 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
|
* 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
|
* to the required number of placeholders. Select lists may contain an array of objects,
|
||||||
* objects, and in that case the placeholders will be grouped and enclosed with
|
* and in that case the placeholders will be grouped and enclosed with parentheses.
|
||||||
* parentheses. This allows for the use of "expression lists" in the SQL statement
|
* This allows for the use of "expression lists" in the SQL statement like:
|
||||||
* like: <br /><br />
|
|
||||||
* {@code select id, name, state from table where (name, age) in (('John', 35), ('Ann', 50))}
|
* {@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
|
* <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.
|
* 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 bindMarkersFactory the bind marker factory.
|
||||||
* @param paramSource the source for named parameters
|
* @param paramSource the source for named parameters
|
||||||
* @return the expanded query that accepts bind parameters and allows for execution
|
* @return the expanded query that accepts bind parameters and allows for execution
|
||||||
* without further translation
|
* without further translation
|
||||||
* @see #parseSqlStatement
|
* @see #parseSqlStatement
|
||||||
*/
|
*/
|
||||||
public static PreparedOperation<String> substituteNamedParameters(ParsedSql parsedSql,
|
public static PreparedOperation<String> substituteNamedParameters(ParsedSql parsedSql,
|
||||||
BindMarkersFactory bindMarkersFactory, BindParameterSource paramSource) {
|
BindMarkersFactory bindMarkersFactory, BindParameterSource paramSource) {
|
||||||
|
|
||||||
NamedParameters markerHolder = new NamedParameters(bindMarkersFactory);
|
NamedParameters markerHolder = new NamedParameters(bindMarkersFactory);
|
||||||
String originalSql = parsedSql.getOriginalSql();
|
String originalSql = parsedSql.getOriginalSql();
|
||||||
List<String> paramNames = parsedSql.getParameterNames();
|
List<String> paramNames = parsedSql.getParameterNames();
|
||||||
if (paramNames.isEmpty()) {
|
if (paramNames.isEmpty()) {
|
||||||
return new ExpandedQuery(originalSql, markerHolder, paramSource);
|
return new ExpandedQuery(originalSql, markerHolder, paramSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
StringBuilder actualSql = new StringBuilder(originalSql.length());
|
StringBuilder actualSql = new StringBuilder(originalSql.length());
|
||||||
int lastIndex = 0;
|
int lastIndex = 0;
|
||||||
for (int i = 0; i < paramNames.size(); i++) {
|
for (int i = 0; i < paramNames.size(); i++) {
|
||||||
|
@ -318,7 +320,6 @@ abstract class NamedParameterUtils {
|
||||||
actualSql.append(marker.getPlaceholder(counter));
|
actualSql.append(marker.getPlaceholder(counter));
|
||||||
counter++;
|
counter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -343,6 +344,7 @@ abstract class NamedParameterUtils {
|
||||||
return (c < 128 && separatorIndex[c]) || Character.isWhitespace(c);
|
return (c < 128 && separatorIndex[c]) || Character.isWhitespace(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// Convenience methods operating on a plain SQL String
|
// Convenience methods operating on a plain SQL String
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
@ -359,6 +361,7 @@ abstract class NamedParameterUtils {
|
||||||
*/
|
*/
|
||||||
public static PreparedOperation<String> substituteNamedParameters(String sql,
|
public static PreparedOperation<String> substituteNamedParameters(String sql,
|
||||||
BindMarkersFactory bindMarkersFactory, BindParameterSource paramSource) {
|
BindMarkersFactory bindMarkersFactory, BindParameterSource paramSource) {
|
||||||
|
|
||||||
ParsedSql parsedSql = parseSqlStatement(sql);
|
ParsedSql parsedSql = parseSqlStatement(sql);
|
||||||
return substituteNamedParameters(parsedSql, bindMarkersFactory, paramSource);
|
return substituteNamedParameters(parsedSql, bindMarkersFactory, paramSource);
|
||||||
}
|
}
|
||||||
|
@ -407,9 +410,9 @@ abstract class NamedParameterUtils {
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(this.parameterName, this.startIndex, this.endIndex);
|
return Objects.hash(this.parameterName, this.startIndex, this.endIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holder for bind markers progress.
|
* Holder for bind markers progress.
|
||||||
*/
|
*/
|
||||||
|
@ -426,7 +429,6 @@ abstract class NamedParameterUtils {
|
||||||
this.identifiable = factory.identifiablePlaceholders();
|
this.identifiable = factory.identifiablePlaceholders();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the {@link NamedParameter} identified by {@code namedParameter}.
|
* Get the {@link NamedParameter} identified by {@code namedParameter}.
|
||||||
* Parameter objects get created if they do not yet exist.
|
* Parameter objects get created if they do not yet exist.
|
||||||
|
@ -434,20 +436,16 @@ abstract class NamedParameterUtils {
|
||||||
* @return the named parameter
|
* @return the named parameter
|
||||||
*/
|
*/
|
||||||
NamedParameter getOrCreate(String namedParameter) {
|
NamedParameter getOrCreate(String namedParameter) {
|
||||||
|
|
||||||
List<NamedParameter> reference = this.references.computeIfAbsent(
|
List<NamedParameter> reference = this.references.computeIfAbsent(
|
||||||
namedParameter, ignore -> new ArrayList<>());
|
namedParameter, key -> new ArrayList<>());
|
||||||
|
|
||||||
if (reference.isEmpty()) {
|
if (reference.isEmpty()) {
|
||||||
NamedParameter param = new NamedParameter(namedParameter);
|
NamedParameter param = new NamedParameter(namedParameter);
|
||||||
reference.add(param);
|
reference.add(param);
|
||||||
return param;
|
return param;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.identifiable) {
|
if (this.identifiable) {
|
||||||
return reference.get(0);
|
return reference.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
NamedParameter param = new NamedParameter(namedParameter);
|
NamedParameter param = new NamedParameter(namedParameter);
|
||||||
reference.add(param);
|
reference.add(param);
|
||||||
return param;
|
return param;
|
||||||
|
@ -458,13 +456,13 @@ abstract class NamedParameterUtils {
|
||||||
return this.references.get(name);
|
return this.references.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class NamedParameter {
|
class NamedParameter {
|
||||||
|
|
||||||
private final String namedParameter;
|
private final String namedParameter;
|
||||||
|
|
||||||
private final List<BindMarker> placeholders = new ArrayList<>();
|
private final List<BindMarker> placeholders = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
NamedParameter(String namedParameter) {
|
NamedParameter(String namedParameter) {
|
||||||
this.namedParameter = namedParameter;
|
this.namedParameter = namedParameter;
|
||||||
}
|
}
|
||||||
|
@ -475,9 +473,7 @@ abstract class NamedParameterUtils {
|
||||||
* @return the placeholder to be used in the SQL statement
|
* @return the placeholder to be used in the SQL statement
|
||||||
*/
|
*/
|
||||||
String addPlaceholder() {
|
String addPlaceholder() {
|
||||||
|
BindMarker bindMarker = NamedParameters.this.bindMarkers.next(this.namedParameter);
|
||||||
BindMarker bindMarker = NamedParameters.this.bindMarkers.next(
|
|
||||||
this.namedParameter);
|
|
||||||
this.placeholders.add(bindMarker);
|
this.placeholders.add(bindMarker);
|
||||||
return bindMarker.getPlaceholder();
|
return bindMarker.getPlaceholder();
|
||||||
}
|
}
|
||||||
|
@ -487,17 +483,15 @@ abstract class NamedParameterUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
String getPlaceholder(int counter) {
|
String getPlaceholder(int counter) {
|
||||||
|
|
||||||
while (counter + 1 > this.placeholders.size()) {
|
while (counter + 1 > this.placeholders.size()) {
|
||||||
addPlaceholder();
|
addPlaceholder();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.placeholders.get(counter).getPlaceholder();
|
return this.placeholders.get(counter).getPlaceholder();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expanded query that allows binding of parameters using parameter names that were
|
* 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.
|
* used to expand the query. Binding unrolls {@link Collection}s and nested arrays.
|
||||||
|
@ -510,35 +504,25 @@ abstract class NamedParameterUtils {
|
||||||
|
|
||||||
private final BindParameterSource parameterSource;
|
private final BindParameterSource parameterSource;
|
||||||
|
|
||||||
|
ExpandedQuery(String expandedSql, NamedParameters parameters, BindParameterSource parameterSource) {
|
||||||
ExpandedQuery(String expandedSql, NamedParameters parameters,
|
|
||||||
BindParameterSource parameterSource) {
|
|
||||||
this.expandedSql = expandedSql;
|
this.expandedSql = expandedSql;
|
||||||
this.parameters = parameters;
|
this.parameters = parameters;
|
||||||
this.parameterSource = parameterSource;
|
this.parameterSource = parameterSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void bind(BindTarget target, String identifier, Object value) {
|
public void bind(BindTarget target, String identifier, Object value) {
|
||||||
|
|
||||||
List<BindMarker> bindMarkers = getBindMarkers(identifier);
|
List<BindMarker> bindMarkers = getBindMarkers(identifier);
|
||||||
|
|
||||||
if (bindMarkers == null) {
|
if (bindMarkers == null) {
|
||||||
target.bind(identifier, value);
|
target.bind(identifier, value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value instanceof Collection) {
|
if (value instanceof Collection) {
|
||||||
Collection<Object> collection = (Collection<Object>) value;
|
Collection<Object> collection = (Collection<Object>) value;
|
||||||
|
|
||||||
Iterator<Object> iterator = collection.iterator();
|
Iterator<Object> iterator = collection.iterator();
|
||||||
Iterator<BindMarker> markers = bindMarkers.iterator();
|
Iterator<BindMarker> markers = bindMarkers.iterator();
|
||||||
|
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
|
|
||||||
Object valueToBind = iterator.next();
|
Object valueToBind = iterator.next();
|
||||||
|
|
||||||
if (valueToBind instanceof Object[]) {
|
if (valueToBind instanceof Object[]) {
|
||||||
Object[] objects = (Object[]) valueToBind;
|
Object[] objects = (Object[]) valueToBind;
|
||||||
for (Object object : objects) {
|
for (Object object : objects) {
|
||||||
|
@ -557,24 +541,19 @@ abstract class NamedParameterUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void bind(BindTarget target, Iterator<BindMarker> markers,
|
private void bind(BindTarget target, Iterator<BindMarker> markers, Object valueToBind) {
|
||||||
Object valueToBind) {
|
|
||||||
|
|
||||||
Assert.isTrue(markers.hasNext(), () -> String.format(
|
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.",
|
"No bind marker for value [%s] in SQL [%s]. Check that the query was expanded using the same arguments.",
|
||||||
valueToBind, toQuery()));
|
valueToBind, toQuery()));
|
||||||
|
|
||||||
markers.next().bind(target, valueToBind);
|
markers.next().bind(target, valueToBind);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void bindNull(BindTarget target, String identifier, Class<?> valueType) {
|
public void bindNull(BindTarget target, String identifier, Class<?> valueType) {
|
||||||
List<BindMarker> bindMarkers = getBindMarkers(identifier);
|
List<BindMarker> bindMarkers = getBindMarkers(identifier);
|
||||||
|
|
||||||
if (bindMarkers == null) {
|
if (bindMarkers == null) {
|
||||||
target.bindNull(identifier, valueType);
|
target.bindNull(identifier, valueType);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (BindMarker bindMarker : bindMarkers) {
|
for (BindMarker bindMarker : bindMarkers) {
|
||||||
bindMarker.bindNull(target, valueType);
|
bindMarker.bindNull(target, valueType);
|
||||||
}
|
}
|
||||||
|
@ -582,19 +561,14 @@ abstract class NamedParameterUtils {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
List<BindMarker> getBindMarkers(String identifier) {
|
List<BindMarker> getBindMarkers(String identifier) {
|
||||||
List<NamedParameters.NamedParameter> parameters = this.parameters.getMarker(
|
List<NamedParameters.NamedParameter> parameters = this.parameters.getMarker(identifier);
|
||||||
identifier);
|
|
||||||
|
|
||||||
if (parameters == null) {
|
if (parameters == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<BindMarker> markers = new ArrayList<>();
|
List<BindMarker> markers = new ArrayList<>();
|
||||||
|
|
||||||
for (NamedParameters.NamedParameter parameter : parameters) {
|
for (NamedParameters.NamedParameter parameter : parameters) {
|
||||||
markers.addAll(parameter.placeholders);
|
markers.addAll(parameter.placeholders);
|
||||||
}
|
}
|
||||||
|
|
||||||
return markers;
|
return markers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -605,14 +579,10 @@ abstract class NamedParameterUtils {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bindTo(BindTarget target) {
|
public void bindTo(BindTarget target) {
|
||||||
|
|
||||||
for (String namedParameter : this.parameterSource.getParameterNames()) {
|
for (String namedParameter : this.parameterSource.getParameterNames()) {
|
||||||
|
|
||||||
Object value = this.parameterSource.getValue(namedParameter);
|
Object value = this.parameterSource.getValue(namedParameter);
|
||||||
|
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
bindNull(target, namedParameter,
|
bindNull(target, namedParameter, this.parameterSource.getType(namedParameter));
|
||||||
this.parameterSource.getType(namedParameter));
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
bind(target, namedParameter, value);
|
bind(target, namedParameter, value);
|
||||||
|
@ -624,7 +594,6 @@ abstract class NamedParameterUtils {
|
||||||
public String toQuery() {
|
public String toQuery() {
|
||||||
return this.expandedSql;
|
return this.expandedSql;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.springframework.util.ObjectUtils;
|
||||||
* A database value that can be set in a statement.
|
* A database value that can be set in a statement.
|
||||||
*
|
*
|
||||||
* @author Mark Paluch
|
* @author Mark Paluch
|
||||||
|
* @author Juergen Hoeller
|
||||||
* @since 5.3
|
* @since 5.3
|
||||||
*/
|
*/
|
||||||
public final class Parameter {
|
public final class Parameter {
|
||||||
|
@ -36,6 +37,7 @@ public final class Parameter {
|
||||||
|
|
||||||
private final Class<?> type;
|
private final Class<?> type;
|
||||||
|
|
||||||
|
|
||||||
private Parameter(@Nullable Object value, Class<?> type) {
|
private Parameter(@Nullable Object value, Class<?> type) {
|
||||||
Assert.notNull(type, "Type must not be null");
|
Assert.notNull(type, "Type must not be null");
|
||||||
this.value = value;
|
this.value = value;
|
||||||
|
@ -45,7 +47,7 @@ public final class Parameter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@link Parameter} from {@code value}.
|
* 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}
|
* @return the {@link Parameter} value for {@code value}
|
||||||
*/
|
*/
|
||||||
public static Parameter from(Object 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}.
|
* Create a new {@link Parameter} from {@code value} and {@code type}.
|
||||||
* @param value can be {@code null}
|
* @param value the parameter value (can be {@code null})
|
||||||
* @param type must not be {@code null}
|
* @param type the parameter type
|
||||||
* @return the {@link Parameter} value for {@code value}
|
* @return the {@link Parameter} value for {@code value}
|
||||||
*/
|
*/
|
||||||
public static Parameter fromOrEmpty(@Nullable Object value, Class<?> type) {
|
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()
|
* @see #hasValue()
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@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() {
|
public Class<?> getType() {
|
||||||
return this.type;
|
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}
|
* @return {@code false} if {@link #getValue()} is {@code null}
|
||||||
*/
|
*/
|
||||||
public boolean hasValue() {
|
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}
|
* @return {@code true} if {@link #getValue()} is {@code null}
|
||||||
*/
|
*/
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
|
@ -115,7 +117,8 @@ public final class Parameter {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Parameter other = (Parameter) obj;
|
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
|
@Override
|
||||||
|
@ -125,7 +128,7 @@ public final class Parameter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
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 {
|
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;
|
private int namedParameterCount;
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.springframework.r2dbc.core.binding.BindTarget;
|
||||||
* Extension to {@link QueryOperation} for a prepared SQL query
|
* Extension to {@link QueryOperation} for a prepared SQL query
|
||||||
* {@link Supplier} with bound parameters. Contains parameter
|
* {@link Supplier} with bound parameters. Contains parameter
|
||||||
* bindings that can be {@link #bindTo bound} bound to a {@link BindTarget}.
|
* bindings that can be {@link #bindTo bound} bound to a {@link BindTarget}.
|
||||||
|
*
|
||||||
* <p>Can be executed with {@link org.springframework.r2dbc.core.DatabaseClient}.
|
* <p>Can be executed with {@link org.springframework.r2dbc.core.DatabaseClient}.
|
||||||
*
|
*
|
||||||
* @author Mark Paluch
|
* @author Mark Paluch
|
||||||
|
@ -35,13 +36,13 @@ public interface PreparedOperation<T> extends QueryOperation {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the underlying query source.
|
* 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();
|
T getSource();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply bindings to {@link BindTarget}.
|
* 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);
|
void bindTo(BindTarget target);
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ import java.util.function.Supplier;
|
||||||
public interface QueryOperation extends Supplier<String> {
|
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.
|
* be used with {@link io.r2dbc.spi.Statement} creation.
|
||||||
* @return the operation as SQL string
|
* @return the operation as SQL string
|
||||||
* @see io.r2dbc.spi.Connection#createStatement(String)
|
* @see io.r2dbc.spi.Connection#createStatement(String)
|
||||||
|
|
|
@ -30,21 +30,20 @@ public interface RowsFetchSpec<T> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get exactly zero or one result.
|
* Get exactly zero or one result.
|
||||||
*
|
* @return a Mono emitting one element, or {@link Mono#empty()} if no match found.
|
||||||
* @return a mono emitting one element. {@link Mono#empty()} if no match found.
|
|
||||||
* Completes with {@code IncorrectResultSizeDataAccessException} if more than one match found
|
* Completes with {@code IncorrectResultSizeDataAccessException} if more than one match found
|
||||||
*/
|
*/
|
||||||
Mono<T> one();
|
Mono<T> one();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the first or no result.
|
* 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();
|
Mono<T> first();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all matching elements.
|
* Get all matching elements.
|
||||||
* @return a flux emitting all results
|
* @return a Flux emitting all results
|
||||||
*/
|
*/
|
||||||
Flux<T> all();
|
Flux<T> all();
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ public interface SqlProvider {
|
||||||
/**
|
/**
|
||||||
* Return the SQL string for this object, i.e.
|
* Return the SQL string for this object, i.e.
|
||||||
* typically the SQL used for creating statements.
|
* 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
|
@Nullable
|
||||||
String getSql();
|
String getSql();
|
||||||
|
|
|
@ -24,18 +24,27 @@ import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a function that filters an {@link ExecuteFunction execute function}.
|
* Represents a function that filters an {@link ExecuteFunction execute function}.
|
||||||
|
*
|
||||||
* <p>The filter is executed when a {@link org.reactivestreams.Subscriber} subscribes
|
* <p>The filter is executed when a {@link org.reactivestreams.Subscriber} subscribes
|
||||||
* to the {@link Publisher} returned by the {@link DatabaseClient}.
|
* to the {@link Publisher} returned by the {@link DatabaseClient}.
|
||||||
|
*
|
||||||
* <p>StatementFilterFunctions are typically used to specify additional details on
|
* <p>StatementFilterFunctions are typically used to specify additional details on
|
||||||
* the Statement objects such as {@code fetchSize} or key generation.
|
* the Statement objects such as {@code fetchSize} or key generation.
|
||||||
*
|
*
|
||||||
* @author Mark Paluch
|
* @author Mark Paluch
|
||||||
|
* @author Juergen Hoeller
|
||||||
* @since 5.3
|
* @since 5.3
|
||||||
* @see ExecuteFunction
|
* @see ExecuteFunction
|
||||||
*/
|
*/
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface StatementFilterFunction {
|
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}.
|
* Apply this filter to the given {@link Statement} and {@link ExecuteFunction}.
|
||||||
* <p>The given {@link ExecuteFunction} represents the next entity in the chain,
|
* <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.
|
* 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();
|
Mono<Integer> rowsUpdated();
|
||||||
|
|
||||||
|
|
|
@ -34,8 +34,8 @@ import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
|
||||||
*/
|
*/
|
||||||
class AnonymousBindMarkers implements BindMarkers {
|
class AnonymousBindMarkers implements BindMarkers {
|
||||||
|
|
||||||
private static final AtomicIntegerFieldUpdater<AnonymousBindMarkers> COUNTER_INCREMENTER = AtomicIntegerFieldUpdater
|
private static final AtomicIntegerFieldUpdater<AnonymousBindMarkers> COUNTER_INCREMENTER =
|
||||||
.newUpdater(AnonymousBindMarkers.class, "counter");
|
AtomicIntegerFieldUpdater.newUpdater(AnonymousBindMarkers.class, "counter");
|
||||||
|
|
||||||
|
|
||||||
private final String placeholder;
|
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.
|
* Bind the given {@code value} to the {@link Statement} using the underlying binding strategy.
|
||||||
* @param bindTarget the target to bind the value to
|
* @param bindTarget the target to bind the value to
|
||||||
* @param value the actual value. Must not be {@code null}
|
* @param value the actual value (must not be {@code null};
|
||||||
* Use {@link #bindNull(BindTarget, Class)} for {@code null} values
|
* use {@link #bindNull(BindTarget, Class)} for {@code null} values)
|
||||||
* @see Statement#bind
|
* @see Statement#bind
|
||||||
*/
|
*/
|
||||||
void bind(BindTarget bindTarget, Object value);
|
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.
|
* Bind a {@code null} value to the {@link Statement} using the underlying binding strategy.
|
||||||
* @param bindTarget the target to bind the value to
|
* @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
|
* @see Statement#bindNull
|
||||||
*/
|
*/
|
||||||
void bindNull(BindTarget bindTarget, Class<?> valueType);
|
void bindNull(BindTarget bindTarget, Class<?> valueType);
|
||||||
|
|
|
@ -36,7 +36,6 @@ public interface BindMarkers {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@link BindMarker}.
|
* Create a new {@link BindMarker}.
|
||||||
* @return a new {@link BindMarker}
|
|
||||||
*/
|
*/
|
||||||
BindMarker next();
|
BindMarker next();
|
||||||
|
|
||||||
|
|
|
@ -40,24 +40,20 @@ public interface BindMarkersFactory {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@link BindMarkers} instance.
|
* Create a new {@link BindMarkers} instance.
|
||||||
* @return a new {@link BindMarkers} instance
|
|
||||||
*/
|
*/
|
||||||
BindMarkers create();
|
BindMarkers create();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return whether the {@link BindMarkersFactory} uses identifiable
|
* Return whether the {@link BindMarkersFactory} uses identifiable placeholders:
|
||||||
* placeholders.
|
* {@code false} if multiple placeholders cannot be distinguished by just the
|
||||||
* @return whether the {@link BindMarkersFactory} uses identifiable
|
* {@link BindMarker#getPlaceholder() placeholder} identifier.
|
||||||
* placeholders. {@code false} if multiple placeholders cannot be
|
|
||||||
* distinguished by just the {@link BindMarker#getPlaceholder() placeholder}
|
|
||||||
* identifier.
|
|
||||||
*/
|
*/
|
||||||
default boolean identifiablePlaceholders() {
|
default boolean identifiablePlaceholders() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Static, factory methods
|
// Static factory methods
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create index-based {@link BindMarkers} using indexes to bind parameters.
|
* Create index-based {@link BindMarkers} using indexes to bind parameters.
|
||||||
|
@ -88,12 +84,10 @@ public interface BindMarkersFactory {
|
||||||
static BindMarkersFactory anonymous(String placeholder) {
|
static BindMarkersFactory anonymous(String placeholder) {
|
||||||
Assert.hasText(placeholder, "Placeholder must not be empty!");
|
Assert.hasText(placeholder, "Placeholder must not be empty!");
|
||||||
return new BindMarkersFactory() {
|
return new BindMarkersFactory() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BindMarkers create() {
|
public BindMarkers create() {
|
||||||
return new AnonymousBindMarkers(placeholder);
|
return new AnonymousBindMarkers(placeholder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean identifiablePlaceholders() {
|
public boolean identifiablePlaceholders() {
|
||||||
return false;
|
return false;
|
||||||
|
@ -140,6 +134,7 @@ public interface BindMarkersFactory {
|
||||||
*/
|
*/
|
||||||
static BindMarkersFactory named(String prefix, String namePrefix, int maxLength,
|
static BindMarkersFactory named(String prefix, String namePrefix, int maxLength,
|
||||||
Function<String, String> hintFilterFunction) {
|
Function<String, String> hintFilterFunction) {
|
||||||
|
|
||||||
Assert.notNull(prefix, "Prefix must not be null");
|
Assert.notNull(prefix, "Prefix must not be null");
|
||||||
Assert.notNull(namePrefix, "Index prefix must not be null");
|
Assert.notNull(namePrefix, "Index prefix must not be null");
|
||||||
Assert.notNull(hintFilterFunction, "Hint filter function 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
|
* Retrieve a {@link BindMarkersFactory} by inspecting {@link ConnectionFactory}
|
||||||
* its metadata.
|
* and its metadata.
|
||||||
*
|
|
||||||
* @param connectionFactory the connection factory to inspect
|
* @param connectionFactory the connection factory to inspect
|
||||||
* @return the resolved {@link BindMarkersFactory}
|
* @return the resolved {@link BindMarkersFactory}
|
||||||
* @throws NoBindMarkersFactoryException if no {@link BindMarkersFactory} can be
|
* @throws NoBindMarkersFactoryException if no {@link BindMarkersFactory} can be resolved
|
||||||
* resolved
|
|
||||||
*/
|
*/
|
||||||
public static BindMarkersFactory resolve(ConnectionFactory connectionFactory) {
|
public static BindMarkersFactory resolve(ConnectionFactory connectionFactory) {
|
||||||
|
|
||||||
for (BindMarkerFactoryProvider detector : DETECTORS) {
|
for (BindMarkerFactoryProvider detector : DETECTORS) {
|
||||||
BindMarkersFactory bindMarkersFactory = detector.getBindMarkers(
|
BindMarkersFactory bindMarkersFactory = detector.getBindMarkers(connectionFactory);
|
||||||
connectionFactory);
|
|
||||||
if (bindMarkersFactory != null) {
|
if (bindMarkersFactory != null) {
|
||||||
return bindMarkersFactory;
|
return bindMarkersFactory;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
throw new NoBindMarkersFactoryException(String.format(
|
||||||
throw new NoBindMarkersFactoryException(
|
"Cannot determine a BindMarkersFactory for %s using %s",
|
||||||
String.format("Cannot determine a BindMarkersFactory for %s using %s",
|
connectionFactory.getMetadata().getName(), connectionFactory));
|
||||||
connectionFactory.getMetadata().getName(), connectionFactory));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// utility constructor.
|
|
||||||
private BindMarkersFactoryResolver() {
|
private BindMarkersFactoryResolver() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,16 +78,13 @@ public final class BindMarkersFactoryResolver {
|
||||||
public interface BindMarkerFactoryProvider {
|
public interface BindMarkerFactoryProvider {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a {@link BindMarkersFactory} for a {@link ConnectionFactory}.
|
* Return a {@link BindMarkersFactory} for a {@link ConnectionFactory}.
|
||||||
*
|
* @param connectionFactory the connection factory to be used with the {@link BindMarkersFactory}
|
||||||
* @param connectionFactory the connection factory to be used with the
|
|
||||||
* {@link BindMarkersFactory}.
|
|
||||||
* @return the {@link BindMarkersFactory} if the {@link BindMarkerFactoryProvider}
|
* @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
|
@Nullable
|
||||||
BindMarkersFactory getBindMarkers(ConnectionFactory connectionFactory);
|
BindMarkersFactory getBindMarkers(ConnectionFactory connectionFactory);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -102,18 +93,15 @@ public final class BindMarkersFactoryResolver {
|
||||||
* {@link BindMarkersFactory}.
|
* {@link BindMarkersFactory}.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public static class NoBindMarkersFactoryException
|
public static class NoBindMarkersFactoryException extends NonTransientDataAccessException {
|
||||||
extends NonTransientDataAccessException {
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for NoBindMarkersFactoryException.
|
* Constructor for NoBindMarkersFactoryException.
|
||||||
*
|
|
||||||
* @param msg the detail message
|
* @param msg the detail message
|
||||||
*/
|
*/
|
||||||
public NoBindMarkersFactoryException(String msg) {
|
public NoBindMarkersFactoryException(String msg) {
|
||||||
super(msg);
|
super(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -125,13 +113,13 @@ public final class BindMarkersFactoryResolver {
|
||||||
*/
|
*/
|
||||||
static class BuiltInBindMarkersFactoryProvider implements BindMarkerFactoryProvider {
|
static class BuiltInBindMarkersFactoryProvider implements BindMarkerFactoryProvider {
|
||||||
|
|
||||||
private static final Map<String, BindMarkersFactory> BUILTIN = new LinkedCaseInsensitiveMap<>(
|
private static final Map<String, BindMarkersFactory> BUILTIN =
|
||||||
Locale.ENGLISH);
|
new LinkedCaseInsensitiveMap<>(Locale.ENGLISH);
|
||||||
|
|
||||||
static {
|
static {
|
||||||
BUILTIN.put("H2", BindMarkersFactory.indexed("$", 1));
|
BUILTIN.put("H2", BindMarkersFactory.indexed("$", 1));
|
||||||
BUILTIN.put("Microsoft SQL Server", BindMarkersFactory.named("@", "P", 32,
|
BUILTIN.put("Microsoft SQL Server", BindMarkersFactory.named("@", "P", 32,
|
||||||
BuiltInBindMarkersFactoryProvider::filterBindMarker));
|
BuiltInBindMarkersFactoryProvider::filterBindMarker));
|
||||||
BUILTIN.put("MySQL", BindMarkersFactory.anonymous("?"));
|
BUILTIN.put("MySQL", BindMarkersFactory.anonymous("?"));
|
||||||
BUILTIN.put("MariaDB", BindMarkersFactory.anonymous("?"));
|
BUILTIN.put("MariaDB", BindMarkersFactory.anonymous("?"));
|
||||||
BUILTIN.put("PostgreSQL", BindMarkersFactory.indexed("$", 1));
|
BUILTIN.put("PostgreSQL", BindMarkersFactory.indexed("$", 1));
|
||||||
|
@ -142,40 +130,31 @@ public final class BindMarkersFactoryResolver {
|
||||||
public BindMarkersFactory getBindMarkers(ConnectionFactory connectionFactory) {
|
public BindMarkersFactory getBindMarkers(ConnectionFactory connectionFactory) {
|
||||||
ConnectionFactoryMetadata metadata = connectionFactory.getMetadata();
|
ConnectionFactoryMetadata metadata = connectionFactory.getMetadata();
|
||||||
BindMarkersFactory r2dbcDialect = BUILTIN.get(metadata.getName());
|
BindMarkersFactory r2dbcDialect = BUILTIN.get(metadata.getName());
|
||||||
|
|
||||||
if (r2dbcDialect != null) {
|
if (r2dbcDialect != null) {
|
||||||
return r2dbcDialect;
|
return r2dbcDialect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
for (String it : BUILTIN.keySet()) {
|
for (String it : BUILTIN.keySet()) {
|
||||||
if (metadata.getName().contains(it)) {
|
if (metadata.getName().contains(it)) {
|
||||||
return BUILTIN.get(it);
|
return BUILTIN.get(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String filterBindMarker(CharSequence input) {
|
private static String filterBindMarker(CharSequence input) {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder(input.length());
|
||||||
|
|
||||||
for (int i = 0; i < input.length(); i++) {
|
for (int i = 0; i < input.length(); i++) {
|
||||||
|
|
||||||
char ch = input.charAt(i);
|
char ch = input.charAt(i);
|
||||||
// ascii letter or digit
|
// ascii letter or digit
|
||||||
if (Character.isLetterOrDigit(ch) && ch < 127) {
|
if (Character.isLetterOrDigit(ch) && ch < 127) {
|
||||||
builder.append(ch);
|
builder.append(ch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (builder.length() == 0) {
|
if (builder.length() == 0) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return "_" + builder.toString();
|
return "_" + builder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,11 +55,11 @@ public class Bindings implements Iterable<Bindings.Binding> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create {@link Bindings} from a {@link Map}.
|
* Create {@link Bindings} from the given collection.
|
||||||
* @param bindings must not be {@code null}
|
* @param bindings a collection of {@link Binding} objects
|
||||||
*/
|
*/
|
||||||
public Bindings(Collection<Binding> bindings) {
|
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());
|
Map<BindMarker, Binding> mapping = CollectionUtils.newLinkedHashMap(bindings.size());
|
||||||
bindings.forEach(binding -> mapping.put(binding.getBindMarker(), binding));
|
bindings.forEach(binding -> mapping.put(binding.getBindMarker(), binding));
|
||||||
this.bindings = mapping;
|
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
|
* Merge this bindings with an other {@link Bindings} object and
|
||||||
* {@link Bindings} object.
|
* create a new merged {@link Bindings} object.
|
||||||
* @param other the object to merge with
|
* @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) {
|
public Bindings and(Bindings other) {
|
||||||
return merge(this, other);
|
return merge(this, other);
|
||||||
|
@ -116,25 +116,23 @@ public class Bindings implements Iterable<Bindings.Binding> {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new, empty {@link Bindings} object.
|
* Return an empty {@link Bindings} object.
|
||||||
* @return a new, empty {@link Bindings} object.
|
|
||||||
*/
|
*/
|
||||||
public static Bindings empty() {
|
public static Bindings empty() {
|
||||||
return EMPTY;
|
return EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merge this bindings with an other {@link Bindings} object and create a new merged
|
* Merge this bindings with an other {@link Bindings} object and
|
||||||
* {@link Bindings} object.
|
* create a new merged {@link Bindings} object.
|
||||||
* @param left the left object to merge with
|
* @param left the left object to merge with
|
||||||
* @param right the right 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) {
|
public static Bindings merge(Bindings left, Bindings right) {
|
||||||
Assert.notNull(left, "Left side Bindings must not be null");
|
Assert.notNull(left, "Left side Bindings must not be null");
|
||||||
Assert.notNull(right, "Right side Bindings must not be null");
|
Assert.notNull(right, "Right side Bindings must not be null");
|
||||||
List<Binding> result = new ArrayList<>(
|
List<Binding> result = new ArrayList<>(left.getBindings().size() + right.getBindings().size());
|
||||||
left.getBindings().size() + right.getBindings().size());
|
|
||||||
result.addAll(left.getBindings().values());
|
result.addAll(left.getBindings().values());
|
||||||
result.addAll(right.getBindings().values());
|
result.addAll(right.getBindings().values());
|
||||||
return new Bindings(result);
|
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}.
|
||||||
* @return the associated {@link BindMarker}.
|
|
||||||
*/
|
*/
|
||||||
public BindMarker getBindMarker() {
|
public BindMarker getBindMarker() {
|
||||||
return this.marker;
|
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 whether the binding has a value associated with it.
|
||||||
* @return {@code true} if there is a value present, otherwise {@code false}
|
* @return {@code true} if there is a value present,
|
||||||
* for a {@code NULL} binding.
|
* otherwise {@code false} for a {@code NULL} binding
|
||||||
*/
|
*/
|
||||||
public abstract boolean hasValue();
|
public abstract boolean hasValue();
|
||||||
|
|
||||||
|
@ -177,8 +174,8 @@ public class Bindings implements Iterable<Bindings.Binding> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the binding value.
|
* Return the binding value.
|
||||||
* @return value of this binding. Can be {@code null}
|
* @return the value of this binding
|
||||||
* if this is a {@code NULL} binding.
|
* (can be {@code null} if this is a {@code NULL} binding)
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public abstract Object getValue();
|
public abstract Object getValue();
|
||||||
|
|
|
@ -22,14 +22,15 @@ import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
|
||||||
* Index-based bind marker. This implementation creates indexed bind
|
* Index-based bind marker. This implementation creates indexed bind
|
||||||
* markers using a numeric index and an optional prefix for bind markers
|
* markers using a numeric index and an optional prefix for bind markers
|
||||||
* to be represented within the query string.
|
* to be represented within the query string.
|
||||||
|
*
|
||||||
* @author Mark Paluch
|
* @author Mark Paluch
|
||||||
* @author Jens Schauder
|
* @author Jens Schauder
|
||||||
* @since 5.3
|
* @since 5.3
|
||||||
*/
|
*/
|
||||||
class IndexedBindMarkers implements BindMarkers {
|
class IndexedBindMarkers implements BindMarkers {
|
||||||
|
|
||||||
private static final AtomicIntegerFieldUpdater<IndexedBindMarkers> COUNTER_INCREMENTER = AtomicIntegerFieldUpdater
|
private static final AtomicIntegerFieldUpdater<IndexedBindMarkers> COUNTER_INCREMENTER =
|
||||||
.newUpdater(IndexedBindMarkers.class, "counter");
|
AtomicIntegerFieldUpdater.newUpdater(IndexedBindMarkers.class, "counter");
|
||||||
|
|
||||||
|
|
||||||
private final int offset;
|
private final int offset;
|
||||||
|
@ -59,6 +60,7 @@ class IndexedBindMarkers implements BindMarkers {
|
||||||
return new IndexedBindMarker(this.prefix + "" + (index + this.offset), index);
|
return new IndexedBindMarker(this.prefix + "" + (index + this.offset), index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A single indexed bind marker.
|
* A single indexed bind marker.
|
||||||
* @author Mark Paluch
|
* @author Mark Paluch
|
||||||
|
@ -69,13 +71,11 @@ class IndexedBindMarkers implements BindMarkers {
|
||||||
|
|
||||||
private final int index;
|
private final int index;
|
||||||
|
|
||||||
|
|
||||||
IndexedBindMarker(String placeholder, int index) {
|
IndexedBindMarker(String placeholder, int index) {
|
||||||
this.placeholder = placeholder;
|
this.placeholder = placeholder;
|
||||||
this.index = index;
|
this.index = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getPlaceholder() {
|
public String getPlaceholder() {
|
||||||
return this.placeholder;
|
return this.placeholder;
|
||||||
|
@ -94,7 +94,6 @@ class IndexedBindMarkers implements BindMarkers {
|
||||||
public int getIndex() {
|
public int getIndex() {
|
||||||
return this.index;
|
return this.index;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ public class MutableBindings extends Bindings {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create new {@link MutableBindings}.
|
* Create new {@link MutableBindings}.
|
||||||
* @param markers must not be {@code null}.
|
* @param markers the {@link BindMarkers} to wrap
|
||||||
*/
|
*/
|
||||||
public MutableBindings(BindMarkers markers) {
|
public MutableBindings(BindMarkers markers) {
|
||||||
super(new LinkedHashMap<>());
|
super(new LinkedHashMap<>());
|
||||||
|
@ -66,8 +66,8 @@ public class MutableBindings extends Bindings {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bind a value to {@link BindMarker}.
|
* Bind a value to {@link BindMarker}.
|
||||||
* @param marker must not be {@code null}
|
* @param marker the {@link BindMarker} to bind to
|
||||||
* @param value must not be {@code null}
|
* @param value the value to bind
|
||||||
*/
|
*/
|
||||||
public MutableBindings bind(BindMarker marker, Object value) {
|
public MutableBindings bind(BindMarker marker, Object value) {
|
||||||
Assert.notNull(marker, "BindMarker must not be null");
|
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}.
|
* Bind a value and return the related {@link BindMarker}.
|
||||||
* Increments {@link BindMarkers} state.
|
* Increments {@link BindMarkers} state.
|
||||||
* @param value must not be {@code null}
|
* @param value the value to bind
|
||||||
*/
|
*/
|
||||||
public BindMarker bind(Object value) {
|
public BindMarker bind(Object value) {
|
||||||
Assert.notNull(value, "Value must not be null");
|
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}.
|
* Bind a {@code NULL} value to {@link BindMarker}.
|
||||||
* @param marker must not be {@code null}
|
* @param marker the {@link BindMarker} to bind to
|
||||||
* @param valueType must not be {@code null}
|
* @param valueType the value type
|
||||||
*/
|
*/
|
||||||
public MutableBindings bindNull(BindMarker marker, Class<?> valueType) {
|
public MutableBindings bindNull(BindMarker marker, Class<?> valueType) {
|
||||||
Assert.notNull(marker, "BindMarker must not be null");
|
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}.
|
* Bind a {@code NULL} value and return the related {@link BindMarker}.
|
||||||
* Increments {@link BindMarkers} state.
|
* Increments {@link BindMarkers} state.
|
||||||
* @param valueType must not be {@code null}
|
* @param valueType the value type
|
||||||
*/
|
*/
|
||||||
public BindMarker bindNull(Class<?> valueType) {
|
public BindMarker bindNull(Class<?> valueType) {
|
||||||
Assert.notNull(valueType, "Value type must not be null");
|
Assert.notNull(valueType, "Value type must not be null");
|
||||||
|
|
|
@ -29,8 +29,8 @@ import org.springframework.util.Assert;
|
||||||
*/
|
*/
|
||||||
class NamedBindMarkers implements BindMarkers {
|
class NamedBindMarkers implements BindMarkers {
|
||||||
|
|
||||||
private static final AtomicIntegerFieldUpdater<NamedBindMarkers> COUNTER_INCREMENTER = AtomicIntegerFieldUpdater
|
private static final AtomicIntegerFieldUpdater<NamedBindMarkers> COUNTER_INCREMENTER =
|
||||||
.newUpdater(NamedBindMarkers.class, "counter");
|
AtomicIntegerFieldUpdater.newUpdater(NamedBindMarkers.class, "counter");
|
||||||
|
|
||||||
|
|
||||||
private final String prefix;
|
private final String prefix;
|
||||||
|
@ -88,7 +88,6 @@ class NamedBindMarkers implements BindMarkers {
|
||||||
private final String identifier;
|
private final String identifier;
|
||||||
|
|
||||||
NamedBindMarker(String placeholder, String identifier) {
|
NamedBindMarker(String placeholder, String identifier) {
|
||||||
|
|
||||||
this.placeholder = placeholder;
|
this.placeholder = placeholder;
|
||||||
this.identifier = identifier;
|
this.identifier = identifier;
|
||||||
}
|
}
|
||||||
|
@ -107,7 +106,6 @@ class NamedBindMarkers implements BindMarkers {
|
||||||
public void bindNull(BindTarget target, Class<?> valueType) {
|
public void bindNull(BindTarget target, Class<?> valueType) {
|
||||||
target.bindNull(this.identifier, valueType);
|
target.bindNull(this.identifier, valueType);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue