Nullability refinements and related polishing
This commit is contained in:
parent
56c661829b
commit
d0209e5f1f
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* 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.
|
||||
|
@ -39,6 +39,7 @@ import org.springframework.lang.Nullable;
|
|||
* Particularly appropriate for waiting on a slowly starting Oracle database.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Marten Deinum
|
||||
* @since 18.12.2003
|
||||
*/
|
||||
public class DatabaseStartupValidator implements InitializingBean {
|
||||
|
@ -59,12 +60,7 @@ public class DatabaseStartupValidator implements InitializingBean {
|
|||
@Nullable
|
||||
private DataSource dataSource;
|
||||
|
||||
/**
|
||||
* The query used to validate the connection
|
||||
* @deprecated in favor of JDBC 4.0 connection validation
|
||||
*/
|
||||
@Nullable
|
||||
@Deprecated
|
||||
private String validationQuery;
|
||||
|
||||
private int interval = DEFAULT_INTERVAL;
|
||||
|
@ -81,8 +77,7 @@ public class DatabaseStartupValidator implements InitializingBean {
|
|||
|
||||
/**
|
||||
* Set the SQL query string to use for validation.
|
||||
*
|
||||
* @deprecated in favor of the JDBC 4.0 connection validation
|
||||
* @deprecated as of 5.3, in favor of the JDBC 4.0 connection validation
|
||||
*/
|
||||
@Deprecated
|
||||
public void setValidationQuery(String validationQuery) {
|
||||
|
@ -148,10 +143,9 @@ public class DatabaseStartupValidator implements InitializingBean {
|
|||
logger.debug("Validation query [" + this.validationQuery + "] threw exception", ex);
|
||||
}
|
||||
else {
|
||||
logger.debug(" Validation threw exception", ex);
|
||||
logger.debug("Validation check threw exception", ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (logger.isInfoEnabled()) {
|
||||
float rest = ((float) (deadLine - System.currentTimeMillis())) / 1000;
|
||||
if (rest > this.interval) {
|
||||
|
|
|
@ -80,7 +80,6 @@ public abstract class ConnectionFactoryUtils {
|
|||
* Translates exceptions into the Spring hierarchy of unchecked generic
|
||||
* data access exceptions, simplifying calling code and making any
|
||||
* exception that is thrown more meaningful.
|
||||
*
|
||||
* <p>Is aware of a corresponding Connection bound to the current
|
||||
* {@link TransactionSynchronizationManager}. Will bind a Connection to the
|
||||
* {@link TransactionSynchronizationManager} if transaction synchronization is active.
|
||||
|
@ -99,7 +98,6 @@ public abstract class ConnectionFactoryUtils {
|
|||
/**
|
||||
* Actually obtain a R2DBC Connection from the given {@link ConnectionFactory}.
|
||||
* Same as {@link #getConnection}, but preserving the original exceptions.
|
||||
*
|
||||
* <p>Is aware of a corresponding Connection bound to the current
|
||||
* {@link TransactionSynchronizationManager}. Will bind a Connection to the
|
||||
* {@link TransactionSynchronizationManager} if transaction synchronization is active
|
||||
|
@ -193,11 +191,9 @@ public abstract class ConnectionFactoryUtils {
|
|||
* @param connectionFactory the {@link ConnectionFactory} that the Connection was obtained from
|
||||
* @see #doGetConnection
|
||||
*/
|
||||
public static Mono<Void> doReleaseConnection(Connection connection,
|
||||
ConnectionFactory connectionFactory) {
|
||||
public static Mono<Void> doReleaseConnection(Connection connection, ConnectionFactory connectionFactory) {
|
||||
return TransactionSynchronizationManager.forCurrentTransaction()
|
||||
.flatMap(synchronizationManager -> {
|
||||
|
||||
ConnectionHolder conHolder = (ConnectionHolder) synchronizationManager.getResource(connectionFactory);
|
||||
if (conHolder != null && connectionEquals(conHolder, connection)) {
|
||||
// It's the transactional Connection: Don't close it.
|
||||
|
@ -235,7 +231,6 @@ public abstract class ConnectionFactoryUtils {
|
|||
* @return the corresponding DataAccessException instance
|
||||
*/
|
||||
public static DataAccessException convertR2dbcException(String task, @Nullable String sql, R2dbcException ex) {
|
||||
|
||||
if (ex instanceof R2dbcTransientException) {
|
||||
if (ex instanceof R2dbcTransientResourceException) {
|
||||
return new TransientDataAccessResourceException(buildMessage(task, sql, ex), ex);
|
||||
|
@ -247,7 +242,6 @@ public abstract class ConnectionFactoryUtils {
|
|||
return new QueryTimeoutException(buildMessage(task, sql, ex), ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (ex instanceof R2dbcNonTransientException) {
|
||||
if (ex instanceof R2dbcNonTransientResourceException) {
|
||||
return new DataAccessResourceFailureException(buildMessage(task, sql, ex), ex);
|
||||
|
@ -262,7 +256,6 @@ public abstract class ConnectionFactoryUtils {
|
|||
return new BadSqlGrammarException(task, (sql != null ? sql : ""), ex);
|
||||
}
|
||||
}
|
||||
|
||||
return new UncategorizedR2dbcException(buildMessage(task, sql, ex), sql, ex);
|
||||
}
|
||||
|
||||
|
@ -326,7 +319,6 @@ public abstract class ConnectionFactoryUtils {
|
|||
* @see #CONNECTION_SYNCHRONIZATION_ORDER
|
||||
*/
|
||||
private static int getConnectionSynchronizationOrder(ConnectionFactory connectionFactory) {
|
||||
|
||||
int order = CONNECTION_SYNCHRONIZATION_ORDER;
|
||||
ConnectionFactory current = connectionFactory;
|
||||
while (current instanceof DelegatingConnectionFactory) {
|
||||
|
@ -336,6 +328,7 @@ public abstract class ConnectionFactoryUtils {
|
|||
return order;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Callback for resource cleanup at the end of a non-native R2DBC transaction.
|
||||
*/
|
||||
|
@ -355,7 +348,6 @@ public abstract class ConnectionFactoryUtils {
|
|||
this.order = getConnectionSynchronizationOrder(connectionFactory);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
|
@ -387,7 +379,8 @@ public abstract class ConnectionFactoryUtils {
|
|||
public Mono<Void> resume() {
|
||||
if (this.holderActive) {
|
||||
return TransactionSynchronizationManager.forCurrentTransaction()
|
||||
.doOnNext(synchronizationManager -> synchronizationManager.bindResource(this.connectionFactory, this.connectionHolder))
|
||||
.doOnNext(synchronizationManager ->
|
||||
synchronizationManager.bindResource(this.connectionFactory, this.connectionHolder))
|
||||
.then();
|
||||
}
|
||||
return Mono.empty();
|
||||
|
@ -401,8 +394,7 @@ public abstract class ConnectionFactoryUtils {
|
|||
// to avoid issues with strict transaction implementations that expect
|
||||
// the close call before transaction completion.
|
||||
if (!this.connectionHolder.isOpen()) {
|
||||
return TransactionSynchronizationManager.forCurrentTransaction()
|
||||
.flatMap(synchronizationManager -> {
|
||||
return TransactionSynchronizationManager.forCurrentTransaction().flatMap(synchronizationManager -> {
|
||||
synchronizationManager.unbindResource(this.connectionFactory);
|
||||
this.holderActive = false;
|
||||
if (this.connectionHolder.hasConnection()) {
|
||||
|
@ -439,4 +431,5 @@ public abstract class ConnectionFactoryUtils {
|
|||
return Mono.empty();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -65,7 +65,6 @@ public class ConnectionHolder extends ResourceHolderSupport {
|
|||
* in an ongoing transaction
|
||||
*/
|
||||
public ConnectionHolder(Connection connection, boolean transactionActive) {
|
||||
|
||||
this.currentConnection = connection;
|
||||
this.transactionActive = transactionActive;
|
||||
}
|
||||
|
@ -80,7 +79,6 @@ public class ConnectionHolder extends ResourceHolderSupport {
|
|||
|
||||
/**
|
||||
* Set whether this holder represents an active, R2DBC-managed transaction.
|
||||
*
|
||||
* @see R2dbcTransactionManager
|
||||
*/
|
||||
protected void setTransactionActive(boolean transactionActive) {
|
||||
|
@ -111,7 +109,6 @@ public class ConnectionHolder extends ResourceHolderSupport {
|
|||
* @see #released()
|
||||
*/
|
||||
public Connection getConnection() {
|
||||
|
||||
Assert.notNull(this.currentConnection, "Active Connection is required");
|
||||
return this.currentConnection;
|
||||
}
|
||||
|
|
|
@ -81,22 +81,21 @@ import org.springframework.util.Assert;
|
|||
@SuppressWarnings("serial")
|
||||
public class R2dbcTransactionManager extends AbstractReactiveTransactionManager implements InitializingBean {
|
||||
|
||||
@Nullable
|
||||
private ConnectionFactory connectionFactory;
|
||||
|
||||
private boolean enforceReadOnly = false;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new @link ConnectionFactoryTransactionManager} instance. A ConnectionFactory has to be set to be able to
|
||||
* use it.
|
||||
*
|
||||
* Create a new @link ConnectionFactoryTransactionManager} instance.
|
||||
* A ConnectionFactory has to be set to be able to use it.
|
||||
* @see #setConnectionFactory
|
||||
*/
|
||||
public R2dbcTransactionManager() {}
|
||||
|
||||
/**
|
||||
* Create a new {@link R2dbcTransactionManager} instance.
|
||||
*
|
||||
* @param connectionFactory the R2DBC ConnectionFactory to manage transactions for
|
||||
*/
|
||||
public R2dbcTransactionManager(ConnectionFactory connectionFactory) {
|
||||
|
@ -108,19 +107,10 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
|
|||
|
||||
/**
|
||||
* Set the R2DBC {@link ConnectionFactory} that this instance should manage transactions for.
|
||||
* <p>
|
||||
* This will typically be a locally defined {@link ConnectionFactory}, for example an connection pool.
|
||||
* <p>
|
||||
* The {@link ConnectionFactory} specified here should be the target {@link ConnectionFactory} to manage transactions
|
||||
* for, not a TransactionAwareConnectionFactoryProxy. Only data access code may work with
|
||||
* TransactionAwareConnectionFactoryProxy, while the transaction manager needs to work on the underlying target
|
||||
* {@link ConnectionFactory}. If there's nevertheless a TransactionAwareConnectionFactoryProxy passed in, it will be
|
||||
* unwrapped to extract its target {@link ConnectionFactory}.
|
||||
* <p>
|
||||
* <b>The {@link ConnectionFactory} passed in here needs to return independent {@link Connection}s.</b> The
|
||||
* {@link Connection}s may come from a pool (the typical case), but the {@link ConnectionFactory} must not return
|
||||
* scoped {@link Connection} or the like.
|
||||
*
|
||||
* <p>This will typically be a locally defined {@link ConnectionFactory}, for example an connection pool.
|
||||
* <p><b>The {@link ConnectionFactory} passed in here needs to return independent {@link Connection}s.</b>
|
||||
* The {@link Connection}s may come from a pool (the typical case), but the {@link ConnectionFactory}
|
||||
* must not return scoped {@link Connection}s or the like.
|
||||
* @see TransactionAwareConnectionFactoryProxy
|
||||
*/
|
||||
public void setConnectionFactory(@Nullable ConnectionFactory connectionFactory) {
|
||||
|
@ -137,7 +127,6 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
|
|||
|
||||
/**
|
||||
* Obtain the {@link ConnectionFactory} for actual use.
|
||||
*
|
||||
* @return the {@link ConnectionFactory} (never {@code null})
|
||||
* @throws IllegalStateException in case of no ConnectionFactory set
|
||||
*/
|
||||
|
@ -149,12 +138,11 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
|
|||
|
||||
/**
|
||||
* Specify whether to enforce the read-only nature of a transaction (as indicated by
|
||||
* {@link TransactionDefinition#isReadOnly()} through an explicit statement on the transactional connection: "SET
|
||||
* TRANSACTION READ ONLY" as understood by Oracle, MySQL and Postgres.
|
||||
* <p>
|
||||
* The exact treatment, including any SQL statement executed on the connection, can be customized through through
|
||||
* {@link #prepareTransactionalConnection}.
|
||||
*
|
||||
* {@link TransactionDefinition#isReadOnly()} through an explicit statement on the
|
||||
* transactional connection: "SET TRANSACTION READ ONLY" as understood by Oracle,
|
||||
* MySQL and Postgres.
|
||||
* <p>The exact treatment, including any SQL statement executed on the connection,
|
||||
* can be customized through through {@link #prepareTransactionalConnection}.
|
||||
* @see #prepareTransactionalConnection
|
||||
*/
|
||||
public void setEnforceReadOnly(boolean enforceReadOnly) {
|
||||
|
@ -162,9 +150,8 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
|
|||
}
|
||||
|
||||
/**
|
||||
* Return whether to enforce the read-only nature of a transaction through an explicit statement on the transactional
|
||||
* connection.
|
||||
*
|
||||
* Return whether to enforce the read-only nature of a transaction through an
|
||||
* explicit statement on the transactional connection.
|
||||
* @see #setEnforceReadOnly
|
||||
*/
|
||||
public boolean isEnforceReadOnly() {
|
||||
|
@ -179,8 +166,7 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Object doGetTransaction(TransactionSynchronizationManager synchronizationManager)
|
||||
throws TransactionException {
|
||||
protected Object doGetTransaction(TransactionSynchronizationManager synchronizationManager) throws TransactionException {
|
||||
ConnectionFactoryTransactionObject txObject = new ConnectionFactoryTransactionObject();
|
||||
ConnectionHolder conHolder = (ConnectionHolder) synchronizationManager.getResource(obtainConnectionFactory());
|
||||
txObject.setConnectionHolder(conHolder, false);
|
||||
|
@ -196,17 +182,15 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
|
|||
@Override
|
||||
protected Mono<Void> doBegin(TransactionSynchronizationManager synchronizationManager, Object transaction,
|
||||
TransactionDefinition definition) throws TransactionException {
|
||||
|
||||
ConnectionFactoryTransactionObject txObject = (ConnectionFactoryTransactionObject) transaction;
|
||||
|
||||
return Mono.defer(() -> {
|
||||
|
||||
Mono<Connection> connectionMono;
|
||||
|
||||
if (!txObject.hasConnectionHolder() || txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
|
||||
Mono<Connection> newCon = Mono.from(obtainConnectionFactory().create());
|
||||
|
||||
connectionMono = newCon.doOnNext(connection -> {
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Acquired Connection [" + newCon + "] for R2DBC transaction");
|
||||
}
|
||||
|
@ -219,22 +203,18 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
|
|||
}
|
||||
|
||||
return connectionMono.flatMap(con -> {
|
||||
|
||||
return prepareTransactionalConnection(con, definition, transaction).then(Mono.from(con.beginTransaction()))
|
||||
.doOnSuccess(v -> {
|
||||
txObject.getConnectionHolder().setTransactionActive(true);
|
||||
|
||||
Duration timeout = determineTimeout(definition);
|
||||
if (!timeout.isNegative() && !timeout.isZero()) {
|
||||
txObject.getConnectionHolder().setTimeoutInMillis(timeout.toMillis());
|
||||
}
|
||||
|
||||
// Bind the connection holder to the thread.
|
||||
if (txObject.isNewConnectionHolder()) {
|
||||
synchronizationManager.bindResource(obtainConnectionFactory(), txObject.getConnectionHolder());
|
||||
}
|
||||
}).thenReturn(con).onErrorResume(e -> {
|
||||
|
||||
if (txObject.isNewConnectionHolder()) {
|
||||
return ConnectionFactoryUtils.releaseConnection(con, obtainConnectionFactory())
|
||||
.doOnTerminate(() -> txObject.setConnectionHolder(null, false))
|
||||
|
@ -243,20 +223,17 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
|
|||
return Mono.error(e);
|
||||
});
|
||||
}).onErrorResume(e -> {
|
||||
|
||||
CannotCreateTransactionException ex = new CannotCreateTransactionException(
|
||||
"Could not open R2DBC Connection for transaction",
|
||||
e);
|
||||
|
||||
"Could not open R2DBC Connection for transaction", e);
|
||||
return Mono.error(ex);
|
||||
});
|
||||
}).then();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the actual timeout to use for the given definition. Will fall back to this manager's default timeout if
|
||||
* the transaction definition doesn't specify a non-default value.
|
||||
*
|
||||
* Determine the actual timeout to use for the given definition.
|
||||
* Will fall back to this manager's default timeout if the
|
||||
* transaction definition doesn't specify a non-default value.
|
||||
* @param definition the transaction definition
|
||||
* @return the actual timeout to use
|
||||
* @see org.springframework.transaction.TransactionDefinition#getTimeout()
|
||||
|
@ -280,14 +257,11 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Mono<Void> doResume(TransactionSynchronizationManager synchronizationManager, Object transaction,
|
||||
Object suspendedResources) throws TransactionException {
|
||||
protected Mono<Void> doResume(TransactionSynchronizationManager synchronizationManager,
|
||||
@Nullable Object transaction, Object suspendedResources) throws TransactionException {
|
||||
|
||||
return Mono.defer(() -> {
|
||||
ConnectionFactoryTransactionObject txObject = (ConnectionFactoryTransactionObject) transaction;
|
||||
txObject.setConnectionHolder(null);
|
||||
synchronizationManager.bindResource(obtainConnectionFactory(), suspendedResources);
|
||||
|
||||
return Mono.empty();
|
||||
});
|
||||
}
|
||||
|
@ -301,7 +275,6 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
|
|||
if (status.isDebug()) {
|
||||
logger.debug("Committing R2DBC transaction on Connection [" + connection + "]");
|
||||
}
|
||||
|
||||
return Mono.from(connection.commitTransaction())
|
||||
.onErrorMap(R2dbcException.class, ex -> translateException("R2DBC commit", ex));
|
||||
}
|
||||
|
@ -315,7 +288,6 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
|
|||
if (status.isDebug()) {
|
||||
logger.debug("Rolling back R2DBC transaction on Connection [" + connection + "]");
|
||||
}
|
||||
|
||||
return Mono.from(connection.rollbackTransaction())
|
||||
.onErrorMap(R2dbcException.class, ex -> translateException("R2DBC rollback", ex));
|
||||
}
|
||||
|
@ -326,10 +298,9 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
|
|||
|
||||
return Mono.fromRunnable(() -> {
|
||||
ConnectionFactoryTransactionObject txObject = (ConnectionFactoryTransactionObject) status.getTransaction();
|
||||
|
||||
if (status.isDebug()) {
|
||||
logger
|
||||
.debug("Setting R2DBC transaction [" + txObject.getConnectionHolder().getConnection() + "] rollback-only");
|
||||
logger.debug("Setting R2DBC transaction [" + txObject.getConnectionHolder().getConnection() +
|
||||
"] rollback-only");
|
||||
}
|
||||
txObject.setRollbackOnly();
|
||||
});
|
||||
|
@ -380,20 +351,19 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
|
|||
|
||||
/**
|
||||
* Prepare the transactional {@link Connection} right after transaction begin.
|
||||
* <p>
|
||||
* The default implementation executes a "SET TRANSACTION READ ONLY" statement if the {@link #setEnforceReadOnly
|
||||
* "enforceReadOnly"} flag is set to {@code true} and the transaction definition indicates a read-only transaction.
|
||||
* <p>
|
||||
* The "SET TRANSACTION READ ONLY" is understood by Oracle, MySQL and Postgres and may work with other databases as
|
||||
* well. If you'd like to adapt this treatment, override this method accordingly.
|
||||
*
|
||||
* <p>The default implementation executes a "SET TRANSACTION READ ONLY" statement if the
|
||||
* {@link #setEnforceReadOnly "enforceReadOnly"} flag is set to {@code true} and the
|
||||
* transaction definition indicates a read-only transaction.
|
||||
* <p>The "SET TRANSACTION READ ONLY" is understood by Oracle, MySQL and Postgres
|
||||
* and may work with other databases as well. If you'd like to adapt this treatment,
|
||||
* override this method accordingly.
|
||||
* @param con the transactional R2DBC Connection
|
||||
* @param definition the current transaction definition
|
||||
* @param transaction the transaction object
|
||||
* @see #setEnforceReadOnly
|
||||
*/
|
||||
protected Mono<Void> prepareTransactionalConnection(Connection con, TransactionDefinition definition,
|
||||
Object transaction) {
|
||||
protected Mono<Void> prepareTransactionalConnection(
|
||||
Connection con, TransactionDefinition definition, Object transaction) {
|
||||
|
||||
ConnectionFactoryTransactionObject txObject = (ConnectionFactoryTransactionObject) transaction;
|
||||
|
||||
|
@ -410,8 +380,7 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
|
|||
if (isolationLevelToUse != null && definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger
|
||||
.debug("Changing isolation level of R2DBC Connection [" + con + "] to " + isolationLevelToUse.asSql());
|
||||
logger.debug("Changing isolation level of R2DBC Connection [" + con + "] to " + isolationLevelToUse.asSql());
|
||||
}
|
||||
IsolationLevel currentIsolation = con.getTransactionIsolationLevel();
|
||||
if (!currentIsolation.asSql().equalsIgnoreCase(isolationLevelToUse.asSql())) {
|
||||
|
@ -439,10 +408,9 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
|
|||
* Resolve the {@link TransactionDefinition#getIsolationLevel() isolation level constant} to a R2DBC
|
||||
* {@link IsolationLevel}. If you'd like to extend isolation level translation for vendor-specific
|
||||
* {@link IsolationLevel}s, override this method accordingly.
|
||||
*
|
||||
* @param isolationLevel the isolation level to translate.
|
||||
* @return the resolved isolation level. Can be {@code null} if not resolvable or the isolation level should remain
|
||||
* {@link TransactionDefinition#ISOLATION_DEFAULT default}.
|
||||
* @return the resolved isolation level. Can be {@code null} if not resolvable or the isolation level
|
||||
* should remain {@link TransactionDefinition#ISOLATION_DEFAULT default}.
|
||||
* @see TransactionDefinition#getIsolationLevel()
|
||||
*/
|
||||
@Nullable
|
||||
|
@ -461,9 +429,8 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
|
|||
}
|
||||
|
||||
/**
|
||||
* Translate the given R2DBC commit/rollback exception to a common Spring exception to propagate from the
|
||||
* {@link #commit}/{@link #rollback} call.
|
||||
*
|
||||
* Translate the given R2DBC commit/rollback exception to a common Spring exception to propagate
|
||||
* from the {@link #commit}/{@link #rollback} call.
|
||||
* @param task the task description (commit or rollback).
|
||||
* @param ex the SQLException thrown from commit/rollback.
|
||||
* @return the translated exception to emit
|
||||
|
@ -474,8 +441,8 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
|
|||
|
||||
|
||||
/**
|
||||
* ConnectionFactory transaction object, representing a ConnectionHolder. Used as transaction object by
|
||||
* ConnectionFactoryTransactionManager.
|
||||
* ConnectionFactory transaction object, representing a ConnectionHolder.
|
||||
* Used as transaction object by R2dbcTransactionManager.
|
||||
*/
|
||||
private static class ConnectionFactoryTransactionObject {
|
||||
|
||||
|
@ -489,7 +456,6 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
|
|||
|
||||
private boolean mustRestoreAutoCommit;
|
||||
|
||||
|
||||
void setConnectionHolder(@Nullable ConnectionHolder connectionHolder, boolean newConnectionHolder) {
|
||||
setConnectionHolder(connectionHolder);
|
||||
this.newConnectionHolder = newConnectionHolder;
|
||||
|
@ -535,4 +501,3 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -91,10 +91,9 @@ public class SingleConnectionFactory extends DelegatingConnectionFactory
|
|||
|
||||
/**
|
||||
* Create a new {@link SingleConnectionFactory} using a R2DBC connection URL.
|
||||
*
|
||||
* @param url the R2DBC URL to use for accessing {@link ConnectionFactory} discovery.
|
||||
* @param suppressClose if the returned {@link Connection} should be a close-suppressing proxy or the physical
|
||||
* {@link Connection}.
|
||||
* @param suppressClose if the returned {@link Connection} should be a close-suppressing proxy
|
||||
* or the physical {@link Connection}.
|
||||
* @see ConnectionFactories#get(String)
|
||||
*/
|
||||
public SingleConnectionFactory(String url, boolean suppressClose) {
|
||||
|
@ -106,21 +105,18 @@ public class SingleConnectionFactory extends DelegatingConnectionFactory
|
|||
/**
|
||||
* Create a new {@link SingleConnectionFactory} with a given {@link Connection} and
|
||||
* {@link ConnectionFactoryMetadata}.
|
||||
*
|
||||
* @param target underlying target {@link Connection}.
|
||||
* @param metadata {@link ConnectionFactory} metadata to be associated with this {@link ConnectionFactory}.
|
||||
* @param suppressClose if the {@link Connection} should be wrapped with a {@link Connection} that suppresses
|
||||
* {@code close()} calls (to allow for normal {@code close()} usage in applications that expect a pooled
|
||||
* {@link Connection} but do not know our {@link SmartConnectionFactory} interface).
|
||||
* @code close()} calls (to allow for normal {@code close()} usage in applications that expect a pooled
|
||||
* @link Connection}).
|
||||
*/
|
||||
public SingleConnectionFactory(Connection target, ConnectionFactoryMetadata metadata,
|
||||
boolean suppressClose) {
|
||||
public SingleConnectionFactory(Connection target, ConnectionFactoryMetadata metadata, boolean suppressClose) {
|
||||
super(new ConnectionFactory() {
|
||||
@Override
|
||||
public Publisher<? extends Connection> create() {
|
||||
return Mono.just(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConnectionFactoryMetadata getMetadata() {
|
||||
return metadata;
|
||||
|
@ -136,15 +132,16 @@ public class SingleConnectionFactory extends DelegatingConnectionFactory
|
|||
|
||||
|
||||
/**
|
||||
* Set whether the returned {@link Connection} should be a close-suppressing proxy or the physical {@link Connection}.
|
||||
* Set whether the returned {@link Connection} should be a close-suppressing proxy
|
||||
* or the physical {@link Connection}.
|
||||
*/
|
||||
public void setSuppressClose(boolean suppressClose) {
|
||||
this.suppressClose = suppressClose;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the returned {@link Connection} will be a close-suppressing proxy or the physical
|
||||
* {@link Connection}.
|
||||
* Return whether the returned {@link Connection} will be a close-suppressing proxy
|
||||
* or the physical {@link Connection}.
|
||||
*/
|
||||
protected boolean isSuppressClose() {
|
||||
return this.suppressClose;
|
||||
|
@ -159,7 +156,6 @@ public class SingleConnectionFactory extends DelegatingConnectionFactory
|
|||
|
||||
/**
|
||||
* Return whether the returned {@link Connection}'s "autoCommit" setting should be overridden.
|
||||
*
|
||||
* @return the "autoCommit" value, or {@code null} if none to be applied
|
||||
*/
|
||||
@Nullable
|
||||
|
@ -167,28 +163,25 @@ public class SingleConnectionFactory extends DelegatingConnectionFactory
|
|||
return this.autoCommit;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Mono<? extends Connection> create() {
|
||||
|
||||
Connection connection = this.target.get();
|
||||
|
||||
return this.connectionEmitter.map(connectionToUse -> {
|
||||
|
||||
if (connection == null) {
|
||||
this.target.compareAndSet(connection, connectionToUse);
|
||||
this.connection = (isSuppressClose() ? getCloseSuppressingConnectionProxy(connectionToUse) : connectionToUse);
|
||||
this.target.compareAndSet(null, connectionToUse);
|
||||
this.connection =
|
||||
(isSuppressClose() ? getCloseSuppressingConnectionProxy(connectionToUse) : connectionToUse);
|
||||
}
|
||||
|
||||
return this.connection;
|
||||
}).flatMap(this::prepareConnection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the underlying {@link Connection}. The provider of this {@link ConnectionFactory} needs to care for proper
|
||||
* shutdown.
|
||||
* <p>
|
||||
* As this bean implements {@link DisposableBean}, a bean factory will automatically invoke this on destruction of its
|
||||
* cached singletons.
|
||||
* Close the underlying {@link Connection}.
|
||||
* The provider of this {@link ConnectionFactory} needs to care for proper shutdown.
|
||||
* <p>As this bean implements {@link DisposableBean}, a bean factory will automatically
|
||||
* invoke this on destruction of its cached singletons.
|
||||
*/
|
||||
@Override
|
||||
public void destroy() {
|
||||
|
@ -199,46 +192,36 @@ public class SingleConnectionFactory extends DelegatingConnectionFactory
|
|||
* Reset the underlying shared Connection, to be reinitialized on next access.
|
||||
*/
|
||||
public Mono<Void> resetConnection() {
|
||||
|
||||
Connection connection = this.target.get();
|
||||
|
||||
if (connection == null) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
return Mono.defer(() -> {
|
||||
|
||||
if (this.target.compareAndSet(connection, null)) {
|
||||
|
||||
this.connection = null;
|
||||
|
||||
return Mono.from(connection.close());
|
||||
}
|
||||
|
||||
return Mono.empty();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the {@link Connection} before using it. Applies {@link #getAutoCommitValue() auto-commit} settings if
|
||||
* configured.
|
||||
*
|
||||
* Prepare the {@link Connection} before using it.
|
||||
* Applies {@link #getAutoCommitValue() auto-commit} settings if configured.
|
||||
* @param connection the requested {@link Connection}.
|
||||
* @return the prepared {@link Connection}.
|
||||
*/
|
||||
protected Mono<Connection> prepareConnection(Connection connection) {
|
||||
|
||||
Boolean autoCommit = getAutoCommitValue();
|
||||
if (autoCommit != null) {
|
||||
return Mono.from(connection.setAutoCommit(autoCommit)).thenReturn(connection);
|
||||
}
|
||||
|
||||
return Mono.just(connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap the given {@link Connection} with a proxy that delegates every method call to it but suppresses close calls.
|
||||
*
|
||||
* Wrap the given {@link Connection} with a proxy that delegates every method call to it
|
||||
* but suppresses close calls.
|
||||
* @param target the original {@link Connection} to wrap.
|
||||
* @return the wrapped Connection.
|
||||
*/
|
||||
|
@ -264,22 +247,18 @@ public class SingleConnectionFactory extends DelegatingConnectionFactory
|
|||
@Override
|
||||
@Nullable
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
// Invocation on ConnectionProxy interface coming in...
|
||||
|
||||
if (method.getName().equals("equals")) {
|
||||
// Only consider equal when proxies are identical.
|
||||
return proxy == args[0];
|
||||
}
|
||||
else if (method.getName().equals("hashCode")) {
|
||||
// Use hashCode of PersistenceManager proxy.
|
||||
return System.identityHashCode(proxy);
|
||||
}
|
||||
else if (method.getName().equals("unwrap")) {
|
||||
return this.target;
|
||||
}
|
||||
else if (method.getName().equals("close")) {
|
||||
// Handle close method: suppress, not valid.
|
||||
return Mono.empty();
|
||||
switch (method.getName()) {
|
||||
case "equals":
|
||||
// Only consider equal when proxies are identical.
|
||||
return proxy == args[0];
|
||||
case "hashCode":
|
||||
// Use hashCode of PersistenceManager proxy.
|
||||
return System.identityHashCode(proxy);
|
||||
case "unwrap":
|
||||
return this.target;
|
||||
case "close":
|
||||
// Handle close method: suppress, not valid.
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
// Invoke method on target Connection.
|
||||
|
@ -290,7 +269,6 @@ public class SingleConnectionFactory extends DelegatingConnectionFactory
|
|||
throw ex.getTargetException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -141,23 +141,18 @@ public class TransactionAwareConnectionFactoryProxy extends DelegatingConnection
|
|||
@Nullable
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
if (ReflectionUtils.isObjectMethod(method)) {
|
||||
|
||||
if (ReflectionUtils.isToStringMethod(method)) {
|
||||
return proxyToString(proxy);
|
||||
}
|
||||
|
||||
if (ReflectionUtils.isEqualsMethod(method)) {
|
||||
return (proxy == args[0]);
|
||||
}
|
||||
|
||||
if (ReflectionUtils.isHashCodeMethod(method)) {
|
||||
return System.identityHashCode(proxy);
|
||||
}
|
||||
}
|
||||
|
||||
// Invocation on ConnectionProxy interface coming in...
|
||||
switch (method.getName()) {
|
||||
|
||||
case "unwrap":
|
||||
return this.connection;
|
||||
case "close":
|
||||
|
@ -183,14 +178,7 @@ public class TransactionAwareConnectionFactoryProxy extends DelegatingConnection
|
|||
|
||||
private String proxyToString(@Nullable Object proxy) {
|
||||
// Allow for differentiating between the proxy and the raw Connection.
|
||||
StringBuilder sb = new StringBuilder("Transaction-aware proxy for target Connection ");
|
||||
if (this.connection != null) {
|
||||
sb.append("[").append(this.connection.toString()).append("]");
|
||||
}
|
||||
else {
|
||||
sb.append(" from ConnectionFactory [").append(this.targetConnectionFactory).append("]");
|
||||
}
|
||||
return sb.toString();
|
||||
return "Transaction-aware proxy for target Connection [" + this.connection.toString() + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ import org.springframework.util.Assert;
|
|||
* or obtain a {@link DatabaseClient#builder()} to create an instance.
|
||||
*
|
||||
* Usage example:
|
||||
* <p><pre class="code">
|
||||
* <pre class="code">
|
||||
* ConnectionFactory factory = …
|
||||
*
|
||||
* DatabaseClient client = DatabaseClient.create(factory);
|
||||
|
@ -60,7 +60,6 @@ public interface DatabaseClient extends ConnectionAccessor {
|
|||
* SQL call along with options leading to the execution. The SQL string can
|
||||
* contain either native parameter bind markers or named parameters (e.g.
|
||||
* {@literal :foo, :bar}) when {@link NamedParameterExpander} is enabled.
|
||||
*
|
||||
* @param sql must not be {@code null} or empty
|
||||
* @return a new {@link GenericExecuteSpec}
|
||||
* @see NamedParameterExpander
|
||||
|
@ -74,7 +73,6 @@ public interface DatabaseClient extends ConnectionAccessor {
|
|||
* the execution. The SQL string can contain either native parameter
|
||||
* bind markers or named parameters (e.g. {@literal :foo, :bar}) when
|
||||
* {@link NamedParameterExpander} is enabled.
|
||||
*
|
||||
* <p>Accepts {@link PreparedOperation} as SQL and binding {@link Supplier}
|
||||
* @param sqlSupplier must not be {@code null}
|
||||
* @return a new {@link GenericExecuteSpec}
|
||||
|
@ -85,7 +83,7 @@ public interface DatabaseClient extends ConnectionAccessor {
|
|||
GenericExecuteSpec sql(Supplier<String> sqlSupplier);
|
||||
|
||||
|
||||
// Static, factory methods
|
||||
// Static factory methods
|
||||
|
||||
/**
|
||||
* Create a {@code DatabaseClient} that will use the provided {@link ConnectionFactory}.
|
||||
|
@ -146,7 +144,6 @@ public interface DatabaseClient extends ConnectionAccessor {
|
|||
* Builder the {@link DatabaseClient} instance.
|
||||
*/
|
||||
DatabaseClient build();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -187,10 +184,8 @@ public interface DatabaseClient extends ConnectionAccessor {
|
|||
/**
|
||||
* Add the given filter to the end of the filter chain.
|
||||
* <p>Filter functions are typically used to invoke methods on the Statement
|
||||
* before it is executed.
|
||||
*
|
||||
* For example:
|
||||
* <p><pre class="code">
|
||||
* before it is executed. For example:
|
||||
* <pre class="code">
|
||||
* DatabaseClient client = …;
|
||||
* client.sql("SELECT book_id FROM book").filter(statement -> statement.fetchSize(100))
|
||||
* </pre>
|
||||
|
@ -204,10 +199,8 @@ public interface DatabaseClient extends ConnectionAccessor {
|
|||
/**
|
||||
* Add the given filter to the end of the filter chain.
|
||||
* <p>Filter functions are typically used to invoke methods on the Statement
|
||||
* before it is executed.
|
||||
*
|
||||
* For example:
|
||||
* <p><pre class="code">
|
||||
* before it is executed. For example:
|
||||
* <pre class="code">
|
||||
* DatabaseClient client = …;
|
||||
* client.sql("SELECT book_id FROM book").filter((statement, next) -> next.execute(statement.fetchSize(100)))
|
||||
* </pre>
|
||||
|
@ -244,7 +237,6 @@ public interface DatabaseClient extends ConnectionAccessor {
|
|||
* @return a {@link Mono} ignoring its payload (actively dropping).
|
||||
*/
|
||||
Mono<Void> then();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -53,7 +53,6 @@ import org.springframework.r2dbc.core.binding.BindTarget;
|
|||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
|
||||
/**
|
||||
* Default implementation of {@link DatabaseClient}.
|
||||
*
|
||||
|
@ -72,22 +71,17 @@ class DefaultDatabaseClient implements DatabaseClient {
|
|||
|
||||
private final ExecuteFunction executeFunction;
|
||||
|
||||
private final boolean namedParameters;
|
||||
|
||||
@Nullable
|
||||
private final NamedParameterExpander namedParameterExpander;
|
||||
|
||||
|
||||
DefaultDatabaseClient(BindMarkersFactory bindMarkersFactory,
|
||||
ConnectionFactory connectionFactory, ExecuteFunction executeFunction,
|
||||
boolean namedParameters) {
|
||||
DefaultDatabaseClient(BindMarkersFactory bindMarkersFactory, ConnectionFactory connectionFactory,
|
||||
ExecuteFunction executeFunction, boolean namedParameters) {
|
||||
|
||||
this.bindMarkersFactory = bindMarkersFactory;
|
||||
this.connectionFactory = connectionFactory;
|
||||
this.executeFunction = executeFunction;
|
||||
this.namedParameters = namedParameters;
|
||||
this.namedParameterExpander = namedParameters ? new NamedParameterExpander()
|
||||
: null;
|
||||
this.namedParameterExpander = (namedParameters ? new NamedParameterExpander() : null);
|
||||
}
|
||||
|
||||
|
||||
|
@ -233,9 +227,7 @@ class DefaultDatabaseClient implements DatabaseClient {
|
|||
|
||||
final StatementFilterFunction filterFunction;
|
||||
|
||||
|
||||
DefaultGenericExecuteSpec(Supplier<String> sqlSupplier) {
|
||||
|
||||
this.byIndex = Collections.emptyMap();
|
||||
this.byName = Collections.emptyMap();
|
||||
this.sqlSupplier = sqlSupplier;
|
||||
|
@ -255,8 +247,7 @@ class DefaultDatabaseClient implements DatabaseClient {
|
|||
public DefaultGenericExecuteSpec bind(int index, Object value) {
|
||||
assertNotPreparedOperation();
|
||||
Assert.notNull(value, () -> String.format(
|
||||
"Value at index %d must not be null. Use bindNull(…) instead.",
|
||||
index));
|
||||
"Value at index %d must not be null. Use bindNull(…) instead.", index));
|
||||
|
||||
Map<Integer, Parameter> byIndex = new LinkedHashMap<>(this.byIndex);
|
||||
|
||||
|
@ -286,8 +277,7 @@ class DefaultDatabaseClient implements DatabaseClient {
|
|||
|
||||
Assert.hasText(name, "Parameter name must not be null or empty!");
|
||||
Assert.notNull(value, () -> String.format(
|
||||
"Value for parameter %s must not be null. Use bindNull(…) instead.",
|
||||
name));
|
||||
"Value for parameter %s must not be null. Use bindNull(…) instead.", name));
|
||||
|
||||
Map<String, Parameter> byName = new LinkedHashMap<>(this.byName);
|
||||
|
||||
|
@ -314,9 +304,7 @@ class DefaultDatabaseClient implements DatabaseClient {
|
|||
|
||||
@Override
|
||||
public DefaultGenericExecuteSpec filter(StatementFilterFunction filter) {
|
||||
|
||||
Assert.notNull(filter, "Statement FilterFunction must not be null");
|
||||
|
||||
return new DefaultGenericExecuteSpec(this.byIndex, this.byName, this.sqlSupplier, this.filterFunction.andThen(filter));
|
||||
}
|
||||
|
||||
|
@ -336,38 +324,29 @@ class DefaultDatabaseClient implements DatabaseClient {
|
|||
return fetch().rowsUpdated().then();
|
||||
}
|
||||
|
||||
private <T> FetchSpec<T> execute(Supplier<String> sqlSupplier,
|
||||
BiFunction<Row, RowMetadata, T> mappingFunction) {
|
||||
|
||||
private <T> FetchSpec<T> execute(Supplier<String> sqlSupplier, BiFunction<Row, RowMetadata, T> mappingFunction) {
|
||||
String sql = getRequiredSql(sqlSupplier);
|
||||
|
||||
Function<Connection, Statement> statementFunction = connection -> {
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Executing SQL statement [" + sql + "]");
|
||||
}
|
||||
|
||||
if (sqlSupplier instanceof PreparedOperation<?>) {
|
||||
|
||||
Statement statement = connection.createStatement(sql);
|
||||
BindTarget bindTarget = new StatementWrapper(statement);
|
||||
((PreparedOperation<?>) sqlSupplier).bindTo(bindTarget);
|
||||
|
||||
return statement;
|
||||
}
|
||||
|
||||
if (DefaultDatabaseClient.this.namedParameters) {
|
||||
if (DefaultDatabaseClient.this.namedParameterExpander != null) {
|
||||
Map<String, Parameter> remainderByName = new LinkedHashMap<>(this.byName);
|
||||
Map<Integer, Parameter> remainderByIndex = new LinkedHashMap<>(this.byIndex);
|
||||
|
||||
Map<String, Parameter> remainderByName = new LinkedHashMap<>(
|
||||
this.byName);
|
||||
Map<Integer, Parameter> remainderByIndex = new LinkedHashMap<>(
|
||||
this.byIndex);
|
||||
List<String> parameterNames = DefaultDatabaseClient.this.namedParameterExpander.getParameterNames(sql);
|
||||
MapBindParameterSource namedBindings = retrieveParameters(
|
||||
sql, parameterNames, remainderByName, remainderByIndex);
|
||||
|
||||
MapBindParameterSource namedBindings = retrieveParameters(sql,
|
||||
remainderByName, remainderByIndex);
|
||||
|
||||
PreparedOperation<String> operation = DefaultDatabaseClient.this.namedParameterExpander.expand(sql,
|
||||
DefaultDatabaseClient.this.bindMarkersFactory, namedBindings);
|
||||
PreparedOperation<String> operation = DefaultDatabaseClient.this.namedParameterExpander.expand(
|
||||
sql, DefaultDatabaseClient.this.bindMarkersFactory, namedBindings);
|
||||
|
||||
String expanded = getRequiredSql(operation);
|
||||
if (logger.isTraceEnabled()) {
|
||||
|
@ -406,23 +385,16 @@ class DefaultDatabaseClient implements DatabaseClient {
|
|||
mappingFunction);
|
||||
}
|
||||
|
||||
private MapBindParameterSource retrieveParameters(String sql,
|
||||
Map<String, Parameter> remainderByName,
|
||||
Map<Integer, Parameter> remainderByIndex) {
|
||||
List<String> parameterNames = DefaultDatabaseClient.this.namedParameterExpander.getParameterNames(sql);
|
||||
Map<String, Parameter> namedBindings = new LinkedHashMap<>(
|
||||
parameterNames.size());
|
||||
private MapBindParameterSource retrieveParameters(String sql, List<String> parameterNames,
|
||||
Map<String, Parameter> remainderByName, Map<Integer, Parameter> remainderByIndex) {
|
||||
|
||||
Map<String, Parameter> namedBindings = new LinkedHashMap<>(parameterNames.size());
|
||||
for (String parameterName : parameterNames) {
|
||||
|
||||
Parameter parameter = getParameter(remainderByName, remainderByIndex,
|
||||
parameterNames, parameterName);
|
||||
|
||||
Parameter parameter = getParameter(remainderByName, remainderByIndex, parameterNames, parameterName);
|
||||
if (parameter == null) {
|
||||
throw new InvalidDataAccessApiUsageException(
|
||||
String.format("No parameter specified for [%s] in query [%s]",
|
||||
parameterName, sql));
|
||||
String.format("No parameter specified for [%s] in query [%s]", parameterName, sql));
|
||||
}
|
||||
|
||||
namedBindings.put(parameterName, parameter);
|
||||
}
|
||||
return new MapBindParameterSource(namedBindings);
|
||||
|
@ -430,8 +402,7 @@ class DefaultDatabaseClient implements DatabaseClient {
|
|||
|
||||
@Nullable
|
||||
private Parameter getParameter(Map<String, Parameter> remainderByName,
|
||||
Map<Integer, Parameter> remainderByIndex, List<String> parameterNames,
|
||||
String parameterName) {
|
||||
Map<Integer, Parameter> remainderByIndex, List<String> parameterNames, String parameterName) {
|
||||
|
||||
if (this.byName.containsKey(parameterName)) {
|
||||
remainderByName.remove(parameterName);
|
||||
|
@ -456,8 +427,9 @@ class DefaultDatabaseClient implements DatabaseClient {
|
|||
|
||||
private void bindByName(Statement statement, Map<String, Parameter> byName) {
|
||||
byName.forEach((name, parameter) -> {
|
||||
if (parameter.hasValue()) {
|
||||
statement.bind(name, parameter.getValue());
|
||||
Object value = parameter.getValue();
|
||||
if (value != null) {
|
||||
statement.bind(name, value);
|
||||
}
|
||||
else {
|
||||
statement.bindNull(name, parameter.getType());
|
||||
|
@ -467,8 +439,9 @@ class DefaultDatabaseClient implements DatabaseClient {
|
|||
|
||||
private void bindByIndex(Statement statement, Map<Integer, Parameter> byIndex) {
|
||||
byIndex.forEach((i, parameter) -> {
|
||||
if (parameter.hasValue()) {
|
||||
statement.bind(i, parameter.getValue());
|
||||
Object value = parameter.getValue();
|
||||
if (value != null) {
|
||||
statement.bind(i, value);
|
||||
}
|
||||
else {
|
||||
statement.bindNull(i, parameter.getType());
|
||||
|
@ -477,10 +450,8 @@ class DefaultDatabaseClient implements DatabaseClient {
|
|||
}
|
||||
|
||||
private String getRequiredSql(Supplier<String> sqlSupplier) {
|
||||
|
||||
String sql = sqlSupplier.get();
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -497,32 +468,26 @@ class DefaultDatabaseClient implements DatabaseClient {
|
|||
|
||||
private final Connection target;
|
||||
|
||||
|
||||
CloseSuppressingInvocationHandler(Connection target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object invoke(Object proxy, Method method, Object[] args)
|
||||
throws Throwable {
|
||||
// Invocation on ConnectionProxy interface coming in...
|
||||
|
||||
if (method.getName().equals("equals")) {
|
||||
// Only consider equal when proxies are identical.
|
||||
return proxy == args[0];
|
||||
}
|
||||
else if (method.getName().equals("hashCode")) {
|
||||
// Use hashCode of PersistenceManager proxy.
|
||||
return System.identityHashCode(proxy);
|
||||
}
|
||||
else if (method.getName().equals("unwrap")) {
|
||||
return this.target;
|
||||
}
|
||||
else if (method.getName().equals("close")) {
|
||||
// Handle close method: suppress, not valid.
|
||||
return Mono.error(
|
||||
new UnsupportedOperationException("Close is not supported!"));
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
switch (method.getName()) {
|
||||
case "equals":
|
||||
// Only consider equal when proxies are identical.
|
||||
return proxy == args[0];
|
||||
case "hashCode":
|
||||
// Use hashCode of PersistenceManager proxy.
|
||||
return System.identityHashCode(proxy);
|
||||
case "unwrap":
|
||||
return this.target;
|
||||
case "close":
|
||||
// Handle close method: suppress, not valid.
|
||||
return Mono.error(
|
||||
new UnsupportedOperationException("Close is not supported!"));
|
||||
}
|
||||
|
||||
// Invoke method on target Connection.
|
||||
|
@ -533,12 +498,11 @@ class DefaultDatabaseClient implements DatabaseClient {
|
|||
throw ex.getTargetException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Holder for a connection that makes sure the close action is invoked atomically only
|
||||
* once.
|
||||
* Holder for a connection that makes sure the close action is invoked atomically only once.
|
||||
*/
|
||||
static class ConnectionCloseHolder extends AtomicBoolean {
|
||||
|
||||
|
@ -548,7 +512,6 @@ class DefaultDatabaseClient implements DatabaseClient {
|
|||
|
||||
final Function<Connection, Publisher<Void>> closeFunction;
|
||||
|
||||
|
||||
ConnectionCloseHolder(Connection connection,
|
||||
Function<Connection, Publisher<Void>> closeFunction) {
|
||||
this.connection = connection;
|
||||
|
@ -556,24 +519,20 @@ class DefaultDatabaseClient implements DatabaseClient {
|
|||
}
|
||||
|
||||
Mono<Void> close() {
|
||||
|
||||
return Mono.defer(() -> {
|
||||
|
||||
if (compareAndSet(false, true)) {
|
||||
return Mono.from(this.closeFunction.apply(this.connection));
|
||||
}
|
||||
|
||||
return Mono.empty();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static class StatementWrapper implements BindTarget {
|
||||
|
||||
final Statement statement;
|
||||
|
||||
|
||||
StatementWrapper(Statement statement) {
|
||||
this.statement = statement;
|
||||
}
|
||||
|
@ -597,7 +556,6 @@ class DefaultDatabaseClient implements DatabaseClient {
|
|||
public void bindNull(int index, Class<?> type) {
|
||||
this.statement.bindNull(index, type);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -74,8 +74,7 @@ public final class Parameter {
|
|||
|
||||
|
||||
/**
|
||||
* Returns the column value. Can be {@code null}.
|
||||
* @return the column value. Can be {@code null}
|
||||
* Return the column value. Can be {@code null}.
|
||||
* @see #hasValue()
|
||||
*/
|
||||
@Nullable
|
||||
|
@ -85,7 +84,6 @@ 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
|
||||
*/
|
||||
public Class<?> getType() {
|
||||
return this.type;
|
||||
|
@ -93,29 +91,30 @@ public final class Parameter {
|
|||
|
||||
/**
|
||||
* Returns whether this {@link Parameter} has a value.
|
||||
* @return whether this {@link Parameter} has a value. {@code false} if {@link #getValue()} is {@code null}
|
||||
* @return {@code false} if {@link #getValue()} is {@code null}
|
||||
*/
|
||||
public boolean hasValue() {
|
||||
return this.value != null;
|
||||
return (this.value != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this {@link Parameter} has a empty.
|
||||
* @return whether this {@link Parameter} is empty. {@code true} if {@link #getValue()} is {@code null}
|
||||
* @return {@code true} if {@link #getValue()} is {@code null}
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return this.value == null;
|
||||
return (this.value == null);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof Parameter)) {
|
||||
if (!(obj instanceof Parameter)) {
|
||||
return false;
|
||||
}
|
||||
Parameter other = (Parameter) o;
|
||||
Parameter other = (Parameter) obj;
|
||||
return ObjectUtils.nullSafeEquals(this.value, other.value) && ObjectUtils.nullSafeEquals(this.type, other.type);
|
||||
}
|
||||
|
||||
|
@ -126,12 +125,7 @@ public final class Parameter {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append("Parameter");
|
||||
sb.append("[value=").append(this.value);
|
||||
sb.append(", type=").append(this.type);
|
||||
sb.append(']');
|
||||
return sb.toString();
|
||||
return "Parameter[value=" + this.value + ", type=" + this.type + ']';
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -32,13 +32,12 @@ import io.r2dbc.spi.Statement;
|
|||
public interface BindMarker {
|
||||
|
||||
/**
|
||||
* Returns the database-specific placeholder for a given substitution.
|
||||
* Return the database-specific placeholder for a given substitution.
|
||||
*/
|
||||
String getPlaceholder();
|
||||
|
||||
/**
|
||||
* Bind the given {@code value} to the {@link Statement} using the underlying binding strategy.
|
||||
*
|
||||
* @param bindTarget the target to bind the value to
|
||||
* @param value the actual value. Must not be {@code null}
|
||||
* Use {@link #bindNull(BindTarget, Class)} for {@code null} values
|
||||
|
|
|
@ -28,6 +28,7 @@ import java.util.function.Consumer;
|
|||
|
||||
import io.r2dbc.spi.Statement;
|
||||
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
@ -69,37 +70,10 @@ public class Bindings implements Iterable<Bindings.Binding> {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new, empty {@link Bindings} object.
|
||||
*
|
||||
* @return a new, empty {@link Bindings} object.
|
||||
*/
|
||||
public static Bindings empty() {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
|
||||
protected Map<BindMarker, Binding> getBindings() {
|
||||
return this.bindings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge this bindings with an other {@link Bindings} object and create a new merged
|
||||
* {@link Bindings} object.
|
||||
* @param left the left object to merge with
|
||||
* @param right the right object to merge with
|
||||
* @return a new, merged {@link Bindings} object
|
||||
*/
|
||||
public static Bindings merge(Bindings left, Bindings right) {
|
||||
Assert.notNull(left, "Left side Bindings must not be null");
|
||||
Assert.notNull(right, "Right side Bindings must not be null");
|
||||
List<Binding> result = new ArrayList<>(
|
||||
left.getBindings().size() + right.getBindings().size());
|
||||
result.addAll(left.getBindings().values());
|
||||
result.addAll(right.getBindings().values());
|
||||
return new Bindings(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge this bindings with an other {@link Bindings} object and create a new merged
|
||||
* {@link Bindings} object.
|
||||
|
@ -141,6 +115,32 @@ public class Bindings implements Iterable<Bindings.Binding> {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new, empty {@link Bindings} object.
|
||||
* @return a new, empty {@link Bindings} object.
|
||||
*/
|
||||
public static Bindings empty() {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge this bindings with an other {@link Bindings} object and create a new merged
|
||||
* {@link Bindings} object.
|
||||
* @param left the left object to merge with
|
||||
* @param right the right object to merge with
|
||||
* @return a new, merged {@link Bindings} object
|
||||
*/
|
||||
public static Bindings merge(Bindings left, Bindings right) {
|
||||
Assert.notNull(left, "Left side Bindings must not be null");
|
||||
Assert.notNull(right, "Right side Bindings must not be null");
|
||||
List<Binding> result = new ArrayList<>(
|
||||
left.getBindings().size() + right.getBindings().size());
|
||||
result.addAll(left.getBindings().values());
|
||||
result.addAll(right.getBindings().values());
|
||||
return new Bindings(result);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Base class for value objects representing a value or a {@code NULL} binding.
|
||||
*/
|
||||
|
@ -188,7 +188,6 @@ public class Bindings implements Iterable<Bindings.Binding> {
|
|||
* @param bindTarget the target to apply bindings to
|
||||
*/
|
||||
public abstract void apply(BindTarget bindTarget);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -199,19 +198,18 @@ public class Bindings implements Iterable<Bindings.Binding> {
|
|||
|
||||
private final Object value;
|
||||
|
||||
|
||||
ValueBinding(BindMarker marker, Object value) {
|
||||
super(marker);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean hasValue() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Object getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
@ -220,9 +218,9 @@ public class Bindings implements Iterable<Bindings.Binding> {
|
|||
public void apply(BindTarget bindTarget) {
|
||||
getBindMarker().bind(bindTarget, getValue());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@code NULL} binding.
|
||||
*/
|
||||
|
@ -230,13 +228,11 @@ public class Bindings implements Iterable<Bindings.Binding> {
|
|||
|
||||
private final Class<?> valueType;
|
||||
|
||||
|
||||
NullBinding(BindMarker marker, Class<?> valueType) {
|
||||
super(marker);
|
||||
this.valueType = valueType;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean hasValue() {
|
||||
return false;
|
||||
|
@ -256,7 +252,6 @@ public class Bindings implements Iterable<Bindings.Binding> {
|
|||
public void apply(BindTarget bindTarget) {
|
||||
getBindMarker().bindNull(bindTarget, getValueType());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -69,6 +69,7 @@ public class RequestEntity<T> extends HttpEntity<T> {
|
|||
@Nullable
|
||||
private final HttpMethod method;
|
||||
|
||||
@Nullable
|
||||
private final URI url;
|
||||
|
||||
@Nullable
|
||||
|
@ -138,7 +139,7 @@ public class RequestEntity<T> extends HttpEntity<T> {
|
|||
* @since 4.3
|
||||
*/
|
||||
public RequestEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers,
|
||||
@Nullable HttpMethod method, URI url, @Nullable Type type) {
|
||||
@Nullable HttpMethod method, @Nullable URI url, @Nullable Type type) {
|
||||
|
||||
super(body, headers);
|
||||
this.method = method;
|
||||
|
@ -160,6 +161,9 @@ public class RequestEntity<T> extends HttpEntity<T> {
|
|||
* Return the URL of the request.
|
||||
*/
|
||||
public URI getUrl() {
|
||||
if (this.url == null) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
return this.url;
|
||||
}
|
||||
|
||||
|
@ -679,14 +683,13 @@ public class RequestEntity<T> extends HttpEntity<T> {
|
|||
*/
|
||||
public static class UriTemplateRequestEntity<T> extends RequestEntity<T> {
|
||||
|
||||
String uriTemplate;
|
||||
private final String uriTemplate;
|
||||
|
||||
@Nullable
|
||||
private Object[] uriVarsArray;
|
||||
private final Object[] uriVarsArray;
|
||||
|
||||
@Nullable
|
||||
Map<String, ?> uriVarsMap;
|
||||
|
||||
private final Map<String, ?> uriVarsMap;
|
||||
|
||||
UriTemplateRequestEntity(
|
||||
@Nullable T body, @Nullable MultiValueMap<String, String> headers,
|
||||
|
@ -699,7 +702,6 @@ public class RequestEntity<T> extends HttpEntity<T> {
|
|||
this.uriVarsMap = uriVarsMap;
|
||||
}
|
||||
|
||||
|
||||
public String getUriTemplate() {
|
||||
return this.uriTemplate;
|
||||
}
|
||||
|
@ -714,11 +716,6 @@ public class RequestEntity<T> extends HttpEntity<T> {
|
|||
return this.uriVarsMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getUrl() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return format(getMethod(), getUriTemplate(), getBody(), getHeaders());
|
||||
|
|
|
@ -667,9 +667,15 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
|
|||
private URI resolveUrl(RequestEntity<?> entity) {
|
||||
if (entity instanceof RequestEntity.UriTemplateRequestEntity) {
|
||||
RequestEntity.UriTemplateRequestEntity<?> ext = (RequestEntity.UriTemplateRequestEntity<?>) entity;
|
||||
return (ext.getVars() != null ?
|
||||
this.uriTemplateHandler.expand(ext.getUriTemplate(), ext.getVars()) :
|
||||
this.uriTemplateHandler.expand(ext.getUriTemplate(), ext.getVarsMap()));
|
||||
if (ext.getVars() != null) {
|
||||
return this.uriTemplateHandler.expand(ext.getUriTemplate(), ext.getVars());
|
||||
}
|
||||
else if (ext.getVarsMap() != null) {
|
||||
return this.uriTemplateHandler.expand(ext.getUriTemplate(), ext.getVarsMap());
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("No variables specified for URI template: " + ext.getUriTemplate());
|
||||
}
|
||||
}
|
||||
else {
|
||||
return entity.getUrl();
|
||||
|
|
|
@ -137,19 +137,19 @@ public class ForwardedHeaderTransformer implements Function<ServerHttpRequest, S
|
|||
private static String getForwardedPrefix(ServerHttpRequest request) {
|
||||
HttpHeaders headers = request.getHeaders();
|
||||
String header = headers.getFirst("X-Forwarded-Prefix");
|
||||
if (header != null) {
|
||||
StringBuilder prefix = new StringBuilder(header.length());
|
||||
String[] rawPrefixes = StringUtils.tokenizeToStringArray(header, ",");
|
||||
for (String rawPrefix : rawPrefixes) {
|
||||
int endIndex = rawPrefix.length();
|
||||
while (endIndex > 1 && rawPrefix.charAt(endIndex - 1) == '/') {
|
||||
endIndex--;
|
||||
}
|
||||
prefix.append((endIndex != rawPrefix.length() ? rawPrefix.substring(0, endIndex) : rawPrefix));
|
||||
}
|
||||
return prefix.toString();
|
||||
if (header == null) {
|
||||
return null;
|
||||
}
|
||||
return header;
|
||||
StringBuilder prefix = new StringBuilder(header.length());
|
||||
String[] rawPrefixes = StringUtils.tokenizeToStringArray(header, ",");
|
||||
for (String rawPrefix : rawPrefixes) {
|
||||
int endIndex = rawPrefix.length();
|
||||
while (endIndex > 1 && rawPrefix.charAt(endIndex - 1) == '/') {
|
||||
endIndex--;
|
||||
}
|
||||
prefix.append((endIndex != rawPrefix.length() ? rawPrefix.substring(0, endIndex) : rawPrefix));
|
||||
}
|
||||
return prefix.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -411,10 +411,10 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
|
|||
|
||||
@Override
|
||||
public RequestMatchResult match(HttpServletRequest request, String pattern) {
|
||||
Assert.isNull(getPatternParser(), "This HandlerMapping requires a PathPattern.");
|
||||
Assert.isNull(getPatternParser(), "This HandlerMapping requires a PathPattern");
|
||||
RequestMappingInfo info = RequestMappingInfo.paths(pattern).options(this.config).build();
|
||||
RequestMappingInfo match = info.getMatchingCondition(request);
|
||||
return (match != null ?
|
||||
return (match != null && match.getPatternsCondition() != null ?
|
||||
new RequestMatchResult(
|
||||
match.getPatternsCondition().getPatterns().iterator().next(),
|
||||
UrlPathHelper.getResolvedLookupPath(request),
|
||||
|
|
Loading…
Reference in New Issue