Remove deprecated DatabaseClient, connectionfactory package and other deprecated code
Removal of the connectionfactory package and deprecated core classes that were migrated into Spring Framework R2DBC. Closes #712
This commit is contained in:
@@ -283,9 +283,9 @@ public class PersonWriteConverter implements Converter<Person, OutboundRow> {
|
||||
|
||||
public OutboundRow convert(Person source) {
|
||||
OutboundRow row = new OutboundRow();
|
||||
row.put("id", SettableValue.from(source.getId()));
|
||||
row.put("name", SettableValue.from(source.getFirstName()));
|
||||
row.put("age", SettableValue.from(source.getAge()));
|
||||
row.put("id", Parameter.from(source.getId()));
|
||||
row.put("name", Parameter.from(source.getFirstName()));
|
||||
row.put("age", Parameter.from(source.getAge()));
|
||||
return row;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2021 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.data.r2dbc;
|
||||
|
||||
import io.r2dbc.spi.R2dbcException;
|
||||
|
||||
/**
|
||||
* Exception thrown when SQL specified is invalid. Such exceptions always have a {@link io.r2dbc.spi.R2dbcException}
|
||||
* root cause.
|
||||
* <p>
|
||||
* It would be possible to have subclasses for no such table, no such column etc. A custom
|
||||
* {@link org.springframework.data.r2dbc.support.R2dbcExceptionTranslator} could create such more specific exceptions,
|
||||
* without affecting code using this class.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @deprecated since 1.2, use directly Spring R2DBC's {@link org.springframework.r2dbc.BadSqlGrammarException} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class BadSqlGrammarException extends org.springframework.r2dbc.BadSqlGrammarException {
|
||||
|
||||
private static final long serialVersionUID = 3814579246913482054L;
|
||||
|
||||
/**
|
||||
* Creates a new {@link BadSqlGrammarException}.
|
||||
*
|
||||
* @param task name of current task.
|
||||
* @param sql the offending SQL statement.
|
||||
* @param ex the root cause.
|
||||
*/
|
||||
public BadSqlGrammarException(String task, String sql, R2dbcException ex) {
|
||||
super(task, sql, ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the wrapped {@link R2dbcException}.
|
||||
*/
|
||||
public R2dbcException getR2dbcException() {
|
||||
return (R2dbcException) getCause();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SQL that caused the problem.
|
||||
*/
|
||||
public String getSql() {
|
||||
return super.getSql();
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2021 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.data.r2dbc;
|
||||
|
||||
import io.r2dbc.spi.R2dbcException;
|
||||
|
||||
import org.springframework.dao.InvalidDataAccessResourceUsageException;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Exception thrown when a {@link io.r2dbc.spi.Result} has been accessed in an invalid fashion. Such exceptions always
|
||||
* have a {@link io.r2dbc.spi.R2dbcException} root cause.
|
||||
* <p>
|
||||
* This typically happens when an invalid {@link org.springframework.data.r2dbc.core.FetchSpec} column index or name has
|
||||
* been specified.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @see BadSqlGrammarException
|
||||
* @deprecated since 1.2, not in use anymore.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@Deprecated
|
||||
public class InvalidResultAccessException extends InvalidDataAccessResourceUsageException {
|
||||
|
||||
private final @Nullable String sql;
|
||||
|
||||
/**
|
||||
* Creates a new {@link InvalidResultAccessException}.
|
||||
*
|
||||
* @param task name of current task.
|
||||
* @param sql the offending SQL statement.
|
||||
* @param ex the root cause.
|
||||
*/
|
||||
public InvalidResultAccessException(String task, @Nullable String sql, R2dbcException ex) {
|
||||
|
||||
super(task + "; invalid Result access for SQL [" + sql + "]", ex);
|
||||
|
||||
this.sql = sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link InvalidResultAccessException}.
|
||||
*
|
||||
* @param ex the root cause.
|
||||
*/
|
||||
public InvalidResultAccessException(R2dbcException ex) {
|
||||
|
||||
super(ex.getMessage(), ex);
|
||||
|
||||
this.sql = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the wrapped {@link R2dbcException}.
|
||||
*/
|
||||
public R2dbcException getR2dbcException() {
|
||||
return (R2dbcException) getCause();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SQL that caused the problem.
|
||||
*
|
||||
* @return the offending SQL, if known.
|
||||
*/
|
||||
@Nullable
|
||||
public String getSql() {
|
||||
return this.sql;
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2021 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.data.r2dbc;
|
||||
|
||||
import io.r2dbc.spi.R2dbcException;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Exception thrown when we can't classify a {@link R2dbcException} into one of our generic data access exceptions.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @deprecated since 1.2, use Spring R2DBC's {@link org.springframework.r2dbc.UncategorizedR2dbcException} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class UncategorizedR2dbcException extends org.springframework.r2dbc.UncategorizedR2dbcException {
|
||||
|
||||
private static final long serialVersionUID = 361587356435210266L;
|
||||
|
||||
/**
|
||||
* Creates a new {@link UncategorizedR2dbcException}.
|
||||
*
|
||||
* @param task name of current task
|
||||
* @param sql the offending SQL statement
|
||||
* @param ex the root cause
|
||||
*/
|
||||
public UncategorizedR2dbcException(String task, @Nullable String sql, R2dbcException ex) {
|
||||
super(task, sql, ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the original {@link R2dbcException}.
|
||||
*
|
||||
* @return the original {@link R2dbcException}.
|
||||
*/
|
||||
public R2dbcException getR2dbcException() {
|
||||
return (R2dbcException) getCause();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SQL that led to the problem (if known).
|
||||
*/
|
||||
@Nullable
|
||||
public String getSql() {
|
||||
return super.getSql();
|
||||
}
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2021 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.data.r2dbc.connectionfactory;
|
||||
|
||||
import io.r2dbc.spi.Connection;
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.transaction.reactive.TransactionSynchronizationManager;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Helper class that provides static methods for obtaining R2DBC Connections from a
|
||||
* {@link io.r2dbc.spi.ConnectionFactory}.
|
||||
* <p>
|
||||
* Used internally by Spring's {@link org.springframework.data.r2dbc.core.DatabaseClient}, Spring's R2DBC operation
|
||||
* objects. Can also be used directly in application code.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC. Use
|
||||
* {@link org.springframework.r2dbc.connection.ConnectionFactoryUtils} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class ConnectionFactoryUtils {
|
||||
|
||||
/**
|
||||
* Order value for ReactiveTransactionSynchronization objects that clean up R2DBC Connections.
|
||||
*/
|
||||
public static final int CONNECTION_SYNCHRONIZATION_ORDER = 1000;
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ConnectionFactoryUtils.class);
|
||||
|
||||
private ConnectionFactoryUtils() {}
|
||||
|
||||
/**
|
||||
* Obtain a {@link io.r2dbc.spi.Connection} from the given {@link io.r2dbc.spi.ConnectionFactory}. 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 reactor.util.context.Context}. Will bind a
|
||||
* Connection to the {@link reactor.util.context.Context} if transaction synchronization is active.
|
||||
*
|
||||
* @param connectionFactory the {@link io.r2dbc.spi.ConnectionFactory} to obtain {@link io.r2dbc.spi.Connection
|
||||
* Connections} from.
|
||||
* @return a R2DBC Connection from the given {@link io.r2dbc.spi.ConnectionFactory}.
|
||||
* @throws DataAccessResourceFailureException if the attempt to get a {@link io.r2dbc.spi.Connection} failed.
|
||||
* @see #releaseConnection
|
||||
*/
|
||||
public static Mono<Connection> getConnection(ConnectionFactory connectionFactory) {
|
||||
return org.springframework.r2dbc.connection.ConnectionFactoryUtils.getConnection(connectionFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually obtain a R2DBC Connection from the given {@link io.r2dbc.spi.ConnectionFactory}. Same as
|
||||
* {@link #getConnection}, but preserving the original exceptions.
|
||||
* <p>
|
||||
* Is aware of a corresponding Connection bound to the current {@link reactor.util.context.Context}. Will bind a
|
||||
* Connection to the {@link reactor.util.context.Context} if transaction synchronization is active.
|
||||
*
|
||||
* @param connectionFactory the {@link io.r2dbc.spi.ConnectionFactory} to obtain Connections from.
|
||||
* @return a R2DBC {@link io.r2dbc.spi.Connection} from the given {@link io.r2dbc.spi.ConnectionFactory}.
|
||||
*/
|
||||
public static Mono<Connection> doGetConnection(ConnectionFactory connectionFactory) {
|
||||
return org.springframework.r2dbc.connection.ConnectionFactoryUtils.doGetConnection(connectionFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the given {@link io.r2dbc.spi.Connection}, obtained from the given {@link io.r2dbc.spi.ConnectionFactory}, if
|
||||
* it is not managed externally (that is, not bound to the thread).
|
||||
*
|
||||
* @param con the {@link io.r2dbc.spi.Connection} to close if necessary.
|
||||
* @param connectionFactory the {@link io.r2dbc.spi.ConnectionFactory} that the Connection was obtained from.
|
||||
* @see #getConnection
|
||||
*/
|
||||
public static Mono<Void> releaseConnection(io.r2dbc.spi.Connection con, ConnectionFactory connectionFactory) {
|
||||
|
||||
return doReleaseConnection(con, connectionFactory)
|
||||
.onErrorMap(e -> new DataAccessResourceFailureException("Failed to close R2DBC Connection", e));
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually close the given {@link io.r2dbc.spi.Connection}, obtained from the given
|
||||
* {@link io.r2dbc.spi.ConnectionFactory}. Same as {@link #releaseConnection}, but preserving the original exception.
|
||||
*
|
||||
* @param connection the {@link io.r2dbc.spi.Connection} to close if necessary.
|
||||
* @param connectionFactory the {@link io.r2dbc.spi.ConnectionFactory} that the Connection was obtained from.
|
||||
* @see #doGetConnection
|
||||
*/
|
||||
public static Mono<Void> doReleaseConnection(io.r2dbc.spi.Connection connection,
|
||||
ConnectionFactory connectionFactory) {
|
||||
|
||||
return org.springframework.r2dbc.connection.ConnectionFactoryUtils.doReleaseConnection(connection,
|
||||
connectionFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the {@link io.r2dbc.spi.Connection}. Translates exceptions into the Spring hierarchy of unchecked generic
|
||||
* data access exceptions, simplifying calling code and making any exception that is thrown more meaningful.
|
||||
*
|
||||
* @param connection the {@link io.r2dbc.spi.Connection} to close.
|
||||
* @param connectionFactory the {@link io.r2dbc.spi.ConnectionFactory} that the {@link io.r2dbc.spi.Connection} was
|
||||
* obtained from.
|
||||
* @return a R2DBC Connection from the given {@link io.r2dbc.spi.ConnectionFactory}.
|
||||
* @throws DataAccessResourceFailureException if the attempt to get a {@link io.r2dbc.spi.Connection} failed
|
||||
*/
|
||||
public static Mono<Void> closeConnection(Connection connection, ConnectionFactory connectionFactory) {
|
||||
|
||||
Assert.notNull(connection, "Connection must not be null!");
|
||||
Assert.notNull(connectionFactory, "ConnectionFactory must not be null!");
|
||||
|
||||
return doCloseConnection(connection, connectionFactory)
|
||||
.onErrorMap(e -> new DataAccessResourceFailureException("Failed to obtain R2DBC Connection", e));
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the {@link io.r2dbc.spi.Connection}, unless a {@link SmartConnectionFactory} doesn't want us to.
|
||||
*
|
||||
* @param connection the {@link io.r2dbc.spi.Connection} to close if necessary.
|
||||
* @param connectionFactory the {@link io.r2dbc.spi.ConnectionFactory} that the Connection was obtained from.
|
||||
* @see Connection#close()
|
||||
* @see SmartConnectionFactory#shouldClose(Connection)
|
||||
*/
|
||||
public static Mono<Void> doCloseConnection(Connection connection, @Nullable ConnectionFactory connectionFactory) {
|
||||
|
||||
if (!(connectionFactory instanceof SmartConnectionFactory)
|
||||
|| ((SmartConnectionFactory) connectionFactory).shouldClose(connection)) {
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Closing R2DBC Connection");
|
||||
}
|
||||
|
||||
return Mono.from(connection.close());
|
||||
}
|
||||
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the {@link io.r2dbc.spi.ConnectionFactory} from the current subscriber {@link reactor.util.context.Context}.
|
||||
*
|
||||
* @param connectionFactory the {@link io.r2dbc.spi.ConnectionFactory} that the Connection was obtained from.
|
||||
* @see TransactionSynchronizationManager
|
||||
*/
|
||||
public static Mono<ConnectionFactory> currentConnectionFactory(ConnectionFactory connectionFactory) {
|
||||
|
||||
return org.springframework.r2dbc.connection.ConnectionFactoryUtils.currentConnectionFactory(connectionFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the innermost target {@link io.r2dbc.spi.Connection} of the given {@link io.r2dbc.spi.Connection}. If the
|
||||
* given {@link io.r2dbc.spi.Connection} is a proxy, it will be unwrapped until a non-proxy
|
||||
* {@link io.r2dbc.spi.Connection} is found. Otherwise, the passed-in Connection will be returned as-is.
|
||||
*
|
||||
* @param con the {@link io.r2dbc.spi.Connection} proxy to unwrap
|
||||
* @return the innermost target Connection, or the passed-in one if no proxy
|
||||
* @see ConnectionProxy#getTargetConnection()
|
||||
*/
|
||||
public static Connection getTargetConnection(Connection con) {
|
||||
|
||||
Connection conToUse = con;
|
||||
while (conToUse instanceof ConnectionProxy) {
|
||||
conToUse = ((ConnectionProxy) conToUse).getTargetConnection();
|
||||
}
|
||||
return conToUse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the connection synchronization order to use for the given {@link io.r2dbc.spi.ConnectionFactory}.
|
||||
* Decreased for every level of nesting that a {@link io.r2dbc.spi.ConnectionFactory} has, checked through the level
|
||||
* of {@link DelegatingConnectionFactory} nesting.
|
||||
*
|
||||
* @param connectionFactory the {@link io.r2dbc.spi.ConnectionFactory} to check.
|
||||
* @return the connection synchronization order to use.
|
||||
* @see #CONNECTION_SYNCHRONIZATION_ORDER
|
||||
*/
|
||||
private static int getConnectionSynchronizationOrder(ConnectionFactory connectionFactory) {
|
||||
|
||||
int order = CONNECTION_SYNCHRONIZATION_ORDER;
|
||||
ConnectionFactory current = connectionFactory;
|
||||
while (current instanceof DelegatingConnectionFactory) {
|
||||
order--;
|
||||
current = ((DelegatingConnectionFactory) current).getTargetConnectionFactory();
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory;
|
||||
|
||||
import io.r2dbc.spi.Connection;
|
||||
|
||||
/**
|
||||
* Simple interface to be implemented by handles for a R2DBC Connection.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @see SimpleConnectionHandle
|
||||
* @see ConnectionHolder
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC without replacement.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
@Deprecated
|
||||
public interface ConnectionHandle {
|
||||
|
||||
/**
|
||||
* Fetch the R2DBC Connection that this handle refers to.
|
||||
*/
|
||||
Connection getConnection();
|
||||
|
||||
/**
|
||||
* Release the R2DBC Connection that this handle refers to. Assumes a non-blocking implementation without
|
||||
* synchronization.
|
||||
* <p>
|
||||
* The default implementation is empty, assuming that the lifecycle of the connection is managed externally.
|
||||
*
|
||||
* @param connection the R2DBC Connection to release
|
||||
*/
|
||||
default void releaseConnection(Connection connection) {
|
||||
}
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory;
|
||||
|
||||
import io.r2dbc.spi.Connection;
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.transaction.support.ResourceHolderSupport;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Resource holder wrapping a R2DBC {@link Connection}. {@link R2dbcTransactionManager} binds instances of this class to
|
||||
* the thread, for a specific {@link ConnectionFactory}.
|
||||
* <p>
|
||||
* Inherits rollback-only support for nested R2DBC transactions and reference count functionality from the base class.
|
||||
* <p>
|
||||
* Note: This is an SPI class, not intended to be used by applications.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @see R2dbcTransactionManager
|
||||
* @see ConnectionFactoryUtils
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC. Use {@link org.springframework.r2dbc.connection.ConnectionHolder}
|
||||
* instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class ConnectionHolder extends ResourceHolderSupport {
|
||||
|
||||
@Nullable private ConnectionHandle connectionHandle;
|
||||
|
||||
@Nullable private Connection currentConnection;
|
||||
|
||||
private boolean transactionActive;
|
||||
|
||||
/**
|
||||
* Create a new ConnectionHolder for the given R2DBC {@link Connection}, wrapping it with a
|
||||
* {@link SimpleConnectionHandle}, assuming that there is no ongoing transaction.
|
||||
*
|
||||
* @param connection the R2DBC {@link Connection} to hold
|
||||
* @see SimpleConnectionHandle
|
||||
* @see #ConnectionHolder(Connection, boolean)
|
||||
*/
|
||||
public ConnectionHolder(Connection connection) {
|
||||
this(connection, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ConnectionHolder for the given R2DBC {@link Connection}, wrapping it with a
|
||||
* {@link SimpleConnectionHandle}.
|
||||
*
|
||||
* @param connection the R2DBC {@link Connection} to hold
|
||||
* @param transactionActive whether the given {@link Connection} is involved in an ongoing transaction
|
||||
* @see SimpleConnectionHandle
|
||||
*/
|
||||
public ConnectionHolder(Connection connection, boolean transactionActive) {
|
||||
|
||||
this.connectionHandle = new SimpleConnectionHandle(connection);
|
||||
this.transactionActive = transactionActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ConnectionHandle held by this ConnectionHolder.
|
||||
*/
|
||||
@Nullable
|
||||
public ConnectionHandle getConnectionHandle() {
|
||||
return this.connectionHandle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this holder currently has a {@link Connection}.
|
||||
*/
|
||||
protected boolean hasConnection() {
|
||||
return (this.connectionHandle != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether this holder represents an active, R2DBC-managed transaction.
|
||||
*
|
||||
* @see R2dbcTransactionManager
|
||||
*/
|
||||
protected void setTransactionActive(boolean transactionActive) {
|
||||
this.transactionActive = transactionActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this holder represents an active, R2DBC-managed transaction.
|
||||
*/
|
||||
protected boolean isTransactionActive() {
|
||||
return this.transactionActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the existing Connection handle with the given {@link Connection}. Reset the handle if given
|
||||
* {@literal null}.
|
||||
* <p>
|
||||
* Used for releasing the {@link Connection} on suspend (with a {@literal null} argument) and setting a fresh
|
||||
* {@link Connection} on resume.
|
||||
*/
|
||||
protected void setConnection(@Nullable Connection connection) {
|
||||
if (this.currentConnection != null) {
|
||||
if (this.connectionHandle != null) {
|
||||
this.connectionHandle.releaseConnection(this.currentConnection);
|
||||
}
|
||||
this.currentConnection = null;
|
||||
}
|
||||
if (connection != null) {
|
||||
this.connectionHandle = new SimpleConnectionHandle(connection);
|
||||
} else {
|
||||
this.connectionHandle = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current {@link Connection} held by this {@link ConnectionHolder}.
|
||||
* <p>
|
||||
* This will be the same {@link Connection} until {@code released} gets called on the {@link ConnectionHolder}, which
|
||||
* will reset the held {@link Connection}, fetching a new {@link Connection} on demand.
|
||||
*
|
||||
* @see ConnectionHandle#getConnection()
|
||||
* @see #released()
|
||||
*/
|
||||
public Connection getConnection() {
|
||||
|
||||
Assert.notNull(this.connectionHandle, "Active Connection is required");
|
||||
if (this.currentConnection == null) {
|
||||
this.currentConnection = this.connectionHandle.getConnection();
|
||||
}
|
||||
return this.currentConnection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the current {@link Connection} held by this {@link ConnectionHolder}.
|
||||
* <p>
|
||||
* This is necessary for {@link ConnectionHandle}s that expect "Connection borrowing", where each returned
|
||||
* {@link Connection} is only temporarily leased and needs to be returned once the data operation is done, to make the
|
||||
* Connection available for other operations within the same transaction.
|
||||
*/
|
||||
@Override
|
||||
public void released() {
|
||||
super.released();
|
||||
if (!isOpen() && this.currentConnection != null) {
|
||||
if (this.connectionHandle != null) {
|
||||
this.connectionHandle.releaseConnection(this.currentConnection);
|
||||
}
|
||||
this.currentConnection = null;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.transaction.support.ResourceHolderSupport#clear()
|
||||
*/
|
||||
@Override
|
||||
public void clear() {
|
||||
super.clear();
|
||||
this.transactionActive = false;
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory;
|
||||
|
||||
import io.r2dbc.spi.Connection;
|
||||
import io.r2dbc.spi.Wrapped;
|
||||
|
||||
/**
|
||||
* Sub interface of {@link Connection} to be implemented by Connection proxies. Allows access to the underlying target
|
||||
* Connection.
|
||||
* <p>
|
||||
* This interface can be checked when there is a need to cast to a native R2DBC {@link Connection}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC. Use R2DBC's {@link Wrapped} mechanism instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface ConnectionProxy extends Connection, Wrapped<Connection> {
|
||||
|
||||
/**
|
||||
* Return the target {@link Connection} of this proxy.
|
||||
* <p>
|
||||
* This will typically be the native driver {@link Connection} or a wrapper from a connection pool.
|
||||
*
|
||||
* @return the underlying Connection (never {@literal null})
|
||||
* @throws IllegalStateException in case the connection has already been closed.
|
||||
*/
|
||||
Connection getTargetConnection();
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory;
|
||||
|
||||
import io.r2dbc.spi.Connection;
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import io.r2dbc.spi.ConnectionFactoryMetadata;
|
||||
import io.r2dbc.spi.Wrapped;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* R2DBC {@link ConnectionFactory} implementation that delegates all calls to a given target {@link ConnectionFactory}.
|
||||
* <p>
|
||||
* This class is meant to be subclassed, with subclasses overriding only those methods (such as {@link #create()}) that
|
||||
* should not simply delegate to the target {@link ConnectionFactory}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @see #create
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC. Use
|
||||
* {@link org.springframework.r2dbc.connection.DelegatingConnectionFactory} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class DelegatingConnectionFactory implements ConnectionFactory, Wrapped<ConnectionFactory> {
|
||||
|
||||
private final ConnectionFactory targetConnectionFactory;
|
||||
|
||||
public DelegatingConnectionFactory(ConnectionFactory targetConnectionFactory) {
|
||||
|
||||
Assert.notNull(targetConnectionFactory, "ConnectionFactory must not be null");
|
||||
this.targetConnectionFactory = targetConnectionFactory;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see io.r2dbc.spi.ConnectionFactory#create()
|
||||
*/
|
||||
@Override
|
||||
public Mono<? extends Connection> create() {
|
||||
return Mono.from(targetConnectionFactory.create());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the target {@link ConnectionFactory} that this {@link ConnectionFactory} should delegate to.
|
||||
*/
|
||||
@Nullable
|
||||
public ConnectionFactory getTargetConnectionFactory() {
|
||||
return this.targetConnectionFactory;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see io.r2dbc.spi.ConnectionFactory#getMetadata()
|
||||
*/
|
||||
@Override
|
||||
public ConnectionFactoryMetadata getMetadata() {
|
||||
return obtainTargetConnectionFactory().getMetadata();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see io.r2dbc.spi.Wrapped#unwrap()
|
||||
*/
|
||||
@Override
|
||||
public ConnectionFactory unwrap() {
|
||||
return obtainTargetConnectionFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the target {@link ConnectionFactory} for actual use (never {@literal null}).
|
||||
*/
|
||||
protected ConnectionFactory obtainTargetConnectionFactory() {
|
||||
return getTargetConnectionFactory();
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory;
|
||||
|
||||
import io.r2dbc.spi.Connection;
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.data.r2dbc.core.DatabaseClient;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.transaction.ReactiveTransactionManager} implementation for a single R2DBC
|
||||
* {@link ConnectionFactory}. This class is capable of working in any environment with any R2DBC driver, as long as the
|
||||
* setup uses a {@link ConnectionFactory} as its {@link Connection} factory mechanism. Binds a R2DBC {@link Connection}
|
||||
* from the specified {@link ConnectionFactory} to the current subscriber context, potentially allowing for one
|
||||
* context-bound {@link Connection} per {@link ConnectionFactory}.
|
||||
* <p>
|
||||
* <b>Note: The {@link ConnectionFactory} that this transaction manager operates on 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 scoped {@link Connection}s or the like. This transaction manager
|
||||
* will associate {@link Connection} with context-bound transactions itself, according to the specified propagation
|
||||
* behavior. It assumes that a separate, independent {@link Connection} can be obtained even during an ongoing
|
||||
* transaction.
|
||||
* <p>
|
||||
* Application code is required to retrieve the R2DBC Connection via
|
||||
* {@link ConnectionFactoryUtils#getConnection(ConnectionFactory)} instead of a standard R2DBC-style
|
||||
* {@link ConnectionFactory#create()} call. Spring classes such as {@link DatabaseClient} use this strategy implicitly.
|
||||
* If not used in combination with this transaction manager, the {@link ConnectionFactoryUtils} lookup strategy behaves
|
||||
* exactly like the native {@link ConnectionFactory} lookup; it can thus be used in a portable fashion.
|
||||
* <p>
|
||||
* Alternatively, you can allow application code to work with the standard R2DBC lookup pattern
|
||||
* {@link ConnectionFactory#create()}, for example for code that is not aware of Spring at all. In that case, define a
|
||||
* {@link TransactionAwareConnectionFactoryProxy} for your target {@link ConnectionFactory}, and pass that proxy
|
||||
* {@link ConnectionFactory} to your DAOs, which will automatically participate in Spring-managed transactions when
|
||||
* accessing it.
|
||||
* <p>
|
||||
* This transaction manager triggers flush callbacks on registered transaction synchronizations (if synchronization is
|
||||
* generally active), assuming resources operating on the underlying R2DBC {@link Connection}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @see ConnectionFactoryUtils#getConnection(ConnectionFactory)
|
||||
* @see ConnectionFactoryUtils#releaseConnection
|
||||
* @see TransactionAwareConnectionFactoryProxy
|
||||
* @see DatabaseClient
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC. Use
|
||||
* {@link org.springframework.r2dbc.connection.R2dbcTransactionManager} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class R2dbcTransactionManager extends org.springframework.r2dbc.connection.R2dbcTransactionManager
|
||||
implements InitializingBean {
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
this();
|
||||
setConnectionFactory(connectionFactory);
|
||||
afterPropertiesSet();
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory;
|
||||
|
||||
import io.r2dbc.spi.Connection;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Simple implementation of the {@link ConnectionHandle} interface, containing a given R2DBC Connection.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC. Use {@link org.springframework.r2dbc.connection} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class SimpleConnectionHandle implements ConnectionHandle {
|
||||
|
||||
private final Connection connection;
|
||||
|
||||
/**
|
||||
* Create a new SimpleConnectionHandle for the given Connection.
|
||||
*
|
||||
* @param connection the R2DBC Connection
|
||||
*/
|
||||
SimpleConnectionHandle(Connection connection) {
|
||||
|
||||
Assert.notNull(connection, "Connection must not be null");
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the specified Connection as-is.
|
||||
*/
|
||||
@Override
|
||||
public Connection getConnection() {
|
||||
return this.connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SimpleConnectionHandle: " + this.connection;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,295 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory;
|
||||
|
||||
import io.r2dbc.spi.Connection;
|
||||
import io.r2dbc.spi.ConnectionFactories;
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import io.r2dbc.spi.ConnectionFactoryMetadata;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Implementation of {@link SmartConnectionFactory} that wraps a single R2DBC Connection which is not closed after use.
|
||||
* Obviously, this is not multi-threading capable.
|
||||
* <p>
|
||||
* Note that at shutdown, someone should close the underlying Connection via the {@code close()} method. Client code
|
||||
* will never call close on the Connection handle if it is SmartDataSource-aware (e.g. uses
|
||||
* {@link ConnectionFactoryUtils#releaseConnection(io.r2dbc.spi.Connection, ConnectionFactory)}).
|
||||
* <p>
|
||||
* If client code will call {@link Connection#close()} in the assumption of a pooled Connection, like when using
|
||||
* persistence tools, set "suppressClose" to "true". This will return a close-suppressing proxy instead of the physical
|
||||
* Connection.
|
||||
* <p>
|
||||
* This is primarily intended for testing. For example, it enables easy testing outside an application server, for code
|
||||
* that expects to work on a {@link ConnectionFactory}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @see #create()
|
||||
* @see io.r2dbc.spi.Connection#close()
|
||||
* @see ConnectionFactoryUtils#releaseConnection(io.r2dbc.spi.Connection, ConnectionFactory)
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC. Use
|
||||
* {@link org.springframework.r2dbc.connection.SingleConnectionFactory} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class SingleConnectionConnectionFactory extends DelegatingConnectionFactory
|
||||
implements SmartConnectionFactory, DisposableBean {
|
||||
|
||||
/** Create a close-suppressing proxy?. */
|
||||
private boolean suppressClose;
|
||||
|
||||
/** Override auto-commit state?. */
|
||||
private @Nullable Boolean autoCommit;
|
||||
|
||||
/** Wrapped Connection. */
|
||||
private final AtomicReference<Connection> target = new AtomicReference<>();
|
||||
|
||||
/** Proxy Connection. */
|
||||
private @Nullable Connection connection;
|
||||
|
||||
private final Mono<? extends Connection> connectionEmitter;
|
||||
|
||||
/**
|
||||
* Constructor for bean-style configuration.
|
||||
*/
|
||||
public SingleConnectionConnectionFactory(ConnectionFactory targetConnectionFactory) {
|
||||
super(targetConnectionFactory);
|
||||
this.connectionEmitter = super.create().cache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link SingleConnectionConnectionFactory} 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}.
|
||||
* @see ConnectionFactories#get(String)
|
||||
*/
|
||||
public SingleConnectionConnectionFactory(String url, boolean suppressClose) {
|
||||
super(ConnectionFactories.get(url));
|
||||
this.suppressClose = suppressClose;
|
||||
this.connectionEmitter = super.create().cache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link SingleConnectionConnectionFactory} 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).
|
||||
*/
|
||||
public SingleConnectionConnectionFactory(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;
|
||||
}
|
||||
});
|
||||
Assert.notNull(target, "Connection must not be null");
|
||||
Assert.notNull(metadata, "ConnectionFactoryMetadata must not be null");
|
||||
this.target.set(target);
|
||||
this.connectionEmitter = Mono.just(target);
|
||||
this.suppressClose = suppressClose;
|
||||
this.connection = (suppressClose ? getCloseSuppressingConnectionProxy(target) : target);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
*/
|
||||
protected boolean isSuppressClose() {
|
||||
return this.suppressClose;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the returned {@link Connection}'s "autoCommit" setting should be overridden.
|
||||
*/
|
||||
public void setAutoCommit(boolean autoCommit) {
|
||||
this.autoCommit = autoCommit;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
protected Boolean getAutoCommitValue() {
|
||||
return this.autoCommit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<? extends Connection> create() {
|
||||
|
||||
Connection connection = this.target.get();
|
||||
|
||||
return connectionEmitter.map(it -> {
|
||||
|
||||
if (connection == null) {
|
||||
this.target.compareAndSet(connection, it);
|
||||
this.connection = (isSuppressClose() ? getCloseSuppressingConnectionProxy(it) : it);
|
||||
}
|
||||
|
||||
return this.connection;
|
||||
}).flatMap(this::prepareConnection);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a single Connection: Do not close it when returning to the "pool".
|
||||
*/
|
||||
@Override
|
||||
public boolean shouldClose(Connection con) {
|
||||
return (con != this.connection && con != this.target.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
resetConnection().block();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @param target the original {@link Connection} to wrap.
|
||||
* @return the wrapped Connection.
|
||||
*/
|
||||
protected Connection getCloseSuppressingConnectionProxy(Connection target) {
|
||||
return (Connection) Proxy.newProxyInstance(ConnectionProxy.class.getClassLoader(),
|
||||
new Class<?>[] { ConnectionProxy.class }, new CloseSuppressingInvocationHandler(target));
|
||||
}
|
||||
|
||||
/**
|
||||
* Invocation handler that suppresses close calls on R2DBC Connections.
|
||||
*
|
||||
* @see io.r2dbc.spi.Connection#close()
|
||||
*/
|
||||
private static class CloseSuppressingInvocationHandler implements InvocationHandler {
|
||||
|
||||
private final io.r2dbc.spi.Connection target;
|
||||
|
||||
CloseSuppressingInvocationHandler(io.r2dbc.spi.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 target;
|
||||
} else if (method.getName().equals("close")) {
|
||||
// Handle close method: suppress, not valid.
|
||||
return Mono.empty();
|
||||
} else if (method.getName().equals("getTargetConnection")) {
|
||||
// Handle getTargetConnection method: return underlying Connection.
|
||||
return this.target;
|
||||
}
|
||||
|
||||
// Invoke method on target Connection.
|
||||
try {
|
||||
Object retVal = method.invoke(this.target, args);
|
||||
|
||||
return retVal;
|
||||
} catch (InvocationTargetException ex) {
|
||||
throw ex.getTargetException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2021 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.data.r2dbc.connectionfactory;
|
||||
|
||||
import io.r2dbc.spi.Connection;
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
|
||||
/**
|
||||
* Extension of the {@code io.r2dbc.spi.ConnectionFactory} interface, to be implemented by special connection factories
|
||||
* that return R2DBC Connections in an unwrapped fashion.
|
||||
* <p>
|
||||
* Classes using this interface can query whether or not the {@link Connection} should be closed after an operation.
|
||||
* Spring's {@link ConnectionFactoryUtils} automatically perform such a check.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @see ConnectionFactoryUtils#closeConnection
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC without replacement.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface SmartConnectionFactory extends ConnectionFactory {
|
||||
|
||||
/**
|
||||
* Should we close this {@link io.r2dbc.spi.Connection}, obtained from this {@code io.r2dbc.spi.ConnectionFactory}?
|
||||
* <p>
|
||||
* Code that uses Connections from a SmartConnectionFactory should always perform a check via this method before
|
||||
* invoking {@code close()}.
|
||||
*
|
||||
* @param connection the {@link io.r2dbc.spi.Connection} to check.
|
||||
* @return whether the given {@link Connection} should be closed.
|
||||
* @see io.r2dbc.spi.Connection#close()
|
||||
*/
|
||||
boolean shouldClose(Connection connection);
|
||||
}
|
||||
@@ -1,198 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory;
|
||||
|
||||
import io.r2dbc.spi.Connection;
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import io.r2dbc.spi.Wrapped;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
|
||||
import org.springframework.data.r2dbc.core.DatabaseClient;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Proxy for a target R2DBC {@link ConnectionFactory}, adding awareness of Spring-managed transactions.
|
||||
* <p>
|
||||
* Data access code that should remain unaware of Spring's data access support can work with this proxy to seamlessly
|
||||
* participate in Spring-managed transactions. Note that the transaction manager, for example
|
||||
* {@link R2dbcTransactionManager}, still needs to work with the underlying {@link ConnectionFactory}, <i>not</i> with
|
||||
* this proxy.
|
||||
* <p>
|
||||
* <b>Make sure that {@link TransactionAwareConnectionFactoryProxy} is the outermost {@link ConnectionFactory} of a
|
||||
* chain of {@link ConnectionFactory} proxies/adapters.</b> {@link TransactionAwareConnectionFactoryProxy} can delegate
|
||||
* either directly to the target connection pool or to some intermediary proxy/adapter.
|
||||
* <p>
|
||||
* Delegates to {@link ConnectionFactoryUtils} for automatically participating in thread-bound transactions, for example
|
||||
* managed by {@link R2dbcTransactionManager}. {@link #create()} calls and {@code close} calls on returned
|
||||
* {@link Connection} will behave properly within a transaction, i.e. always operate on the transactional Connection. If
|
||||
* not within a transaction, normal {@link ConnectionFactory} behavior applies.
|
||||
* <p>
|
||||
* This proxy allows data access code to work with the plain R2DBC API. However, if possible, use Spring's
|
||||
* {@link ConnectionFactoryUtils} or {@link DatabaseClient} to get transaction participation even without a proxy for
|
||||
* the target {@link ConnectionFactory}, avoiding the need to define such a proxy in the first place.
|
||||
* <p>
|
||||
* <b>NOTE:</b> This {@link ConnectionFactory} proxy needs to return wrapped {@link Connection}s (which implement the
|
||||
* {@link ConnectionProxy} interface) in order to handle close calls properly. Use {@link Wrapped#unwrap()} to retrieve
|
||||
* the native R2DBC Connection.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @see ConnectionFactory#create
|
||||
* @see Connection#close
|
||||
* @see ConnectionFactoryUtils#doGetConnection
|
||||
* @see ConnectionFactoryUtils#doReleaseConnection
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC. Use
|
||||
* {@link org.springframework.r2dbc.connection.TransactionAwareConnectionFactoryProxy} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class TransactionAwareConnectionFactoryProxy extends DelegatingConnectionFactory {
|
||||
|
||||
/**
|
||||
* Create a new {@link TransactionAwareConnectionFactoryProxy}.
|
||||
*
|
||||
* @param targetConnectionFactory the target {@link ConnectionFactory}.
|
||||
* @throws IllegalArgumentException if given {@link ConnectionFactory} is {@literal null}.
|
||||
*/
|
||||
public TransactionAwareConnectionFactoryProxy(ConnectionFactory targetConnectionFactory) {
|
||||
super(targetConnectionFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to {@link ConnectionFactoryUtils} for automatically participating in Spring-managed transactions.
|
||||
* <p>
|
||||
* The returned {@link ConnectionFactory} handle implements the {@link ConnectionProxy} interface, allowing to
|
||||
* retrieve the underlying target {@link Connection}.
|
||||
*
|
||||
* @return a transactional {@link Connection} if any, a new one else.
|
||||
* @see ConnectionFactoryUtils#doGetConnection
|
||||
* @see ConnectionProxy#getTargetConnection
|
||||
*/
|
||||
@Override
|
||||
public Mono<Connection> create() {
|
||||
return getTransactionAwareConnectionProxy(obtainTargetConnectionFactory());
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the given {@link Connection} with a proxy that delegates every method call to it but delegates
|
||||
* {@code close()} calls to {@link ConnectionFactoryUtils}.
|
||||
*
|
||||
* @param targetConnectionFactory the {@link ConnectionFactory} that the {@link Connection} came from.
|
||||
* @return the wrapped {@link Connection}.
|
||||
* @see Connection#close()
|
||||
* @see ConnectionFactoryUtils#doReleaseConnection
|
||||
*/
|
||||
protected Mono<Connection> getTransactionAwareConnectionProxy(ConnectionFactory targetConnectionFactory) {
|
||||
return ConnectionFactoryUtils.getConnection(targetConnectionFactory)
|
||||
.map(it -> proxyConnection(it, targetConnectionFactory));
|
||||
}
|
||||
|
||||
private static Connection proxyConnection(Connection connection, ConnectionFactory targetConnectionFactory) {
|
||||
|
||||
return (Connection) Proxy.newProxyInstance(ConnectionProxy.class.getClassLoader(),
|
||||
new Class<?>[] { ConnectionProxy.class },
|
||||
new TransactionAwareInvocationHandler(connection, targetConnectionFactory));
|
||||
}
|
||||
|
||||
/**
|
||||
* Invocation handler that delegates close calls on R2DBC Connections to {@link ConnectionFactoryUtils} for being
|
||||
* aware of context-bound transactions.
|
||||
*/
|
||||
private static class TransactionAwareInvocationHandler implements InvocationHandler {
|
||||
|
||||
private final Connection connection;
|
||||
|
||||
private final ConnectionFactory targetConnectionFactory;
|
||||
|
||||
private boolean closed = false;
|
||||
|
||||
TransactionAwareInvocationHandler(Connection connection, ConnectionFactory targetConnectionFactory) {
|
||||
|
||||
this.connection = connection;
|
||||
this.targetConnectionFactory = targetConnectionFactory;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
|
||||
*/
|
||||
@Override
|
||||
@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":
|
||||
// Handle close method: only close if not within a transaction.
|
||||
return ConnectionFactoryUtils.doReleaseConnection(this.connection, this.targetConnectionFactory)
|
||||
.doOnSubscribe(n -> this.closed = true);
|
||||
case "isClosed":
|
||||
return this.closed;
|
||||
}
|
||||
|
||||
if (this.closed) {
|
||||
throw new IllegalStateException("Connection handle already closed");
|
||||
}
|
||||
|
||||
if (method.getName().equals("getTargetConnection")) {
|
||||
// Handle getTargetConnection method: return underlying Connection.
|
||||
return this.connection;
|
||||
}
|
||||
|
||||
// Invoke method on target Connection.
|
||||
try {
|
||||
return method.invoke(this.connection, args);
|
||||
} catch (InvocationTargetException ex) {
|
||||
throw ex.getTargetException();
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory.init;
|
||||
|
||||
import org.springframework.core.io.support.EncodedResource;
|
||||
|
||||
/**
|
||||
* Thrown by {@link ScriptUtils} if an SQL script cannot be read.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC. Use
|
||||
* {@link org.springframework.r2dbc.connection.init.CannotReadScriptException} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class CannotReadScriptException extends ScriptException {
|
||||
|
||||
private static final long serialVersionUID = 7253084944991764250L;
|
||||
|
||||
/**
|
||||
* Creates a new {@link CannotReadScriptException}.
|
||||
*
|
||||
* @param resource the resource that cannot be read from.
|
||||
* @param cause the underlying cause of the resource access failure.
|
||||
*/
|
||||
public CannotReadScriptException(EncodedResource resource, Throwable cause) {
|
||||
super("Cannot read SQL script from " + resource, cause);
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory.init;
|
||||
|
||||
import io.r2dbc.spi.Connection;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Composite {@link DatabasePopulator} that delegates to a list of given {@link DatabasePopulator} implementations,
|
||||
* executing all scripts.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC. Use
|
||||
* {@link org.springframework.r2dbc.connection.init.CompositeDatabasePopulator} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class CompositeDatabasePopulator implements DatabasePopulator {
|
||||
|
||||
private final List<DatabasePopulator> populators = new ArrayList<>(4);
|
||||
|
||||
/**
|
||||
* Creates an empty {@link CompositeDatabasePopulator}.
|
||||
*
|
||||
* @see #setPopulators
|
||||
* @see #addPopulators
|
||||
*/
|
||||
public CompositeDatabasePopulator() {}
|
||||
|
||||
/**
|
||||
* Creates a {@link CompositeDatabasePopulator}. with the given populators.
|
||||
*
|
||||
* @param populators one or more populators to delegate to.
|
||||
*/
|
||||
public CompositeDatabasePopulator(Collection<DatabasePopulator> populators) {
|
||||
|
||||
Assert.notNull(populators, "Collection of DatabasePopulator must not be null!");
|
||||
|
||||
this.populators.addAll(populators);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link CompositeDatabasePopulator} with the given populators.
|
||||
*
|
||||
* @param populators one or more populators to delegate to.
|
||||
*/
|
||||
public CompositeDatabasePopulator(DatabasePopulator... populators) {
|
||||
|
||||
Assert.notNull(populators, "DatabasePopulators must not be null!");
|
||||
|
||||
this.populators.addAll(Arrays.asList(populators));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify one or more populators to delegate to.
|
||||
*/
|
||||
public void setPopulators(DatabasePopulator... populators) {
|
||||
|
||||
Assert.notNull(populators, "DatabasePopulators must not be null!");
|
||||
|
||||
this.populators.clear();
|
||||
this.populators.addAll(Arrays.asList(populators));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add one or more populators to the list of delegates.
|
||||
*/
|
||||
public void addPopulators(DatabasePopulator... populators) {
|
||||
|
||||
Assert.notNull(populators, "DatabasePopulators must not be null!");
|
||||
|
||||
this.populators.addAll(Arrays.asList(populators));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> populate(Connection connection) throws ScriptException {
|
||||
|
||||
Assert.notNull(connection, "Connection must not be null!");
|
||||
|
||||
return Flux.fromIterable(this.populators).concatMap(it -> it.populate(connection)).then();
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory.init;
|
||||
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Used to {@link #setDatabasePopulator set up} a database during initialization and {@link #setDatabaseCleaner clean
|
||||
* up} a database during destruction.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @see DatabasePopulator
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC. Use
|
||||
* {@link org.springframework.r2dbc.connection.init.ConnectionFactoryInitializer} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class ConnectionFactoryInitializer implements InitializingBean, DisposableBean {
|
||||
|
||||
private @Nullable ConnectionFactory connectionFactory;
|
||||
|
||||
private @Nullable DatabasePopulator databasePopulator;
|
||||
|
||||
private @Nullable DatabasePopulator databaseCleaner;
|
||||
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* The {@link ConnectionFactory} for the database to populate when this component is initialized and to clean up when
|
||||
* this component is shut down.
|
||||
* <p>
|
||||
* This property is mandatory with no default provided.
|
||||
*
|
||||
* @param connectionFactory the R2DBC {@link ConnectionFactory}.
|
||||
*/
|
||||
public void setConnectionFactory(ConnectionFactory connectionFactory) {
|
||||
this.connectionFactory = connectionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link DatabasePopulator} to execute during the bean initialization phase.
|
||||
*
|
||||
* @param databasePopulator the {@link DatabasePopulator} to use during initialization
|
||||
* @see #setDatabaseCleaner
|
||||
*/
|
||||
public void setDatabasePopulator(DatabasePopulator databasePopulator) {
|
||||
this.databasePopulator = databasePopulator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link DatabasePopulator} to execute during the bean destruction phase, cleaning up the database and
|
||||
* leaving it in a known state for others.
|
||||
*
|
||||
* @param databaseCleaner the {@link DatabasePopulator} to use during destruction
|
||||
* @see #setDatabasePopulator
|
||||
*/
|
||||
public void setDatabaseCleaner(DatabasePopulator databaseCleaner) {
|
||||
this.databaseCleaner = databaseCleaner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag to explicitly enable or disable the {@link #setDatabasePopulator database populator} and
|
||||
* {@link #setDatabaseCleaner database cleaner}.
|
||||
*
|
||||
* @param enabled {@literal true} if the database populator and database cleaner should be called on startup and
|
||||
* shutdown, respectively
|
||||
*/
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the {@link #setDatabasePopulator database populator} to set up the database.
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
execute(this.databasePopulator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the {@link #setDatabaseCleaner database cleaner} to clean up the database.
|
||||
*/
|
||||
@Override
|
||||
public void destroy() {
|
||||
execute(this.databaseCleaner);
|
||||
}
|
||||
|
||||
private void execute(@Nullable DatabasePopulator populator) {
|
||||
|
||||
Assert.state(this.connectionFactory != null, "ConnectionFactory must be set");
|
||||
|
||||
if (this.enabled && populator != null) {
|
||||
DatabasePopulatorUtils.execute(populator, this.connectionFactory).block();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory.init;
|
||||
|
||||
import io.r2dbc.spi.Connection;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* Strategy used to populate, initialize, or clean up a database.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @see ResourceDatabasePopulator
|
||||
* @see DatabasePopulatorUtils
|
||||
* @see ConnectionFactoryInitializer
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC. Use
|
||||
* {@link org.springframework.r2dbc.connection.init.DatabasePopulator} instead.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
@Deprecated
|
||||
public interface DatabasePopulator {
|
||||
|
||||
/**
|
||||
* Populate, initialize, or clean up the database using the provided R2DBC {@link Connection}.
|
||||
*
|
||||
* @param connection the R2DBC connection to use to populate the db; already configured and ready to use, must not be
|
||||
* {@literal null}.
|
||||
* @return {@link Mono} that initiates script execution and is notified upon completion.
|
||||
* @throws ScriptException in all other error cases
|
||||
* @see DatabasePopulatorUtils#execute
|
||||
*/
|
||||
Mono<Void> populate(Connection connection) throws ScriptException;
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory.init;
|
||||
|
||||
import io.r2dbc.spi.Connection;
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.r2dbc.connectionfactory.ConnectionFactoryUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Utility methods for executing a {@link DatabasePopulator}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC. Use
|
||||
* {@link org.springframework.r2dbc.connection.init.DatabasePopulator#populate(ConnectionFactory)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class DatabasePopulatorUtils {
|
||||
|
||||
// utility constructor
|
||||
private DatabasePopulatorUtils() {}
|
||||
|
||||
/**
|
||||
* Execute the given {@link DatabasePopulator} against the given {@link io.r2dbc.spi.ConnectionFactory}.
|
||||
*
|
||||
* @param populator the {@link DatabasePopulator} to execute.
|
||||
* @param connectionFactory the {@link ConnectionFactory} to execute against.
|
||||
* @return {@link Mono} that initiates {@link DatabasePopulator#populate(Connection)} and is notified upon completion.
|
||||
*/
|
||||
public static Mono<Void> execute(DatabasePopulator populator, ConnectionFactory connectionFactory)
|
||||
throws DataAccessException {
|
||||
|
||||
Assert.notNull(populator, "DatabasePopulator must not be null");
|
||||
Assert.notNull(connectionFactory, "ConnectionFactory must not be null");
|
||||
|
||||
return Mono.usingWhen(ConnectionFactoryUtils.getConnection(connectionFactory), //
|
||||
populator::populate, //
|
||||
it -> ConnectionFactoryUtils.releaseConnection(it, connectionFactory), //
|
||||
(it, err) -> ConnectionFactoryUtils.releaseConnection(it, connectionFactory),
|
||||
it -> ConnectionFactoryUtils.releaseConnection(it, connectionFactory))
|
||||
.onErrorMap(ex -> !(ex instanceof ScriptException), ex -> {
|
||||
return new UncategorizedScriptException("Failed to execute database script", ex);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,283 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory.init;
|
||||
|
||||
import io.r2dbc.spi.Connection;
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.core.io.support.EncodedResource;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Populates, initializes, or cleans up a database using SQL scripts defined in external resources.
|
||||
* <ul>
|
||||
* <li>Call {@link #addScript} to add a single SQL script location.
|
||||
* <li>Call {@link #addScripts} to add multiple SQL script locations.
|
||||
* <li>Consult the setter methods in this class for further configuration options.
|
||||
* <li>Call {@link #populate} or {@link #execute} to initialize or clean up the database using the configured scripts.
|
||||
* </ul>
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @see DatabasePopulatorUtils
|
||||
* @see ScriptUtils
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC. Use
|
||||
* {@link org.springframework.r2dbc.connection.init.ResourceDatabasePopulator} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class ResourceDatabasePopulator implements DatabasePopulator {
|
||||
|
||||
List<Resource> scripts = new ArrayList<>();
|
||||
|
||||
private @Nullable Charset sqlScriptEncoding;
|
||||
|
||||
private String separator = ScriptUtils.DEFAULT_STATEMENT_SEPARATOR;
|
||||
|
||||
private String commentPrefix = ScriptUtils.DEFAULT_COMMENT_PREFIX;
|
||||
|
||||
private String blockCommentStartDelimiter = ScriptUtils.DEFAULT_BLOCK_COMMENT_START_DELIMITER;
|
||||
|
||||
private String blockCommentEndDelimiter = ScriptUtils.DEFAULT_BLOCK_COMMENT_END_DELIMITER;
|
||||
|
||||
private boolean continueOnError = false;
|
||||
|
||||
private boolean ignoreFailedDrops = false;
|
||||
|
||||
private DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
|
||||
|
||||
/**
|
||||
* Creates a new {@link ResourceDatabasePopulator} with default settings.
|
||||
*/
|
||||
public ResourceDatabasePopulator() {}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ResourceDatabasePopulator} with default settings for the supplied scripts.
|
||||
*
|
||||
* @param scripts the scripts to execute to initialize or clean up the database (never {@literal null})
|
||||
*/
|
||||
public ResourceDatabasePopulator(Resource... scripts) {
|
||||
setScripts(scripts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ResourceDatabasePopulator} with the supplied values.
|
||||
*
|
||||
* @param continueOnError flag to indicate that all failures in SQL should be logged but not cause a failure
|
||||
* @param ignoreFailedDrops flag to indicate that a failed SQL {@code DROP} statement can be ignored
|
||||
* @param sqlScriptEncoding the encoding for the supplied SQL scripts (may be {@literal null} or <em>empty</em> to
|
||||
* indicate platform encoding)
|
||||
* @param scripts the scripts to execute to initialize or clean up the database, must not be {@literal null}.
|
||||
*/
|
||||
public ResourceDatabasePopulator(boolean continueOnError, boolean ignoreFailedDrops,
|
||||
@Nullable String sqlScriptEncoding, Resource... scripts) {
|
||||
|
||||
this.continueOnError = continueOnError;
|
||||
this.ignoreFailedDrops = ignoreFailedDrops;
|
||||
setSqlScriptEncoding(sqlScriptEncoding);
|
||||
setScripts(scripts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a script to execute to initialize or clean up the database.
|
||||
*
|
||||
* @param script the path to an SQL script, must not be {@literal null}.
|
||||
*/
|
||||
public void addScript(Resource script) {
|
||||
Assert.notNull(script, "Script must not be null");
|
||||
this.scripts.add(script);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add multiple scripts to execute to initialize or clean up the database.
|
||||
*
|
||||
* @param scripts the scripts to execute, must not be {@literal null}.
|
||||
*/
|
||||
public void addScripts(Resource... scripts) {
|
||||
assertContentsOfScriptArray(scripts);
|
||||
this.scripts.addAll(Arrays.asList(scripts));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the scripts to execute to initialize or clean up the database, replacing any previously added scripts.
|
||||
*
|
||||
* @param scripts the scripts to execute, must not be {@literal null}.
|
||||
*/
|
||||
public void setScripts(Resource... scripts) {
|
||||
assertContentsOfScriptArray(scripts);
|
||||
// Ensure that the list is modifiable
|
||||
this.scripts = new ArrayList<>(Arrays.asList(scripts));
|
||||
}
|
||||
|
||||
private void assertContentsOfScriptArray(Resource... scripts) {
|
||||
Assert.notNull(scripts, "Scripts array must not be null");
|
||||
Assert.noNullElements(scripts, "Scripts array must not contain null elements");
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the encoding for the configured SQL scripts, if different from the platform encoding.
|
||||
*
|
||||
* @param sqlScriptEncoding the encoding used in scripts (may be {@literal null} or empty to indicate platform
|
||||
* encoding).
|
||||
* @see #addScript(Resource)
|
||||
*/
|
||||
public void setSqlScriptEncoding(@Nullable String sqlScriptEncoding) {
|
||||
setSqlScriptEncoding(StringUtils.hasText(sqlScriptEncoding) ? Charset.forName(sqlScriptEncoding) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the encoding for the configured SQL scripts, if different from the platform encoding.
|
||||
*
|
||||
* @param sqlScriptEncoding the encoding used in scripts (may be {@literal null} to indicate platform encoding).
|
||||
* @see #addScript(Resource)
|
||||
*/
|
||||
public void setSqlScriptEncoding(@Nullable Charset sqlScriptEncoding) {
|
||||
this.sqlScriptEncoding = sqlScriptEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the statement separator, if a custom one.
|
||||
* <p>
|
||||
* Defaults to {@code ";"} if not specified and falls back to {@code "\n"} as a last resort; may be set to
|
||||
* {@link ScriptUtils#EOF_STATEMENT_SEPARATOR} to signal that each script contains a single statement without a
|
||||
* separator.
|
||||
*
|
||||
* @param separator the script statement separator.
|
||||
*/
|
||||
public void setSeparator(String separator) {
|
||||
this.separator = separator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the prefix that identifies single-line comments within the SQL scripts.
|
||||
* <p>
|
||||
* Defaults to {@code "--"}.
|
||||
*
|
||||
* @param commentPrefix the prefix for single-line comments
|
||||
*/
|
||||
public void setCommentPrefix(String commentPrefix) {
|
||||
this.commentPrefix = commentPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the start delimiter that identifies block comments within the SQL scripts.
|
||||
* <p>
|
||||
* Defaults to {@code "/*"}.
|
||||
*
|
||||
* @param blockCommentStartDelimiter the start delimiter for block comments (never {@literal null} or empty).
|
||||
* @see #setBlockCommentEndDelimiter
|
||||
*/
|
||||
public void setBlockCommentStartDelimiter(String blockCommentStartDelimiter) {
|
||||
|
||||
Assert.hasText(blockCommentStartDelimiter, "BlockCommentStartDelimiter must not be null or empty");
|
||||
|
||||
this.blockCommentStartDelimiter = blockCommentStartDelimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the end delimiter that identifies block comments within the SQL scripts.
|
||||
* <p>
|
||||
* Defaults to {@code "*/"}.
|
||||
*
|
||||
* @param blockCommentEndDelimiter the end delimiter for block comments (never {@literal null} or empty)
|
||||
* @see #setBlockCommentStartDelimiter
|
||||
*/
|
||||
public void setBlockCommentEndDelimiter(String blockCommentEndDelimiter) {
|
||||
|
||||
Assert.hasText(blockCommentEndDelimiter, "BlockCommentEndDelimiter must not be null or empty");
|
||||
|
||||
this.blockCommentEndDelimiter = blockCommentEndDelimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag to indicate that all failures in SQL should be logged but not cause a failure.
|
||||
* <p>
|
||||
* Defaults to {@literal false}.
|
||||
*
|
||||
* @param continueOnError {@literal true} if script execution should continue on error.
|
||||
*/
|
||||
public void setContinueOnError(boolean continueOnError) {
|
||||
this.continueOnError = continueOnError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag to indicate that a failed SQL {@code DROP} statement can be ignored.
|
||||
* <p>
|
||||
* This is useful for a non-embedded database whose SQL dialect does not support an {@code IF EXISTS} clause in a
|
||||
* {@code DROP} statement.
|
||||
* <p>
|
||||
* The default is {@literal false} so that if the populator runs accidentally, it will fail fast if a script starts
|
||||
* with a {@code DROP} statement.
|
||||
*
|
||||
* @param ignoreFailedDrops {@literal true} if failed drop statements should be ignored.
|
||||
*/
|
||||
public void setIgnoreFailedDrops(boolean ignoreFailedDrops) {
|
||||
this.ignoreFailedDrops = ignoreFailedDrops;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link DataBufferFactory} to use for {@link Resource} loading.
|
||||
* <p>
|
||||
* Defaults to {@link DefaultDataBufferFactory}.
|
||||
*
|
||||
* @param dataBufferFactory the {@link DataBufferFactory} to use, must not be {@literal null}.
|
||||
*/
|
||||
public void setDataBufferFactory(DataBufferFactory dataBufferFactory) {
|
||||
|
||||
Assert.notNull(dataBufferFactory, "DataBufferFactory must not be null!");
|
||||
|
||||
this.dataBufferFactory = dataBufferFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> populate(Connection connection) throws ScriptException {
|
||||
|
||||
Assert.notNull(connection, "Connection must not be null");
|
||||
|
||||
return Flux.fromIterable(this.scripts).concatMap(it -> {
|
||||
|
||||
EncodedResource encodedScript = new EncodedResource(it, this.sqlScriptEncoding);
|
||||
|
||||
return ScriptUtils.executeSqlScript(connection, encodedScript, this.dataBufferFactory, this.continueOnError,
|
||||
this.ignoreFailedDrops, this.commentPrefix, this.separator, this.blockCommentStartDelimiter,
|
||||
this.blockCommentEndDelimiter);
|
||||
}).then();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute this {@link ResourceDatabasePopulator} against the given {@link ConnectionFactory}.
|
||||
* <p>
|
||||
* Delegates to {@link DatabasePopulatorUtils#execute}.
|
||||
*
|
||||
* @param connectionFactory the {@link ConnectionFactory} to execute against, must not be {@literal null}..
|
||||
* @return {@link Mono} tthat initiates script execution and is notified upon completion.
|
||||
* @throws ScriptException if an error occurs.
|
||||
* @see #populate(Connection)
|
||||
*/
|
||||
public Mono<Void> execute(ConnectionFactory connectionFactory) throws ScriptException {
|
||||
return DatabasePopulatorUtils.execute(this, connectionFactory);
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory.init;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Root of the hierarchy of data access exceptions that are related to processing of SQL scripts.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC. Use {@link org.springframework.r2dbc.connection.init.ScriptException}
|
||||
* instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class ScriptException extends DataAccessException {
|
||||
|
||||
/**
|
||||
* Creates a new {@link ScriptException}.
|
||||
*
|
||||
* @param message the detail message.
|
||||
*/
|
||||
public ScriptException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ScriptException}.
|
||||
*
|
||||
* @param message the detail message.
|
||||
* @param cause the root cause.
|
||||
*/
|
||||
public ScriptException(String message, @Nullable Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory.init;
|
||||
|
||||
import org.springframework.core.io.support.EncodedResource;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Thrown by {@link ScriptUtils} if an SQL script cannot be properly parsed.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC. Use
|
||||
* {@link org.springframework.r2dbc.connection.init.ScriptParseException} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class ScriptParseException extends ScriptException {
|
||||
|
||||
private static final long serialVersionUID = 6130513243627087332L;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ScriptParseException}.
|
||||
*
|
||||
* @param message detailed message.
|
||||
* @param resource the resource from which the SQL script was read.
|
||||
*/
|
||||
public ScriptParseException(String message, @Nullable EncodedResource resource) {
|
||||
super(buildMessage(message, resource));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ScriptParseException}.
|
||||
*
|
||||
* @param message detailed message.
|
||||
* @param resource the resource from which the SQL script was read.
|
||||
* @param cause the underlying cause of the failure.
|
||||
*/
|
||||
public ScriptParseException(String message, @Nullable EncodedResource resource, @Nullable Throwable cause) {
|
||||
super(buildMessage(message, resource), cause);
|
||||
}
|
||||
|
||||
private static String buildMessage(String message, @Nullable EncodedResource resource) {
|
||||
return String.format("Failed to parse SQL script from resource [%s]: %s",
|
||||
(resource == null ? "<unknown>" : resource), message);
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory.init;
|
||||
|
||||
import org.springframework.core.io.support.EncodedResource;
|
||||
|
||||
/**
|
||||
* Thrown by {@link ScriptUtils} if a statement in an SQL script failed when executing it against the target database.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC. Use
|
||||
* {@link org.springframework.r2dbc.connection.init.ScriptStatementFailedException} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class ScriptStatementFailedException extends ScriptException {
|
||||
|
||||
private static final long serialVersionUID = 912676424615782262L;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ScriptStatementFailedException}.
|
||||
*
|
||||
* @param statement the actual SQL statement that failed.
|
||||
* @param statementNumber the statement number in the SQL script (i.e., the n'th statement present in the resource).
|
||||
* @param encodedResource the resource from which the SQL statement was read.
|
||||
* @param cause the underlying cause of the failure.
|
||||
*/
|
||||
public ScriptStatementFailedException(String statement, int statementNumber, EncodedResource encodedResource,
|
||||
Throwable cause) {
|
||||
super(buildErrorMessage(statement, statementNumber, encodedResource), cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an error message for an SQL script execution failure, based on the supplied arguments.
|
||||
*
|
||||
* @param statement the actual SQL statement that failed.
|
||||
* @param statementNumber the statement number in the SQL script (i.e., the n'th statement present in the resource).
|
||||
* @param encodedResource the resource from which the SQL statement was read.
|
||||
* @return an error message suitable for an exception's detail message or logging.
|
||||
*/
|
||||
public static String buildErrorMessage(String statement, int statementNumber, EncodedResource encodedResource) {
|
||||
return String.format("Failed to execute SQL script statement #%s of %s: %s", statementNumber, encodedResource,
|
||||
statement);
|
||||
}
|
||||
}
|
||||
@@ -1,540 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory.init;
|
||||
|
||||
import io.r2dbc.spi.Connection;
|
||||
import io.r2dbc.spi.Result;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.LineNumberReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.core.io.support.EncodedResource;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Generic utility methods for working with SQL scripts.
|
||||
* <p>
|
||||
* Mainly for internal use within the framework.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC. Use {@link org.springframework.r2dbc.connection.init.ScriptUtils}
|
||||
* instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class ScriptUtils {
|
||||
|
||||
/**
|
||||
* Default statement separator within SQL scripts: {@code ";"}.
|
||||
*/
|
||||
public static final String DEFAULT_STATEMENT_SEPARATOR = ";";
|
||||
|
||||
/**
|
||||
* Fallback statement separator within SQL scripts: {@code "\n"}.
|
||||
* <p>
|
||||
* Used if neither a custom separator nor the {@link #DEFAULT_STATEMENT_SEPARATOR} is present in a given script.
|
||||
*/
|
||||
public static final String FALLBACK_STATEMENT_SEPARATOR = "\n";
|
||||
|
||||
/**
|
||||
* End of file (EOF) SQL statement separator: {@code "^^^ END OF SCRIPT ^^^"}.
|
||||
* <p>
|
||||
* This value may be supplied as the {@code separator} to
|
||||
* {@link #executeSqlScript(Connection, EncodedResource, DataBufferFactory, boolean, boolean, String, String, String, String)}
|
||||
* to denote that an SQL script contains a single statement (potentially spanning multiple lines) with no explicit
|
||||
* statement separator. Note that such a script should not actually contain this value; it is merely a
|
||||
* <em>virtual</em> statement separator.
|
||||
*/
|
||||
public static final String EOF_STATEMENT_SEPARATOR = "^^^ END OF SCRIPT ^^^";
|
||||
|
||||
/**
|
||||
* Default prefix for single-line comments within SQL scripts: {@code "--"}.
|
||||
*/
|
||||
public static final String DEFAULT_COMMENT_PREFIX = "--";
|
||||
|
||||
/**
|
||||
* Default start delimiter for block comments within SQL scripts: {@code "/*"}.
|
||||
*/
|
||||
public static final String DEFAULT_BLOCK_COMMENT_START_DELIMITER = "/*";
|
||||
|
||||
/**
|
||||
* Default end delimiter for block comments within SQL scripts: <code>"*/"</code>.
|
||||
*/
|
||||
public static final String DEFAULT_BLOCK_COMMENT_END_DELIMITER = "*/";
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ScriptUtils.class);
|
||||
|
||||
// utility constructor
|
||||
private ScriptUtils() {}
|
||||
|
||||
/**
|
||||
* Split an SQL script into separate statements delimited by the provided separator character. Each individual
|
||||
* statement will be added to the provided {@link List}.
|
||||
* <p>
|
||||
* Within the script, {@value #DEFAULT_COMMENT_PREFIX} will be used as the comment prefix; any text beginning with the
|
||||
* comment prefix and extending to the end of the line will be omitted from the output. Similarly,
|
||||
* {@value #DEFAULT_BLOCK_COMMENT_START_DELIMITER} and {@value #DEFAULT_BLOCK_COMMENT_END_DELIMITER} will be used as
|
||||
* the <em>start</em> and <em>end</em> block comment delimiters: any text enclosed in a block comment will be omitted
|
||||
* from the output. In addition, multiple adjacent whitespace characters will be collapsed into a single space.
|
||||
*
|
||||
* @param script the SQL script.
|
||||
* @param separator character separating each statement (typically a ';').
|
||||
* @param statements the list that will contain the individual statements .
|
||||
* @throws ScriptException if an error occurred while splitting the SQL script.
|
||||
* @see #splitSqlScript(String, String, List)
|
||||
* @see #splitSqlScript(EncodedResource, String, String, String, String, String, List)
|
||||
*/
|
||||
static void splitSqlScript(String script, char separator, List<String> statements) throws ScriptException {
|
||||
splitSqlScript(script, String.valueOf(separator), statements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Split an SQL script into separate statements delimited by the provided separator string. Each individual statement
|
||||
* will be added to the provided {@link List}.
|
||||
* <p>
|
||||
* Within the script, {@value #DEFAULT_COMMENT_PREFIX} will be used as the comment prefix; any text beginning with the
|
||||
* comment prefix and extending to the end of the line will be omitted from the output. Similarly,
|
||||
* {@value #DEFAULT_BLOCK_COMMENT_START_DELIMITER} and {@value #DEFAULT_BLOCK_COMMENT_END_DELIMITER} will be used as
|
||||
* the <em>start</em> and <em>end</em> block comment delimiters: any text enclosed in a block comment will be omitted
|
||||
* from the output. In addition, multiple adjacent whitespace characters will be collapsed into a single space.
|
||||
*
|
||||
* @param script the SQL script.
|
||||
* @param separator text separating each statement (typically a ';' or newline character).
|
||||
* @param statements the list that will contain the individual statements.
|
||||
* @throws ScriptException if an error occurred while splitting the SQL script.
|
||||
* @see #splitSqlScript(String, char, List)
|
||||
* @see #splitSqlScript(EncodedResource, String, String, String, String, String, List)
|
||||
*/
|
||||
static void splitSqlScript(String script, String separator, List<String> statements) throws ScriptException {
|
||||
splitSqlScript(null, script, separator, DEFAULT_COMMENT_PREFIX, DEFAULT_BLOCK_COMMENT_START_DELIMITER,
|
||||
DEFAULT_BLOCK_COMMENT_END_DELIMITER, statements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Split an SQL script into separate statements delimited by the provided separator string. Each individual statement
|
||||
* will be added to the provided {@link List}.
|
||||
* <p>
|
||||
* Within the script, the provided {@code commentPrefix} will be honored: any text beginning with the comment prefix
|
||||
* and extending to the end of the line will be omitted from the output. Similarly, the provided
|
||||
* {@code blockCommentStartDelimiter} and {@code blockCommentEndDelimiter} delimiters will be honored: any text
|
||||
* enclosed in a block comment will be omitted from the output. In addition, multiple adjacent whitespace characters
|
||||
* will be collapsed into a single space.
|
||||
*
|
||||
* @param resource the resource from which the script was read.
|
||||
* @param script the SQL script.
|
||||
* @param separator text separating each statement (typically a ';' or newline character).
|
||||
* @param commentPrefix the prefix that identifies SQL line comments (typically "--").
|
||||
* @param blockCommentStartDelimiter the <em>start</em> block comment delimiter. Must not be {@literal null} or empty.
|
||||
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter. Must not be {@literal null} or empty.
|
||||
* @param statements the list that will contain the individual statements.
|
||||
* @throws ScriptException if an error occurred while splitting the SQL script.
|
||||
*/
|
||||
private static void splitSqlScript(@Nullable EncodedResource resource, String script, String separator,
|
||||
String commentPrefix, String blockCommentStartDelimiter, String blockCommentEndDelimiter, List<String> statements)
|
||||
throws ScriptException {
|
||||
|
||||
Assert.hasText(script, "'script' must not be null or empty");
|
||||
Assert.notNull(separator, "'separator' must not be null");
|
||||
Assert.hasText(commentPrefix, "'commentPrefix' must not be null or empty");
|
||||
Assert.hasText(blockCommentStartDelimiter, "'blockCommentStartDelimiter' must not be null or empty");
|
||||
Assert.hasText(blockCommentEndDelimiter, "'blockCommentEndDelimiter' must not be null or empty");
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
boolean inSingleQuote = false;
|
||||
boolean inDoubleQuote = false;
|
||||
boolean inEscape = false;
|
||||
|
||||
for (int i = 0; i < script.length(); i++) {
|
||||
char c = script.charAt(i);
|
||||
if (inEscape) {
|
||||
inEscape = false;
|
||||
sb.append(c);
|
||||
continue;
|
||||
}
|
||||
// MySQL style escapes
|
||||
if (c == '\\') {
|
||||
inEscape = true;
|
||||
sb.append(c);
|
||||
continue;
|
||||
}
|
||||
if (!inDoubleQuote && (c == '\'')) {
|
||||
inSingleQuote = !inSingleQuote;
|
||||
} else if (!inSingleQuote && (c == '"')) {
|
||||
inDoubleQuote = !inDoubleQuote;
|
||||
}
|
||||
if (!inSingleQuote && !inDoubleQuote) {
|
||||
if (script.startsWith(separator, i)) {
|
||||
// We've reached the end of the current statement
|
||||
if (sb.length() > 0) {
|
||||
statements.add(sb.toString());
|
||||
sb = new StringBuilder();
|
||||
}
|
||||
i += separator.length() - 1;
|
||||
continue;
|
||||
} else if (script.startsWith(commentPrefix, i)) {
|
||||
// Skip over any content from the start of the comment to the EOL
|
||||
int indexOfNextNewline = script.indexOf('\n', i);
|
||||
if (indexOfNextNewline > i) {
|
||||
i = indexOfNextNewline;
|
||||
continue;
|
||||
} else {
|
||||
// If there's no EOL, we must be at the end of the script, so stop here.
|
||||
break;
|
||||
}
|
||||
} else if (script.startsWith(blockCommentStartDelimiter, i)) {
|
||||
// Skip over any block comments
|
||||
int indexOfCommentEnd = script.indexOf(blockCommentEndDelimiter, i);
|
||||
if (indexOfCommentEnd > i) {
|
||||
i = indexOfCommentEnd + blockCommentEndDelimiter.length() - 1;
|
||||
continue;
|
||||
} else {
|
||||
throw new ScriptParseException("Missing block comment end delimiter: " + blockCommentEndDelimiter,
|
||||
resource);
|
||||
}
|
||||
} else if (c == ' ' || c == '\r' || c == '\n' || c == '\t') {
|
||||
// Avoid multiple adjacent whitespace characters
|
||||
if (sb.length() > 0 && sb.charAt(sb.length() - 1) != ' ') {
|
||||
c = ' ';
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(sb)) {
|
||||
statements.add(sb.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a script without blocking from the given resource, using "{@code --}" as the comment prefix and "{@code ;}" as
|
||||
* the statement separator, and build a String containing the lines.
|
||||
*
|
||||
* @param resource the {@link EncodedResource} to be read.
|
||||
* @param dataBufferFactory the buffer factory for non-blocking script loading.
|
||||
* @return {@link String} containing the script lines.
|
||||
* @see DefaultDataBufferFactory
|
||||
*/
|
||||
public static Mono<String> readScript(EncodedResource resource, DataBufferFactory dataBufferFactory) {
|
||||
return readScript(resource, dataBufferFactory, DEFAULT_COMMENT_PREFIX, DEFAULT_STATEMENT_SEPARATOR,
|
||||
DEFAULT_BLOCK_COMMENT_END_DELIMITER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a script without blocking from the provided resource, using the supplied comment prefix and statement
|
||||
* separator, and build a {@link String} and build a String containing the lines.
|
||||
* <p>
|
||||
* Lines <em>beginning</em> with the comment prefix are excluded from the results; however, line comments anywhere
|
||||
* else — for example, within a statement — will be included in the results.
|
||||
*
|
||||
* @param resource the {@link EncodedResource} containing the script to be processed.
|
||||
* @param commentPrefix the prefix that identifies comments in the SQL script (typically "--").
|
||||
* @param separator the statement separator in the SQL script (typically ";").
|
||||
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter.
|
||||
* @return a {@link Mono} of {@link String} containing the script lines that completes once the resource was loaded.
|
||||
*/
|
||||
private static Mono<String> readScript(EncodedResource resource, DataBufferFactory dataBufferFactory,
|
||||
@Nullable String commentPrefix, @Nullable String separator, @Nullable String blockCommentEndDelimiter) {
|
||||
|
||||
return DataBufferUtils.join(DataBufferUtils.read(resource.getResource(), dataBufferFactory, 8192))
|
||||
.handle((it, sink) -> {
|
||||
|
||||
try (InputStream is = it.asInputStream()) {
|
||||
|
||||
InputStreamReader in = resource.getCharset() != null ? new InputStreamReader(is, resource.getCharset())
|
||||
: new InputStreamReader(is);
|
||||
LineNumberReader lnr = new LineNumberReader(in);
|
||||
String script = readScript(lnr, commentPrefix, separator, blockCommentEndDelimiter);
|
||||
|
||||
sink.next(script);
|
||||
sink.complete();
|
||||
} catch (Exception e) {
|
||||
sink.error(e);
|
||||
} finally {
|
||||
DataBufferUtils.release(it);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a script from the provided {@link LineNumberReader}, using the supplied comment prefix and statement
|
||||
* separator, and build a {@link String} containing the lines.
|
||||
* <p>
|
||||
* Lines <em>beginning</em> with the comment prefix are excluded from the results; however, line comments anywhere
|
||||
* else — for example, within a statement — will be included in the results.
|
||||
*
|
||||
* @param lineNumberReader the {@link LineNumberReader} containing the script to be processed.
|
||||
* @param lineCommentPrefix the prefix that identifies comments in the SQL script (typically "--").
|
||||
* @param separator the statement separator in the SQL script (typically ";").
|
||||
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter.
|
||||
* @return a {@link String} containing the script lines.
|
||||
* @throws IOException in case of I/O errors
|
||||
*/
|
||||
private static String readScript(LineNumberReader lineNumberReader, @Nullable String lineCommentPrefix,
|
||||
@Nullable String separator, @Nullable String blockCommentEndDelimiter) throws IOException {
|
||||
|
||||
String currentStatement = lineNumberReader.readLine();
|
||||
StringBuilder scriptBuilder = new StringBuilder();
|
||||
while (currentStatement != null) {
|
||||
if ((blockCommentEndDelimiter != null && currentStatement.contains(blockCommentEndDelimiter))
|
||||
|| (lineCommentPrefix != null && !currentStatement.startsWith(lineCommentPrefix))) {
|
||||
if (scriptBuilder.length() > 0) {
|
||||
scriptBuilder.append('\n');
|
||||
}
|
||||
scriptBuilder.append(currentStatement);
|
||||
}
|
||||
currentStatement = lineNumberReader.readLine();
|
||||
}
|
||||
appendSeparatorToScriptIfNecessary(scriptBuilder, separator);
|
||||
return scriptBuilder.toString();
|
||||
}
|
||||
|
||||
private static void appendSeparatorToScriptIfNecessary(StringBuilder scriptBuilder, @Nullable String separator) {
|
||||
if (separator == null) {
|
||||
return;
|
||||
}
|
||||
String trimmed = separator.trim();
|
||||
if (trimmed.length() == separator.length()) {
|
||||
return;
|
||||
}
|
||||
// separator ends in whitespace, so we might want to see if the script is trying
|
||||
// to end the same way
|
||||
if (scriptBuilder.lastIndexOf(trimmed) == scriptBuilder.length() - trimmed.length()) {
|
||||
scriptBuilder.append(separator.substring(trimmed.length()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the provided SQL script contain the specified delimiter?
|
||||
*
|
||||
* @param script the SQL script
|
||||
* @param delim the string delimiting each statement - typically a ';' character
|
||||
*/
|
||||
static boolean containsSqlScriptDelimiters(String script, String delim) {
|
||||
|
||||
boolean inLiteral = false;
|
||||
boolean inEscape = false;
|
||||
|
||||
for (int i = 0; i < script.length(); i++) {
|
||||
char c = script.charAt(i);
|
||||
if (inEscape) {
|
||||
inEscape = false;
|
||||
continue;
|
||||
}
|
||||
// MySQL style escapes
|
||||
if (c == '\\') {
|
||||
inEscape = true;
|
||||
continue;
|
||||
}
|
||||
if (c == '\'') {
|
||||
inLiteral = !inLiteral;
|
||||
}
|
||||
if (!inLiteral && script.startsWith(delim, i)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given SQL script using default settings for statement separators, comment delimiters, and exception
|
||||
* handling flags.
|
||||
* <p>
|
||||
* Statement separators and comments will be removed before executing individual statements within the supplied
|
||||
* script.
|
||||
* <p>
|
||||
* <strong>Warning</strong>: this method does <em>not</em> release the provided {@link Connection}.
|
||||
*
|
||||
* @param connection the R2DBC connection to use to execute the script; already configured and ready to use.
|
||||
* @param resource the resource to load the SQL script from; encoded with the current platform's default encoding.
|
||||
* @return {@link Mono} that initiates script execution and is notified upon completion.
|
||||
* @throws ScriptException if an error occurred while executing the SQL script.
|
||||
* @see #executeSqlScript(Connection, EncodedResource, DataBufferFactory, boolean, boolean, String, String, String,
|
||||
* String)
|
||||
* @see #DEFAULT_STATEMENT_SEPARATOR
|
||||
* @see #DEFAULT_COMMENT_PREFIX
|
||||
* @see #DEFAULT_BLOCK_COMMENT_START_DELIMITER
|
||||
* @see #DEFAULT_BLOCK_COMMENT_END_DELIMITER
|
||||
* @see org.springframework.data.r2dbc.connectionfactory.ConnectionFactoryUtils#getConnection
|
||||
* @see org.springframework.data.r2dbc.connectionfactory.ConnectionFactoryUtils#releaseConnection
|
||||
*/
|
||||
public static Mono<Void> executeSqlScript(Connection connection, Resource resource) throws ScriptException {
|
||||
return executeSqlScript(connection, new EncodedResource(resource));
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given SQL script using default settings for statement separators, comment delimiters, and exception
|
||||
* handling flags.
|
||||
* <p>
|
||||
* Statement separators and comments will be removed before executing individual statements within the supplied
|
||||
* script.
|
||||
* <p>
|
||||
* <strong>Warning</strong>: this method does <em>not</em> release the provided {@link Connection}.
|
||||
*
|
||||
* @param connection the R2DBC connection to use to execute the script; already configured and ready to use.
|
||||
* @param resource the resource (potentially associated with a specific encoding) to load the SQL script from.
|
||||
* @return {@link Mono} that initiates script execution and is notified upon completion.
|
||||
* @throws ScriptException if an error occurred while executing the SQL script.
|
||||
* @see #executeSqlScript(Connection, EncodedResource, DataBufferFactory, boolean, boolean, String, String, String,
|
||||
* String)
|
||||
* @see #DEFAULT_STATEMENT_SEPARATOR
|
||||
* @see #DEFAULT_COMMENT_PREFIX
|
||||
* @see #DEFAULT_BLOCK_COMMENT_START_DELIMITER
|
||||
* @see #DEFAULT_BLOCK_COMMENT_END_DELIMITER
|
||||
* @see org.springframework.data.r2dbc.connectionfactory.ConnectionFactoryUtils#getConnection
|
||||
* @see org.springframework.data.r2dbc.connectionfactory.ConnectionFactoryUtils#releaseConnection
|
||||
*/
|
||||
public static Mono<Void> executeSqlScript(Connection connection, EncodedResource resource) throws ScriptException {
|
||||
return executeSqlScript(connection, resource, new DefaultDataBufferFactory(), false, false, DEFAULT_COMMENT_PREFIX,
|
||||
DEFAULT_STATEMENT_SEPARATOR, DEFAULT_BLOCK_COMMENT_START_DELIMITER, DEFAULT_BLOCK_COMMENT_END_DELIMITER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given SQL script.
|
||||
* <p>
|
||||
* Statement separators and comments will be removed before executing individual statements within the supplied
|
||||
* script.
|
||||
* <p>
|
||||
* <strong>Warning</strong>: this method does <em>not</em> release the provided {@link Connection}.
|
||||
*
|
||||
* @param connection the R2DBC connection to use to execute the script; already configured and ready to use.
|
||||
* @param dataBufferFactory the buffer factory for non-blocking script loading.
|
||||
* @param resource the resource (potentially associated with a specific encoding) to load the SQL script from.
|
||||
* @param continueOnError whether or not to continue without throwing an exception in the event of an error.
|
||||
* @param ignoreFailedDrops whether or not to continue in the event of specifically an error on a {@code DROP}
|
||||
* statement.
|
||||
* @param commentPrefix the prefix that identifies single-line comments in the SQL script (typically "--").
|
||||
* @param separator the script statement separator; defaults to {@value #DEFAULT_STATEMENT_SEPARATOR} if not specified
|
||||
* and falls back to {@value #FALLBACK_STATEMENT_SEPARATOR} as a last resort; may be set to
|
||||
* {@value #EOF_STATEMENT_SEPARATOR} to signal that the script contains a single statement without a
|
||||
* separator.
|
||||
* @param blockCommentStartDelimiter the <em>start</em> block comment delimiter.
|
||||
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter.
|
||||
* @return {@link Mono} that initiates script execution and is notified upon completion.
|
||||
* @throws ScriptException if an error occurred while executing the SQL script.
|
||||
* @see #DEFAULT_STATEMENT_SEPARATOR
|
||||
* @see #FALLBACK_STATEMENT_SEPARATOR
|
||||
* @see #EOF_STATEMENT_SEPARATOR
|
||||
* @see org.springframework.data.r2dbc.connectionfactory.ConnectionFactoryUtils#getConnection
|
||||
* @see org.springframework.data.r2dbc.connectionfactory.ConnectionFactoryUtils#releaseConnection
|
||||
*/
|
||||
public static Mono<Void> executeSqlScript(Connection connection, EncodedResource resource,
|
||||
DataBufferFactory dataBufferFactory, boolean continueOnError, boolean ignoreFailedDrops, String commentPrefix,
|
||||
@Nullable String separator, String blockCommentStartDelimiter, String blockCommentEndDelimiter)
|
||||
throws ScriptException {
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Executing SQL script from " + resource);
|
||||
}
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
Mono<String> script = readScript(resource, dataBufferFactory, commentPrefix, separator, blockCommentEndDelimiter)
|
||||
.onErrorMap(IOException.class, ex -> new CannotReadScriptException(resource, ex));
|
||||
|
||||
AtomicInteger statementNumber = new AtomicInteger();
|
||||
|
||||
Flux<Void> executeScript = script.flatMapIterable(it -> {
|
||||
return splitStatements(it, resource, commentPrefix, separator, blockCommentStartDelimiter,
|
||||
blockCommentEndDelimiter);
|
||||
}).concatMap(statement -> {
|
||||
|
||||
statementNumber.incrementAndGet();
|
||||
return runStatement(statement, connection, resource, continueOnError, ignoreFailedDrops, statementNumber);
|
||||
});
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
|
||||
executeScript = executeScript.doOnComplete(() -> {
|
||||
|
||||
long elapsedTime = System.currentTimeMillis() - startTime;
|
||||
logger.debug("Executed SQL script from " + resource + " in " + elapsedTime + " ms.");
|
||||
});
|
||||
}
|
||||
|
||||
return executeScript.onErrorMap(ex -> !(ex instanceof ScriptException),
|
||||
ex -> new UncategorizedScriptException("Failed to execute database script from resource [" + resource + "]",
|
||||
ex))
|
||||
.then();
|
||||
}
|
||||
|
||||
private static List<String> splitStatements(String script, EncodedResource resource, String commentPrefix,
|
||||
@Nullable String separator, String blockCommentStartDelimiter, String blockCommentEndDelimiter) {
|
||||
|
||||
String separatorToUse = separator;
|
||||
if (separatorToUse == null) {
|
||||
separatorToUse = DEFAULT_STATEMENT_SEPARATOR;
|
||||
}
|
||||
if (!EOF_STATEMENT_SEPARATOR.equals(separatorToUse) && !containsSqlScriptDelimiters(script, separatorToUse)) {
|
||||
separatorToUse = FALLBACK_STATEMENT_SEPARATOR;
|
||||
}
|
||||
|
||||
List<String> statements = new ArrayList<>();
|
||||
splitSqlScript(resource, script, separatorToUse, commentPrefix, blockCommentStartDelimiter,
|
||||
blockCommentEndDelimiter, statements);
|
||||
|
||||
return statements;
|
||||
}
|
||||
|
||||
private static Publisher<? extends Void> runStatement(String statement, Connection connection,
|
||||
EncodedResource resource, boolean continueOnError, boolean ignoreFailedDrops, AtomicInteger statementNumber) {
|
||||
|
||||
Mono<Long> execution = Flux.from(connection.createStatement(statement).execute()) //
|
||||
.flatMap(Result::getRowsUpdated) //
|
||||
.collect(Collectors.summingLong(it -> it));
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
execution = execution.doOnNext(rowsAffected -> {
|
||||
logger.debug(rowsAffected + " returned as update count for SQL: " + statement);
|
||||
});
|
||||
}
|
||||
|
||||
return execution.onErrorResume(ex -> {
|
||||
|
||||
boolean dropStatement = StringUtils.startsWithIgnoreCase(statement.trim(), "drop");
|
||||
if (continueOnError || (dropStatement && ignoreFailedDrops)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(ScriptStatementFailedException.buildErrorMessage(statement, statementNumber.get(), resource),
|
||||
ex);
|
||||
}
|
||||
} else {
|
||||
return Mono.error(new ScriptStatementFailedException(statement, statementNumber.get(), resource, ex));
|
||||
}
|
||||
|
||||
return Mono.empty();
|
||||
}).then();
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory.init;
|
||||
|
||||
/**
|
||||
* Thrown when we cannot determine anything more specific than "something went wrong while processing an SQL script":
|
||||
* for example, a {@link io.r2dbc.spi.R2dbcException} from R2DBC that we cannot pinpoint more precisely.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC. Use
|
||||
* {@link org.springframework.r2dbc.connection.init.UncategorizedScriptException} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class UncategorizedScriptException extends ScriptException {
|
||||
|
||||
private static final long serialVersionUID = -3196706179230349902L;
|
||||
|
||||
/**
|
||||
* Creates a new {@link UncategorizedScriptException}.
|
||||
*
|
||||
* @param message detailed message.
|
||||
*/
|
||||
public UncategorizedScriptException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link UncategorizedScriptException}.
|
||||
*
|
||||
* @param message detailed message.
|
||||
* @param cause the root cause.
|
||||
*/
|
||||
public UncategorizedScriptException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
/**
|
||||
* Provides extensible support for initializing databases through scripts. Deprecated since 1.2 in favor of Spring
|
||||
* R2DBC. Use {@link org.springframework.r2dbc.connection.init} instead.
|
||||
*/
|
||||
@org.springframework.lang.NonNullApi
|
||||
@org.springframework.lang.NonNullFields
|
||||
package org.springframework.data.r2dbc.connectionfactory.init;
|
||||
@@ -1,248 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory.lookup;
|
||||
|
||||
import io.r2dbc.spi.Connection;
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import io.r2dbc.spi.ConnectionFactoryMetadata;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Abstract {@link ConnectionFactory} implementation that routes {@link #create()} calls to one of various target
|
||||
* {@link ConnectionFactory factories} based on a lookup key. The latter is typically (but not necessarily) determined
|
||||
* from some subscriber context.
|
||||
* <p>
|
||||
* Allows to configure a {@link #setDefaultTargetConnectionFactory(Object) default ConnectionFactory} as fallback.
|
||||
* <p>
|
||||
* Calls to {@link #getMetadata()} are routed to the {@link #setDefaultTargetConnectionFactory(Object) default
|
||||
* ConnectionFactory} if configured.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Jens Schauder
|
||||
* @see #setTargetConnectionFactories
|
||||
* @see #setDefaultTargetConnectionFactory
|
||||
* @see #determineCurrentLookupKey()
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC. Use
|
||||
* {@link org.springframework.r2dbc.connection.lookup.AbstractRoutingConnectionFactory} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class AbstractRoutingConnectionFactory implements ConnectionFactory, InitializingBean {
|
||||
|
||||
private static final Object FALLBACK_MARKER = new Object();
|
||||
|
||||
private @Nullable Map<?, ?> targetConnectionFactories;
|
||||
|
||||
private @Nullable Object defaultTargetConnectionFactory;
|
||||
|
||||
private boolean lenientFallback = true;
|
||||
|
||||
private ConnectionFactoryLookup connectionFactoryLookup = new MapConnectionFactoryLookup();
|
||||
|
||||
private @Nullable Map<Object, ConnectionFactory> resolvedConnectionFactories;
|
||||
|
||||
private @Nullable ConnectionFactory resolvedDefaultConnectionFactory;
|
||||
|
||||
/**
|
||||
* Specify the map of target {@link ConnectionFactory ConnectionFactories}, with the lookup key as key. The mapped
|
||||
* value can either be a corresponding {@link ConnectionFactory} instance or a connection factory name String (to be
|
||||
* resolved via a {@link #setConnectionFactoryLookup ConnectionFactoryLookup}).
|
||||
* <p>
|
||||
* The key can be of arbitrary type; this class implements the generic lookup process only. The concrete key
|
||||
* representation will be handled by {@link #resolveSpecifiedLookupKey(Object)} and
|
||||
* {@link #determineCurrentLookupKey()}.
|
||||
*/
|
||||
public void setTargetConnectionFactories(Map<?, ?> targetConnectionFactories) {
|
||||
this.targetConnectionFactories = targetConnectionFactories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the default target {@link ConnectionFactory}, if any.
|
||||
* <p>
|
||||
* The mapped value can either be a corresponding {@link ConnectionFactory} instance or a connection factory name
|
||||
* {@link String} (to be resolved via a {@link #setConnectionFactoryLookup ConnectionFactoryLookup}).
|
||||
* <p>
|
||||
* This {@link ConnectionFactory} will be used as target if none of the keyed {@link #setTargetConnectionFactories
|
||||
* targetConnectionFactories} match the {@link #determineCurrentLookupKey() current lookup key}.
|
||||
*/
|
||||
public void setDefaultTargetConnectionFactory(Object defaultTargetConnectionFactory) {
|
||||
this.defaultTargetConnectionFactory = defaultTargetConnectionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether to apply a lenient fallback to the default {@link ConnectionFactory} if no specific
|
||||
* {@link ConnectionFactory} could be found for the current lookup key.
|
||||
* <p>
|
||||
* Default is {@literal true}, accepting lookup keys without a corresponding entry in the target
|
||||
* {@link ConnectionFactory} map - simply falling back to the default {@link ConnectionFactory} in that case.
|
||||
* <p>
|
||||
* Switch this flag to {@literal false} if you would prefer the fallback to only apply when no lookup key was emitted.
|
||||
* Lookup keys without a {@link ConnectionFactory} entry will then lead to an {@link IllegalStateException}.
|
||||
*
|
||||
* @see #setTargetConnectionFactories
|
||||
* @see #setDefaultTargetConnectionFactory
|
||||
* @see #determineCurrentLookupKey()
|
||||
*/
|
||||
public void setLenientFallback(boolean lenientFallback) {
|
||||
this.lenientFallback = lenientFallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link ConnectionFactoryLookup} implementation to use for resolving connection factory name Strings in the
|
||||
* {@link #setTargetConnectionFactories targetConnectionFactories} map.
|
||||
*/
|
||||
public void setConnectionFactoryLookup(ConnectionFactoryLookup connectionFactoryLookup) {
|
||||
|
||||
Assert.notNull(connectionFactoryLookup, "ConnectionFactoryLookup must not be null!");
|
||||
|
||||
this.connectionFactoryLookup = connectionFactoryLookup;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
|
||||
Assert.notNull(this.targetConnectionFactories, "Property 'targetConnectionFactories' must not be null!");
|
||||
|
||||
this.resolvedConnectionFactories = new HashMap<>(this.targetConnectionFactories.size());
|
||||
this.targetConnectionFactories.forEach((key, value) -> {
|
||||
Object lookupKey = resolveSpecifiedLookupKey(key);
|
||||
ConnectionFactory connectionFactory = resolveSpecifiedConnectionFactory(value);
|
||||
this.resolvedConnectionFactories.put(lookupKey, connectionFactory);
|
||||
});
|
||||
|
||||
if (this.defaultTargetConnectionFactory != null) {
|
||||
this.resolvedDefaultConnectionFactory = resolveSpecifiedConnectionFactory(this.defaultTargetConnectionFactory);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the given lookup key object, as specified in the {@link #setTargetConnectionFactories
|
||||
* targetConnectionFactories} map, into the actual lookup key to be used for matching with the
|
||||
* {@link #determineCurrentLookupKey() current lookup key}.
|
||||
* <p>
|
||||
* The default implementation simply returns the given key as-is.
|
||||
*
|
||||
* @param lookupKey the lookup key object as specified by the user.
|
||||
* @return the lookup key as needed for matching.
|
||||
*/
|
||||
protected Object resolveSpecifiedLookupKey(Object lookupKey) {
|
||||
return lookupKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the specified connection factory object into a {@link ConnectionFactory} instance.
|
||||
* <p>
|
||||
* The default implementation handles {@link ConnectionFactory} instances and connection factory names (to be resolved
|
||||
* via a {@link #setConnectionFactoryLookup ConnectionFactoryLookup}).
|
||||
*
|
||||
* @param connectionFactory the connection factory value object as specified in the
|
||||
* {@link #setTargetConnectionFactories targetConnectionFactories} map.
|
||||
* @return the resolved {@link ConnectionFactory} (never {@literal null}).
|
||||
* @throws IllegalArgumentException in case of an unsupported value type.
|
||||
*/
|
||||
protected ConnectionFactory resolveSpecifiedConnectionFactory(Object connectionFactory)
|
||||
throws IllegalArgumentException {
|
||||
|
||||
if (connectionFactory instanceof ConnectionFactory) {
|
||||
return (ConnectionFactory) connectionFactory;
|
||||
} else if (connectionFactory instanceof String) {
|
||||
return this.connectionFactoryLookup.getConnectionFactory((String) connectionFactory);
|
||||
} else {
|
||||
|
||||
throw new IllegalArgumentException(
|
||||
"Illegal connection factory value - only 'io.r2dbc.spi.ConnectionFactory' and 'String' supported: "
|
||||
+ connectionFactory);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see io.r2dbc.spi.ConnectionFactory#create()
|
||||
*/
|
||||
@Override
|
||||
public Mono<Connection> create() {
|
||||
|
||||
return determineTargetConnectionFactory() //
|
||||
.map(ConnectionFactory::create) //
|
||||
.flatMap(Mono::from);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see io.r2dbc.spi.ConnectionFactory#getMetadata()
|
||||
*/
|
||||
@Override
|
||||
public ConnectionFactoryMetadata getMetadata() {
|
||||
|
||||
if (this.resolvedDefaultConnectionFactory != null) {
|
||||
return this.resolvedDefaultConnectionFactory.getMetadata();
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException(
|
||||
"No default ConnectionFactory configured to retrieve ConnectionFactoryMetadata");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current target {@link ConnectionFactory}. Determines the {@link #determineCurrentLookupKey() current
|
||||
* lookup key}, performs a lookup in the {@link #setTargetConnectionFactories targetConnectionFactories} map, falls
|
||||
* back to the specified {@link #setDefaultTargetConnectionFactory default target ConnectionFactory} if necessary.
|
||||
*
|
||||
* @see #determineCurrentLookupKey()
|
||||
* @return {@link Mono} emitting the current {@link ConnectionFactory} as per {@link #determineCurrentLookupKey()}.
|
||||
*/
|
||||
protected Mono<ConnectionFactory> determineTargetConnectionFactory() {
|
||||
|
||||
Assert.state(this.resolvedConnectionFactories != null, "ConnectionFactory router not initialized");
|
||||
|
||||
Mono<Object> lookupKey = determineCurrentLookupKey().defaultIfEmpty(FALLBACK_MARKER);
|
||||
|
||||
return lookupKey.handle((key, sink) -> {
|
||||
|
||||
ConnectionFactory connectionFactory = this.resolvedConnectionFactories.get(key);
|
||||
|
||||
if (connectionFactory == null && (key == FALLBACK_MARKER || this.lenientFallback)) {
|
||||
connectionFactory = this.resolvedDefaultConnectionFactory;
|
||||
}
|
||||
|
||||
if (connectionFactory == null) {
|
||||
sink.error(new IllegalStateException(String.format(
|
||||
"Cannot determine target ConnectionFactory for lookup key '%s'", key == FALLBACK_MARKER ? null : key)));
|
||||
return;
|
||||
}
|
||||
|
||||
sink.next(connectionFactory);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the current lookup key. This will typically be implemented to check a subscriber context. Allows for
|
||||
* arbitrary keys. The returned key needs to match the stored lookup key type, as resolved by the
|
||||
* {@link #resolveSpecifiedLookupKey} method.
|
||||
*
|
||||
* @return {@link Mono} emitting the lookup key. May complete without emitting a value if no lookup key available.
|
||||
*/
|
||||
protected abstract Mono<Object> determineCurrentLookupKey();
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory.lookup;
|
||||
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link ConnectionFactoryLookup} implementation based on a Spring {@link BeanFactory}.
|
||||
* <p>
|
||||
* Will lookup Spring managed beans identified by bean name, expecting them to be of type {@link ConnectionFactory}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @see BeanFactory
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC. Use
|
||||
* {@link org.springframework.r2dbc.connection.lookup.BeanFactoryConnectionFactoryLookup} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class BeanFactoryConnectionFactoryLookup implements ConnectionFactoryLookup, BeanFactoryAware {
|
||||
|
||||
@Nullable private BeanFactory beanFactory;
|
||||
|
||||
/**
|
||||
* Creates a new {@link BeanFactoryConnectionFactoryLookup} instance.
|
||||
* <p>
|
||||
* The {@link BeanFactory} to access must be set via {@code setBeanFactory}.
|
||||
*
|
||||
* @see #setBeanFactory
|
||||
*/
|
||||
public BeanFactoryConnectionFactoryLookup() {}
|
||||
|
||||
/**
|
||||
* Create a new instance of the {@link BeanFactoryConnectionFactoryLookup} class.
|
||||
* <p>
|
||||
* Use of this constructor is redundant if this object is being created by a Spring IoC container, as the supplied
|
||||
* {@link BeanFactory} will be replaced by the {@link BeanFactory} that creates it (see the {@link BeanFactoryAware}
|
||||
* contract). So only use this constructor if you are using this class outside the context of a Spring IoC container.
|
||||
*
|
||||
* @param beanFactory the bean factory to be used to lookup {@link ConnectionFactory ConnectionFactories}.
|
||||
*/
|
||||
public BeanFactoryConnectionFactoryLookup(BeanFactory beanFactory) {
|
||||
|
||||
Assert.notNull(beanFactory, "BeanFactory must not be null!");
|
||||
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org.springframework.beans.factory.BeanFactory)
|
||||
*/
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.connectionfactory.lookup.ConnectionFactoryLookup#getConnectionFactory(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public ConnectionFactory getConnectionFactory(String connectionFactoryName)
|
||||
throws ConnectionFactoryLookupFailureException {
|
||||
|
||||
Assert.state(this.beanFactory != null, "BeanFactory must not be null!");
|
||||
|
||||
try {
|
||||
return this.beanFactory.getBean(connectionFactoryName, ConnectionFactory.class);
|
||||
} catch (BeansException ex) {
|
||||
throw new ConnectionFactoryLookupFailureException(
|
||||
String.format("Failed to look up ConnectionFactory bean with name '%s'", connectionFactoryName), ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory.lookup;
|
||||
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
|
||||
/**
|
||||
* Strategy interface for looking up {@link ConnectionFactory} by name.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC. Use
|
||||
* {@link org.springframework.r2dbc.connection.lookup.ConnectionFactoryLookup} instead.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
@Deprecated
|
||||
public interface ConnectionFactoryLookup {
|
||||
|
||||
/**
|
||||
* Retrieve the {@link ConnectionFactory} identified by the given name.
|
||||
*
|
||||
* @param connectionFactoryName the name of the {@link ConnectionFactory}.
|
||||
* @return the {@link ConnectionFactory} (never {@literal null}).
|
||||
* @throws ConnectionFactoryLookupFailureException if the lookup failed.
|
||||
*/
|
||||
ConnectionFactory getConnectionFactory(String connectionFactoryName) throws ConnectionFactoryLookupFailureException;
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory.lookup;
|
||||
|
||||
import org.springframework.dao.NonTransientDataAccessException;
|
||||
|
||||
/**
|
||||
* Exception to be thrown by a {@link ConnectionFactoryLookup} implementation, indicating that the specified
|
||||
* {@link io.r2dbc.spi.ConnectionFactory} could not be obtained.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC. Use
|
||||
* {@link org.springframework.r2dbc.connection.lookup.ConnectionFactoryLookupFailureException} instead.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@Deprecated
|
||||
public class ConnectionFactoryLookupFailureException extends NonTransientDataAccessException {
|
||||
|
||||
/**
|
||||
* Constructor for {@link ConnectionFactoryLookupFailureException}.
|
||||
*
|
||||
* @param msg the detail message.
|
||||
*/
|
||||
public ConnectionFactoryLookupFailureException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for {@link ConnectionFactoryLookupFailureException}.
|
||||
*
|
||||
* @param msg the detail message.
|
||||
* @param cause the root cause.
|
||||
*/
|
||||
public ConnectionFactoryLookupFailureException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory.lookup;
|
||||
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Simple {@link ConnectionFactoryLookup} implementation that relies on a map for doing lookups.
|
||||
* <p>
|
||||
* Useful for testing environments or applications that need to match arbitrary {@link String} names to target
|
||||
* {@link ConnectionFactory} objects.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Jens Schauder
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC. Use
|
||||
* {@link org.springframework.r2dbc.connection.lookup.MapConnectionFactoryLookup} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class MapConnectionFactoryLookup implements ConnectionFactoryLookup {
|
||||
|
||||
private final Map<String, ConnectionFactory> connectionFactories = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Create a new instance of the {@link MapConnectionFactoryLookup} class.
|
||||
*/
|
||||
public MapConnectionFactoryLookup() {}
|
||||
|
||||
/**
|
||||
* Create a new instance of the {@link MapConnectionFactoryLookup} class.
|
||||
*
|
||||
* @param connectionFactories the {@link Map} of {@link ConnectionFactory}. The keys are {@link String Strings}, the
|
||||
* values are actual {@link ConnectionFactory} instances.
|
||||
*/
|
||||
public MapConnectionFactoryLookup(Map<String, ConnectionFactory> connectionFactories) {
|
||||
setConnectionFactories(connectionFactories);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of the {@link MapConnectionFactoryLookup} class.
|
||||
*
|
||||
* @param connectionFactoryName the name under which the supplied {@link ConnectionFactory} is to be added
|
||||
* @param connectionFactory the {@link ConnectionFactory} to be added
|
||||
*/
|
||||
public MapConnectionFactoryLookup(String connectionFactoryName, ConnectionFactory connectionFactory) {
|
||||
addConnectionFactory(connectionFactoryName, connectionFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link Map} of {@link ConnectionFactory ConnectionFactories}. The keys are {@link String Strings}, the
|
||||
* values are actual {@link ConnectionFactory} instances.
|
||||
* <p>
|
||||
* If the supplied {@link Map} is {@literal null}, then this method call effectively has no effect.
|
||||
*
|
||||
* @param connectionFactories said {@link Map} of {@link ConnectionFactory connectionFactories}
|
||||
*/
|
||||
public void setConnectionFactories(Map<String, ConnectionFactory> connectionFactories) {
|
||||
|
||||
Assert.notNull(connectionFactories, "ConnectionFactories must not be null!");
|
||||
|
||||
this.connectionFactories.putAll(connectionFactories);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link Map} of {@link ConnectionFactory ConnectionFactories} maintained by this object.
|
||||
* <p>
|
||||
* The returned {@link Map} is {@link Collections#unmodifiableMap(Map) unmodifiable}.
|
||||
*
|
||||
* @return {@link Map} of {@link ConnectionFactory connectionFactory} (never {@literal null}).
|
||||
*/
|
||||
public Map<String, ConnectionFactory> getConnectionFactories() {
|
||||
return Collections.unmodifiableMap(this.connectionFactories);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the supplied {@link ConnectionFactory} to the map of {@link ConnectionFactory ConnectionFactorys} maintained by
|
||||
* this object.
|
||||
*
|
||||
* @param connectionFactoryName the name under which the supplied {@link ConnectionFactory} is to be added
|
||||
* @param connectionFactory the {@link ConnectionFactory} to be so added
|
||||
*/
|
||||
public void addConnectionFactory(String connectionFactoryName, ConnectionFactory connectionFactory) {
|
||||
|
||||
Assert.notNull(connectionFactoryName, "ConnectionFactory name must not be null!");
|
||||
Assert.notNull(connectionFactory, "ConnectionFactory must not be null!");
|
||||
|
||||
this.connectionFactories.put(connectionFactoryName, connectionFactory);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.connectionfactory.lookup.ConnectionFactoryLookup#getConnectionFactory(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public ConnectionFactory getConnectionFactory(String connectionFactoryName)
|
||||
throws ConnectionFactoryLookupFailureException {
|
||||
|
||||
Assert.notNull(connectionFactoryName, "ConnectionFactory name must not be null!");
|
||||
|
||||
return this.connectionFactories.computeIfAbsent(connectionFactoryName, key -> {
|
||||
|
||||
throw new ConnectionFactoryLookupFailureException(
|
||||
"No ConnectionFactory with name '" + connectionFactoryName + "' registered");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory.lookup;
|
||||
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An implementation of {@link ConnectionFactoryLookup} that simply wraps a single given {@link ConnectionFactory},
|
||||
* returned for any connection factory name.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC. Use
|
||||
* {@link org.springframework.r2dbc.connection.lookup.SingleConnectionFactoryLookup} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class SingleConnectionFactoryLookup implements ConnectionFactoryLookup {
|
||||
|
||||
private final ConnectionFactory connectionFactory;
|
||||
|
||||
/**
|
||||
* Create a new instance of the {@link SingleConnectionFactoryLookup} class.
|
||||
*
|
||||
* @param connectionFactory the single {@link ConnectionFactory} to wrap.
|
||||
*/
|
||||
public SingleConnectionFactoryLookup(ConnectionFactory connectionFactory) {
|
||||
|
||||
Assert.notNull(connectionFactory, "ConnectionFactory must not be null!");
|
||||
|
||||
this.connectionFactory = connectionFactory;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.connectionfactory.lookup.ConnectionFactoryLookup#getConnectionFactory(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public ConnectionFactory getConnectionFactory(String connectionFactoryName)
|
||||
throws ConnectionFactoryLookupFailureException {
|
||||
return this.connectionFactory;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
/**
|
||||
* Provides a strategy for looking up R2DBC ConnectionFactories by name. Deprecated since 1.2 in favor of Spring R2DBC.
|
||||
* Use {@link org.springframework.r2dbc.connection.lookup} instead.
|
||||
*/
|
||||
@org.springframework.lang.NonNullApi
|
||||
@org.springframework.lang.NonNullFields
|
||||
package org.springframework.data.r2dbc.connectionfactory.lookup;
|
||||
@@ -1,7 +0,0 @@
|
||||
/**
|
||||
* Connection and ConnectionFactory specifics for R2DBC. Deprecated since 1.2 in favor of Spring R2DBC. Use
|
||||
* {@link org.springframework.r2dbc.connection} instead.
|
||||
*/
|
||||
@org.springframework.lang.NonNullApi
|
||||
@org.springframework.lang.NonNullFields
|
||||
package org.springframework.data.r2dbc.connectionfactory;
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2021 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.data.r2dbc.convert;
|
||||
|
||||
import io.r2dbc.spi.ColumnMetadata;
|
||||
import io.r2dbc.spi.Row;
|
||||
import io.r2dbc.spi.RowMetadata;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.LinkedCaseInsensitiveMap;
|
||||
|
||||
/**
|
||||
* {@link BiFunction Mapping function} implementation that creates a {@link Map} for each row, representing all columns
|
||||
* as key-value pairs: one entry for each column, with the column name as key.
|
||||
* <p>
|
||||
* The {@link Map} implementation to use and the key to use for each column in the column Map can be customized through
|
||||
* overriding {@link #createColumnMap} and {@link #getColumnKey}, respectively.
|
||||
* <p>
|
||||
* <b>Note:</b> By default, {@link ColumnMapRowMapper} will try to build a linked {@link Map} with case-insensitive
|
||||
* keys, to preserve column order as well as allow any casing to be used for column names. This requires Commons
|
||||
* Collections on the classpath (which will be autodetected). Else, the fallback is a standard linked
|
||||
* {@link java.util.HashMap}, which will still preserve column order but requires the application to specify the column
|
||||
* names in the same casing as exposed by the driver.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @deprecated since 1.2, use Spring R2DBC's {@link org.springframework.r2dbc.core.ColumnMapRowMapper} directly.
|
||||
*/
|
||||
public class ColumnMapRowMapper extends org.springframework.r2dbc.core.ColumnMapRowMapper {
|
||||
|
||||
public final static ColumnMapRowMapper INSTANCE = new ColumnMapRowMapper();
|
||||
|
||||
@Override
|
||||
public Map<String, Object> apply(Row row, RowMetadata rowMetadata) {
|
||||
return super.apply(row, rowMetadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link Map} instance to be used as column map.
|
||||
* <p>
|
||||
* By default, a linked case-insensitive Map will be created.
|
||||
*
|
||||
* @param columnCount the column count, to be used as initial capacity for the Map.
|
||||
* @return the new {@link Map} instance.
|
||||
* @see LinkedCaseInsensitiveMap
|
||||
*/
|
||||
protected Map<String, Object> createColumnMap(int columnCount) {
|
||||
return super.createColumnMap(columnCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the key to use for the given column in the column {@link Map}.
|
||||
*
|
||||
* @param columnName the column name as returned by the {@link Row}.
|
||||
* @return the column key to use.
|
||||
* @see ColumnMetadata#getName()
|
||||
*/
|
||||
protected String getColumnKey(String columnName) {
|
||||
return super.getColumnKey(columnName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a R2DBC object value for the specified column.
|
||||
* <p>
|
||||
* The default implementation uses the {@link Row#get(int)} method.
|
||||
*
|
||||
* @param row is the {@link Row} holding the data.
|
||||
* @param index is the column index.
|
||||
* @return the Object returned.
|
||||
*/
|
||||
@Nullable
|
||||
protected Object getColumnValue(Row row, int index) {
|
||||
return super.getColumnValue(row, index);
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ import org.springframework.lang.Nullable;
|
||||
* @deprecated since 1.2, without replacement.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface BindParameterSource {
|
||||
interface BindParameterSource {
|
||||
|
||||
/**
|
||||
* Determine whether there is a value for the specified named parameter.
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2021 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.data.r2dbc.core;
|
||||
|
||||
import io.r2dbc.spi.Connection;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
|
||||
/**
|
||||
* Interface declaring methods that accept callback {@link Function} to operate within the scope of a
|
||||
* {@link Connection}. Callback functions operate on a provided connection and must not close the connection as the
|
||||
* connections may be pooled or be subject to other kinds of resource management.
|
||||
* <p>
|
||||
* Callback functions are responsible for creating a {@link org.reactivestreams.Publisher} that defines the scope of how
|
||||
* long the allocated {@link Connection} is valid. Connections are released after the publisher terminates.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @deprecated since 1.2, use Spring R2DBC's {@link org.springframework.r2dbc.core.DatabaseClient} support instead.
|
||||
*/
|
||||
public interface ConnectionAccessor extends org.springframework.r2dbc.core.ConnectionAccessor {
|
||||
|
||||
/**
|
||||
* Execute a callback {@link Function} within a {@link Connection} scope. The function is responsible for creating a
|
||||
* {@link Mono}. The connection is released after the {@link Mono} terminates (or the subscription is cancelled).
|
||||
* Connection resources must not be passed outside of the {@link Function} closure, otherwise resources may get
|
||||
* defunct.
|
||||
*
|
||||
* @param action must not be {@literal null}.
|
||||
* @return the resulting {@link Mono}.
|
||||
* @throws DataAccessException
|
||||
*/
|
||||
<T> Mono<T> inConnection(Function<Connection, Mono<T>> action) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a callback {@link Function} within a {@link Connection} scope. The function is responsible for creating a
|
||||
* {@link Flux}. The connection is released after the {@link Flux} terminates (or the subscription is cancelled).
|
||||
* Connection resources must not be passed outside of the {@link Function} closure, otherwise resources may get
|
||||
* defunct.
|
||||
*
|
||||
* @param action must not be {@literal null}.
|
||||
* @return the resulting {@link Flux}.
|
||||
* @throws DataAccessException
|
||||
*/
|
||||
<T> Flux<T> inConnectionMany(Function<Connection, Flux<T>> action) throws DataAccessException;
|
||||
|
||||
}
|
||||
@@ -1,929 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2021 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.data.r2dbc.core;
|
||||
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import io.r2dbc.spi.Row;
|
||||
import io.r2dbc.spi.RowMetadata;
|
||||
import io.r2dbc.spi.Statement;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.r2dbc.mapping.SettableValue;
|
||||
import org.springframework.data.r2dbc.query.Update;
|
||||
import org.springframework.data.r2dbc.support.R2dbcExceptionTranslator;
|
||||
import org.springframework.data.relational.core.query.CriteriaDefinition;
|
||||
import org.springframework.data.relational.core.sql.SqlIdentifier;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A non-blocking, reactive client for performing database calls requests with Reactive Streams back pressure. Provides
|
||||
* a higher level, common API over R2DBC client libraries.
|
||||
* <p>
|
||||
* Use one of the static factory methods {@link #create(ConnectionFactory)} or obtain a {@link DatabaseClient#builder()}
|
||||
* to create an instance.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Bogdan Ilchyshyn
|
||||
* @deprecated since 1.2, use Spring R2DBC's {@link org.springframework.r2dbc.core.DatabaseClient} support instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface DatabaseClient {
|
||||
|
||||
/**
|
||||
* Return the {@link ConnectionFactory} that this client uses.
|
||||
*
|
||||
* @return the connection factory.
|
||||
* @since 1.2
|
||||
*/
|
||||
ConnectionFactory getConnectionFactory();
|
||||
|
||||
/**
|
||||
* Specify a static {@code sql} string to execute. Contract for specifying a SQL call along with options leading to
|
||||
* the exchange. 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 {@literal null} or empty.
|
||||
* @return a new {@link GenericExecuteSpec}.
|
||||
* @see NamedParameterExpander
|
||||
* @see DatabaseClient.Builder#namedParameters(boolean)
|
||||
*/
|
||||
GenericExecuteSpec execute(String sql);
|
||||
|
||||
/**
|
||||
* Specify a {@link Supplier SQL supplier} that provides SQL to execute. Contract for specifying a SQL call along with
|
||||
* options leading to the exchange. 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}.
|
||||
* </p>
|
||||
*
|
||||
* @param sqlSupplier must not be {@literal null}.
|
||||
* @return a new {@link GenericExecuteSpec}.
|
||||
* @see NamedParameterExpander
|
||||
* @see DatabaseClient.Builder#namedParameters(boolean)
|
||||
* @see PreparedOperation
|
||||
*/
|
||||
GenericExecuteSpec execute(Supplier<String> sqlSupplier);
|
||||
|
||||
/**
|
||||
* Prepare an SQL SELECT call.
|
||||
*/
|
||||
SelectFromSpec select();
|
||||
|
||||
/**
|
||||
* Prepare an SQL INSERT call.
|
||||
*/
|
||||
InsertIntoSpec insert();
|
||||
|
||||
/**
|
||||
* Prepare an SQL UPDATE call.
|
||||
*/
|
||||
UpdateTableSpec update();
|
||||
|
||||
/**
|
||||
* Prepare an SQL DELETE call.
|
||||
*/
|
||||
DeleteFromSpec delete();
|
||||
|
||||
/**
|
||||
* Return a builder to mutate properties of this database client.
|
||||
*/
|
||||
DatabaseClient.Builder mutate();
|
||||
|
||||
// Static, factory methods
|
||||
|
||||
/**
|
||||
* Creates a {@code DatabaseClient} that will use the provided {@link io.r2dbc.spi.ConnectionFactory}.
|
||||
*
|
||||
* @param factory The {@code ConnectionFactory} to use for obtaining connections.
|
||||
* @return a new {@code DatabaseClient}. Guaranteed to be not {@literal null}.
|
||||
*/
|
||||
static DatabaseClient create(ConnectionFactory factory) {
|
||||
return new DefaultDatabaseClientBuilder().connectionFactory(factory).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a {@code DatabaseClient} builder.
|
||||
*/
|
||||
static DatabaseClient.Builder builder() {
|
||||
return new DefaultDatabaseClientBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* A mutable builder for creating a {@link DatabaseClient}.
|
||||
*/
|
||||
interface Builder {
|
||||
|
||||
/**
|
||||
* Configures the {@link ConnectionFactory R2DBC connector}.
|
||||
*
|
||||
* @param factory must not be {@literal null}.
|
||||
* @return {@code this} {@link Builder}.
|
||||
*/
|
||||
Builder connectionFactory(ConnectionFactory factory);
|
||||
|
||||
/**
|
||||
* Configures a {@link R2dbcExceptionTranslator}.
|
||||
*
|
||||
* @param exceptionTranslator must not be {@literal null}.
|
||||
* @return {@code this} {@link Builder}.
|
||||
*/
|
||||
Builder exceptionTranslator(R2dbcExceptionTranslator exceptionTranslator);
|
||||
|
||||
/**
|
||||
* Configures a {@link ExecuteFunction} to execute {@link Statement} objects.
|
||||
*
|
||||
* @param executeFunction must not be {@literal null}.
|
||||
* @return {@code this} {@link Builder}.
|
||||
* @since 1.1
|
||||
* @see Statement#execute()
|
||||
*/
|
||||
Builder executeFunction(ExecuteFunction executeFunction);
|
||||
|
||||
/**
|
||||
* Configures a {@link ReactiveDataAccessStrategy}.
|
||||
*
|
||||
* @param accessStrategy must not be {@literal null}.
|
||||
* @return {@code this} {@link Builder}.
|
||||
*/
|
||||
Builder dataAccessStrategy(ReactiveDataAccessStrategy accessStrategy);
|
||||
|
||||
/**
|
||||
* Configures whether to use named parameter expansion. Defaults to {@literal true}.
|
||||
*
|
||||
* @param enabled {@literal true} to use named parameter expansion. {@literal false} to disable named parameter
|
||||
* expansion.
|
||||
* @return {@code this} {@link Builder}.
|
||||
* @see NamedParameterExpander
|
||||
*/
|
||||
Builder namedParameters(boolean enabled);
|
||||
|
||||
/**
|
||||
* Configures the {@link org.springframework.data.projection.ProjectionFactory projection factory}.
|
||||
*
|
||||
* @param factory must not be {@literal null}.
|
||||
* @return {@code this} {@link Builder}.
|
||||
* @since 1.1
|
||||
*/
|
||||
Builder projectionFactory(ProjectionFactory factory);
|
||||
|
||||
/**
|
||||
* Configures a {@link Consumer} to configure this builder.
|
||||
*
|
||||
* @param builderConsumer must not be {@literal null}.
|
||||
* @return {@code this} {@link Builder}.
|
||||
*/
|
||||
Builder apply(Consumer<Builder> builderConsumer);
|
||||
|
||||
/**
|
||||
* Builder the {@link DatabaseClient} instance.
|
||||
*/
|
||||
DatabaseClient build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Contract for specifying a SQL call along with options leading to the exchange.
|
||||
*/
|
||||
interface GenericExecuteSpec extends BindSpec<GenericExecuteSpec>, StatementFilterSpec<GenericExecuteSpec> {
|
||||
|
||||
/**
|
||||
* Define the target type the result should be mapped to. <br />
|
||||
* Skip this step if you are anyway fine with the default conversion.
|
||||
*
|
||||
* @param resultType must not be {@literal null}.
|
||||
* @param <R> result type.
|
||||
*/
|
||||
<R> TypedExecuteSpec<R> as(Class<R> resultType);
|
||||
|
||||
/**
|
||||
* Configure a result mapping {@link java.util.function.Function function}.
|
||||
*
|
||||
* @param mappingFunction must not be {@literal null}.
|
||||
* @param <R> result type.
|
||||
* @return a {@link FetchSpec} for configuration what to fetch. Guaranteed to be not {@literal null}.
|
||||
*/
|
||||
<R> RowsFetchSpec<R> map(Function<Row, R> mappingFunction);
|
||||
|
||||
/**
|
||||
* Configure a result mapping {@link java.util.function.BiFunction function}.
|
||||
*
|
||||
* @param mappingFunction must not be {@literal null}.
|
||||
* @param <R> result type.
|
||||
* @return a {@link FetchSpec} for configuration what to fetch. Guaranteed to be not {@literal null}.
|
||||
*/
|
||||
<R> RowsFetchSpec<R> map(BiFunction<Row, RowMetadata, R> mappingFunction);
|
||||
|
||||
/**
|
||||
* Perform the SQL call and retrieve the result.
|
||||
*/
|
||||
FetchSpec<Map<String, Object>> fetch();
|
||||
|
||||
/**
|
||||
* Perform the SQL call and return a {@link Mono} that completes without result on statement completion.
|
||||
*
|
||||
* @return a {@link Mono} ignoring its payload (actively dropping).
|
||||
*/
|
||||
Mono<Void> then();
|
||||
}
|
||||
|
||||
/**
|
||||
* Contract for specifying a SQL call along with options leading to the exchange.
|
||||
*/
|
||||
interface TypedExecuteSpec<T> extends BindSpec<TypedExecuteSpec<T>>, StatementFilterSpec<TypedExecuteSpec<T>> {
|
||||
|
||||
/**
|
||||
* Define the target type the result should be mapped to. <br />
|
||||
* Skip this step if you are anyway fine with the default conversion.
|
||||
*
|
||||
* @param resultType must not be {@literal null}.
|
||||
* @param <R> result type.
|
||||
*/
|
||||
<R> TypedExecuteSpec<R> as(Class<R> resultType);
|
||||
|
||||
/**
|
||||
* Configure a result mapping {@link java.util.function.Function function}.
|
||||
*
|
||||
* @param mappingFunction must not be {@literal null}.
|
||||
* @param <R> result type.
|
||||
* @return a {@link FetchSpec} for configuration what to fetch. Guaranteed to be not {@literal null}.
|
||||
*/
|
||||
<R> RowsFetchSpec<R> map(Function<Row, R> mappingFunction);
|
||||
|
||||
/**
|
||||
* Configure a result mapping {@link java.util.function.BiFunction function}.
|
||||
*
|
||||
* @param mappingFunction must not be {@literal null}.
|
||||
* @param <R> result type.
|
||||
* @return a {@link FetchSpec} for configuration what to fetch. Guaranteed to be not {@literal null}.
|
||||
*/
|
||||
<R> RowsFetchSpec<R> map(BiFunction<Row, RowMetadata, R> mappingFunction);
|
||||
|
||||
/**
|
||||
* Perform the SQL call and retrieve the result.
|
||||
*/
|
||||
FetchSpec<T> fetch();
|
||||
|
||||
/**
|
||||
* Perform the SQL call and return a {@link Mono} that completes without result on statement completion.
|
||||
*
|
||||
* @return a {@link Mono} ignoring its payload (actively dropping).
|
||||
*/
|
||||
Mono<Void> then();
|
||||
}
|
||||
|
||||
/**
|
||||
* Contract for specifying {@code SELECT} options leading to the exchange.
|
||||
*/
|
||||
interface SelectFromSpec {
|
||||
|
||||
/**
|
||||
* Specify the source {@code table} to select from.
|
||||
*
|
||||
* @param table must not be {@literal null} or empty.
|
||||
* @return a {@link GenericSelectSpec} for further configuration of the select. Guaranteed to be not
|
||||
* {@literal null}.
|
||||
* @see SqlIdentifier#unquoted(String)
|
||||
*/
|
||||
default GenericSelectSpec from(String table) {
|
||||
return from(SqlIdentifier.unquoted(table));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the source {@code table} to select from.
|
||||
*
|
||||
* @param table must not be {@literal null} or empty.
|
||||
* @return a {@link GenericSelectSpec} for further configuration of the select. Guaranteed to be not
|
||||
* {@literal null}.
|
||||
* @since 1.1
|
||||
*/
|
||||
GenericSelectSpec from(SqlIdentifier table);
|
||||
|
||||
/**
|
||||
* Specify the source table to select from to using the {@link Class entity class}.
|
||||
*
|
||||
* @param table must not be {@literal null}.
|
||||
* @return a {@link TypedSelectSpec} for further configuration of the select. Guaranteed to be not {@literal null}.
|
||||
*/
|
||||
<T> TypedSelectSpec<T> from(Class<T> table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Contract for specifying {@code INSERT} options leading to the exchange.
|
||||
*/
|
||||
interface InsertIntoSpec {
|
||||
|
||||
/**
|
||||
* Specify the target {@code table} to insert into.
|
||||
*
|
||||
* @param table must not be {@literal null} or empty.
|
||||
* @return a {@link GenericInsertSpec} for further configuration of the insert. Guaranteed to be not
|
||||
* {@literal null}.
|
||||
* @see SqlIdentifier#unquoted(String)
|
||||
*/
|
||||
default GenericInsertSpec<Map<String, Object>> into(String table) {
|
||||
return into(SqlIdentifier.unquoted(table));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the target {@code table} to insert into.
|
||||
*
|
||||
* @param table must not be {@literal null} or empty.
|
||||
* @return a {@link GenericInsertSpec} for further configuration of the insert. Guaranteed to be not
|
||||
* {@literal null}.
|
||||
* @since 1.1
|
||||
*/
|
||||
GenericInsertSpec<Map<String, Object>> into(SqlIdentifier table);
|
||||
|
||||
/**
|
||||
* Specify the target table to insert to using the {@link Class entity class}.
|
||||
*
|
||||
* @param table must not be {@literal null}.
|
||||
* @return a {@link TypedInsertSpec} for further configuration of the insert. Guaranteed to be not {@literal null}.
|
||||
*/
|
||||
<T> TypedInsertSpec<T> into(Class<T> table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Contract for specifying {@code UPDATE} options leading to the exchange.
|
||||
*/
|
||||
interface UpdateTableSpec {
|
||||
|
||||
/**
|
||||
* Specify the target {@code table} to update.
|
||||
*
|
||||
* @param table must not be {@literal null} or empty.
|
||||
* @return a {@link GenericUpdateSpec} for further configuration of the update. Guaranteed to be not
|
||||
* {@literal null}.
|
||||
* @see SqlIdentifier#unquoted(String)
|
||||
*/
|
||||
default GenericUpdateSpec table(String table) {
|
||||
return table(SqlIdentifier.unquoted(table));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the target {@code table} to update.
|
||||
*
|
||||
* @param table must not be {@literal null} or empty.
|
||||
* @return a {@link GenericUpdateSpec} for further configuration of the update. Guaranteed to be not
|
||||
* {@literal null}.
|
||||
* @since 1.1
|
||||
*/
|
||||
GenericUpdateSpec table(SqlIdentifier table);
|
||||
|
||||
/**
|
||||
* Specify the target table to update to using the {@link Class entity class}.
|
||||
*
|
||||
* @param table must not be {@literal null}.
|
||||
* @return a {@link TypedUpdateSpec} for further configuration of the update. Guaranteed to be not {@literal null}.
|
||||
*/
|
||||
<T> TypedUpdateSpec<T> table(Class<T> table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Contract for specifying {@code DELETE} options leading to the exchange.
|
||||
*/
|
||||
interface DeleteFromSpec {
|
||||
|
||||
/**
|
||||
* Specify the source {@code table} to delete from.
|
||||
*
|
||||
* @param table must not be {@literal null} or empty.
|
||||
* @return a {@link DeleteMatchingSpec} for further configuration of the delete. Guaranteed to be not
|
||||
* {@literal null}.
|
||||
* @see SqlIdentifier#unquoted(String)
|
||||
*/
|
||||
default DeleteMatchingSpec from(String table) {
|
||||
return from(SqlIdentifier.unquoted(table));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the source {@code table} to delete from.
|
||||
*
|
||||
* @param table must not be {@literal null} or empty.
|
||||
* @return a {@link DeleteMatchingSpec} for further configuration of the delete. Guaranteed to be not
|
||||
* {@literal null}.
|
||||
* @since 1.1
|
||||
*/
|
||||
DeleteMatchingSpec from(SqlIdentifier table);
|
||||
|
||||
/**
|
||||
* Specify the source table to delete from to using the {@link Class entity class}.
|
||||
*
|
||||
* @param table must not be {@literal null}.
|
||||
* @return a {@link TypedDeleteSpec} for further configuration of the delete. Guaranteed to be not {@literal null}.
|
||||
*/
|
||||
<T> TypedDeleteSpec<T> from(Class<T> table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Contract for specifying {@code SELECT} options leading to the exchange.
|
||||
*/
|
||||
interface GenericSelectSpec extends SelectSpec<GenericSelectSpec> {
|
||||
|
||||
/**
|
||||
* Define the target type the result should be mapped to. <br />
|
||||
* Skip this step if you are anyway fine with the default conversion.
|
||||
*
|
||||
* @param resultType must not be {@literal null}.
|
||||
* @param <R> result type.
|
||||
*/
|
||||
<R> TypedSelectSpec<R> as(Class<R> resultType);
|
||||
|
||||
/**
|
||||
* Configure a result mapping {@link java.util.function.Function function}.
|
||||
*
|
||||
* @param mappingFunction must not be {@literal null}.
|
||||
* @param <R> result type.
|
||||
* @return a {@link FetchSpec} for configuration what to fetch. Guaranteed to be not {@literal null}.
|
||||
*/
|
||||
<R> RowsFetchSpec<R> map(Function<Row, R> mappingFunction);
|
||||
|
||||
/**
|
||||
* Configure a result mapping {@link java.util.function.BiFunction function}.
|
||||
*
|
||||
* @param mappingFunction must not be {@literal null}.
|
||||
* @param <R> result type.
|
||||
* @return a {@link FetchSpec} for configuration what to fetch. Guaranteed to be not {@literal null}.
|
||||
*/
|
||||
<R> RowsFetchSpec<R> map(BiFunction<Row, RowMetadata, R> mappingFunction);
|
||||
|
||||
/**
|
||||
* Perform the SQL call and retrieve the result.
|
||||
*/
|
||||
FetchSpec<Map<String, Object>> fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Contract for specifying {@code SELECT} options leading to the exchange.
|
||||
*/
|
||||
interface TypedSelectSpec<T> extends SelectSpec<TypedSelectSpec<T>> {
|
||||
|
||||
/**
|
||||
* Define the target type the result should be mapped to. <br />
|
||||
* Skip this step if you are anyway fine with the default conversion.
|
||||
*
|
||||
* @param resultType must not be {@literal null}.
|
||||
* @param <R> result type.
|
||||
*/
|
||||
<R> RowsFetchSpec<R> as(Class<R> resultType);
|
||||
|
||||
/**
|
||||
* Configure a result mapping {@link java.util.function.Function function}.
|
||||
*
|
||||
* @param mappingFunction must not be {@literal null}.
|
||||
* @param <R> result type.
|
||||
* @return a {@link FetchSpec} for configuration what to fetch. Guaranteed to be not {@literal null}.
|
||||
*/
|
||||
<R> RowsFetchSpec<R> map(Function<Row, R> mappingFunction);
|
||||
|
||||
/**
|
||||
* Configure a result mapping {@link java.util.function.BiFunction function}.
|
||||
*
|
||||
* @param mappingFunction must not be {@literal null}.
|
||||
* @param <R> result type.
|
||||
* @return a {@link FetchSpec} for configuration what to fetch. Guaranteed to be not {@literal null}.
|
||||
*/
|
||||
<R> RowsFetchSpec<R> map(BiFunction<Row, RowMetadata, R> mappingFunction);
|
||||
|
||||
/**
|
||||
* Perform the SQL call and retrieve the result.
|
||||
*/
|
||||
FetchSpec<T> fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Contract for specifying {@code SELECT} options leading to the exchange.
|
||||
*/
|
||||
interface SelectSpec<S extends SelectSpec<S>> {
|
||||
|
||||
/**
|
||||
* Configure projected fields.
|
||||
*
|
||||
* @param selectedFields must not be {@literal null}.
|
||||
* @see SqlIdentifier#unquoted(String)
|
||||
*/
|
||||
default S project(String... selectedFields) {
|
||||
return project(Arrays.stream(selectedFields).map(SqlIdentifier::unquoted).toArray(SqlIdentifier[]::new));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure projected fields.
|
||||
*
|
||||
* @param selectedFields must not be {@literal null}.
|
||||
* @since 1.1
|
||||
*/
|
||||
S project(SqlIdentifier... selectedFields);
|
||||
|
||||
/**
|
||||
* Configure a filter {@link CriteriaDefinition}.
|
||||
*
|
||||
* @param criteria must not be {@literal null}.
|
||||
*/
|
||||
S matching(CriteriaDefinition criteria);
|
||||
|
||||
/**
|
||||
* Configure {@link Sort}.
|
||||
*
|
||||
* @param sort must not be {@literal null}.
|
||||
*/
|
||||
S orderBy(Sort sort);
|
||||
|
||||
/**
|
||||
* Configure {@link Sort}.
|
||||
*
|
||||
* @param orders must not be {@literal null}.
|
||||
*/
|
||||
default S orderBy(Sort.Order... orders) {
|
||||
return orderBy(Sort.by(orders));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure pagination. Overrides {@link Sort} if the {@link Pageable} contains a {@link Sort} object.
|
||||
*
|
||||
* @param pageable must not be {@literal null}.
|
||||
*/
|
||||
S page(Pageable pageable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Contract for specifying {@code INSERT} options leading to the exchange.
|
||||
*
|
||||
* @param <T> Result type of tabular insert results.
|
||||
*/
|
||||
interface GenericInsertSpec<T> extends InsertSpec<T> {
|
||||
|
||||
/**
|
||||
* Specify a field and non-{@literal null} value to insert. {@code value} can be either a scalar value or
|
||||
* {@link SettableValue}.
|
||||
*
|
||||
* @param field must not be {@literal null} or empty.
|
||||
* @param value the field value to set, must not be {@literal null}. Can be either a scalar value or
|
||||
* {@link SettableValue}.
|
||||
* @see SqlIdentifier#unquoted(String)
|
||||
*/
|
||||
default GenericInsertSpec<T> value(String field, Object value) {
|
||||
return value(SqlIdentifier.unquoted(field), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a field and non-{@literal null} value to insert. {@code value} can be either a scalar value or
|
||||
* {@link SettableValue}.
|
||||
*
|
||||
* @param field must not be {@literal null} or empty.
|
||||
* @param value the field value to set, must not be {@literal null}. Can be either a scalar value or
|
||||
* {@link SettableValue}.
|
||||
*/
|
||||
GenericInsertSpec<T> value(SqlIdentifier field, Object value);
|
||||
|
||||
/**
|
||||
* Specify a {@literal null} value to insert.
|
||||
*
|
||||
* @param field must not be {@literal null} or empty.
|
||||
* @param type must not be {@literal null}.
|
||||
* @see SqlIdentifier#unquoted(String)
|
||||
*/
|
||||
default GenericInsertSpec<T> nullValue(String field, Class<?> type) {
|
||||
return nullValue(SqlIdentifier.unquoted(field), type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a {@literal null} value to insert.
|
||||
*
|
||||
* @param field must not be {@literal null} or empty.
|
||||
* @param type must not be {@literal null}.
|
||||
* @since 1.1
|
||||
*/
|
||||
default GenericInsertSpec<T> nullValue(SqlIdentifier field, Class<?> type) {
|
||||
return value(field, SettableValue.empty(type));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Contract for specifying {@code INSERT} options leading the exchange.
|
||||
*/
|
||||
interface TypedInsertSpec<T> {
|
||||
|
||||
/**
|
||||
* Insert the given {@code objectToInsert}.
|
||||
*
|
||||
* @param objectToInsert the object of which the attributes will provide the values for the insert. Must not be
|
||||
* {@literal null}.
|
||||
* @return a {@link InsertSpec} for further configuration of the insert. Guaranteed to be not {@literal null}.
|
||||
*/
|
||||
InsertSpec<Map<String, Object>> using(T objectToInsert);
|
||||
|
||||
/**
|
||||
* Use the given {@code tableName} as insert target.
|
||||
*
|
||||
* @param tableName must not be {@literal null} or empty.
|
||||
* @return a {@link TypedInsertSpec} for further configuration of the insert. Guaranteed to be not {@literal null}.
|
||||
* @see SqlIdentifier#unquoted(String)
|
||||
*/
|
||||
default TypedInsertSpec<T> table(String tableName) {
|
||||
return table(SqlIdentifier.unquoted(tableName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the given {@code tableName} as insert target.
|
||||
*
|
||||
* @param tableName must not be {@literal null} or empty.
|
||||
* @return a {@link TypedInsertSpec} for further configuration of the insert. Guaranteed to be not {@literal null}.
|
||||
* @since 1.1
|
||||
*/
|
||||
TypedInsertSpec<T> table(SqlIdentifier tableName);
|
||||
|
||||
/**
|
||||
* Insert the given {@link Publisher} to insert one or more objects. Inserts only a single object when calling
|
||||
* {@link FetchSpec#one()} or {@link FetchSpec#first()}.
|
||||
*
|
||||
* @param objectToInsert a publisher providing the objects of which the attributes will provide the values for the
|
||||
* insert. Must not be {@literal null}.
|
||||
* @return a {@link InsertSpec} for further configuration of the insert. Guaranteed to be not {@literal null}.
|
||||
* @see InsertSpec#fetch()
|
||||
*/
|
||||
InsertSpec<Map<String, Object>> using(Publisher<T> objectToInsert);
|
||||
}
|
||||
|
||||
/**
|
||||
* Contract for specifying {@code INSERT} options leading to the exchange.
|
||||
*
|
||||
* @param <T> Result type of tabular insert results.
|
||||
*/
|
||||
interface InsertSpec<T> {
|
||||
|
||||
/**
|
||||
* Configure a result mapping {@link java.util.function.Function function}.
|
||||
*
|
||||
* @param mappingFunction must not be {@literal null}.
|
||||
* @param <R> result type.
|
||||
* @return a {@link FetchSpec} for configuration what to fetch. Guaranteed to be not {@literal null}.
|
||||
*/
|
||||
<R> RowsFetchSpec<R> map(Function<Row, R> mappingFunction);
|
||||
|
||||
/**
|
||||
* Configure a result mapping {@link java.util.function.BiFunction function}.
|
||||
*
|
||||
* @param mappingFunction must not be {@literal null}.
|
||||
* @param <R> result type.
|
||||
* @return a {@link FetchSpec} for configuration what to fetch. Guaranteed to be not {@literal null}.
|
||||
*/
|
||||
<R> RowsFetchSpec<R> map(BiFunction<Row, RowMetadata, R> mappingFunction);
|
||||
|
||||
/**
|
||||
* Perform the SQL call and retrieve the result.
|
||||
*/
|
||||
FetchSpec<T> fetch();
|
||||
|
||||
/**
|
||||
* Perform the SQL call and return a {@link Mono} that completes without result on statement completion.
|
||||
*
|
||||
* @return a {@link Mono} ignoring its payload (actively dropping).
|
||||
*/
|
||||
Mono<Void> then();
|
||||
}
|
||||
|
||||
/**
|
||||
* Contract for specifying {@code UPDATE} options leading to the exchange.
|
||||
*/
|
||||
interface GenericUpdateSpec {
|
||||
|
||||
/**
|
||||
* Specify an {@link Update} object containing assignments.
|
||||
*
|
||||
* @param update must not be {@literal null}.
|
||||
* @deprecated since 1.1, use {@link #using(org.springframework.data.relational.core.query.Update)}.
|
||||
*/
|
||||
@Deprecated
|
||||
UpdateMatchingSpec using(Update update);
|
||||
|
||||
/**
|
||||
* Specify an {@link Update} object containing assignments.
|
||||
*
|
||||
* @param update must not be {@literal null}.
|
||||
* @since 1.1
|
||||
*/
|
||||
UpdateMatchingSpec using(org.springframework.data.relational.core.query.Update update);
|
||||
}
|
||||
|
||||
/**
|
||||
* Contract for specifying {@code UPDATE} options leading to the exchange.
|
||||
*/
|
||||
interface TypedUpdateSpec<T> {
|
||||
|
||||
/**
|
||||
* Update the given {@code objectToUpdate}.
|
||||
*
|
||||
* @param objectToUpdate the object of which the attributes will provide the values for the update and the primary
|
||||
* key. Must not be {@literal null}.
|
||||
* @return a {@link UpdateMatchingSpec} for further configuration of the update. Guaranteed to be not {@literal null}.
|
||||
*/
|
||||
UpdateMatchingSpec using(T objectToUpdate);
|
||||
|
||||
/**
|
||||
* Use the given {@code tableName} as update target.
|
||||
*
|
||||
* @param tableName must not be {@literal null} or empty.
|
||||
* @return a {@link TypedUpdateSpec} for further configuration of the update. Guaranteed to be not {@literal null}.
|
||||
* @see SqlIdentifier#unquoted(String)
|
||||
*/
|
||||
default TypedUpdateSpec<T> table(String tableName) {
|
||||
return table(SqlIdentifier.unquoted(tableName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the given {@code tableName} as update target.
|
||||
*
|
||||
* @param tableName must not be {@literal null} or empty.
|
||||
* @return a {@link TypedUpdateSpec} for further configuration of the update. Guaranteed to be not {@literal null}.
|
||||
* @since 1.1
|
||||
*/
|
||||
TypedUpdateSpec<T> table(SqlIdentifier tableName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Contract for specifying {@code UPDATE} options leading to the exchange.
|
||||
*/
|
||||
interface UpdateMatchingSpec extends UpdateSpec {
|
||||
|
||||
/**
|
||||
* Configure a filter {@link CriteriaDefinition}.
|
||||
*
|
||||
* @param criteria must not be {@literal null}.
|
||||
*/
|
||||
UpdateSpec matching(CriteriaDefinition criteria);
|
||||
}
|
||||
|
||||
/**
|
||||
* Contract for specifying {@code UPDATE} options leading to the exchange.
|
||||
*/
|
||||
interface UpdateSpec {
|
||||
|
||||
/**
|
||||
* Perform the SQL call and retrieve the result.
|
||||
*/
|
||||
UpdatedRowsFetchSpec fetch();
|
||||
|
||||
/**
|
||||
* Perform the SQL call and return a {@link Mono} that completes without result on statement completion.
|
||||
*
|
||||
* @return a {@link Mono} ignoring its payload (actively dropping).
|
||||
*/
|
||||
Mono<Void> then();
|
||||
}
|
||||
|
||||
/**
|
||||
* Contract for specifying {@code DELETE} options leading to the exchange.
|
||||
*/
|
||||
interface TypedDeleteSpec<T> extends DeleteSpec {
|
||||
|
||||
/**
|
||||
* Use the given {@code tableName} as delete target.
|
||||
*
|
||||
* @param tableName must not be {@literal null} or empty.
|
||||
* @return a {@link TypedDeleteSpec} for further configuration of the delete. Guaranteed to be not {@literal null}.
|
||||
* @see SqlIdentifier#unquoted(String)
|
||||
*/
|
||||
default TypedDeleteSpec<T> table(String tableName) {
|
||||
return table(SqlIdentifier.unquoted(tableName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the given {@code tableName} as delete target.
|
||||
*
|
||||
* @param tableName must not be {@literal null} or empty.
|
||||
* @return a {@link TypedDeleteSpec} for further configuration of the delete. Guaranteed to be not {@literal null}.
|
||||
* @since 1.1
|
||||
*/
|
||||
TypedDeleteSpec<T> table(SqlIdentifier tableName);
|
||||
|
||||
/**
|
||||
* Configure a filter {@link CriteriaDefinition}.
|
||||
*
|
||||
* @param criteria must not be {@literal null}.
|
||||
*/
|
||||
DeleteSpec matching(CriteriaDefinition criteria);
|
||||
}
|
||||
|
||||
/**
|
||||
* Contract for specifying {@code DELETE} options leading to the exchange.
|
||||
*/
|
||||
interface DeleteMatchingSpec extends DeleteSpec {
|
||||
|
||||
/**
|
||||
* Configure a filter {@link CriteriaDefinition}.
|
||||
*
|
||||
* @param criteria must not be {@literal null}.
|
||||
*/
|
||||
DeleteSpec matching(CriteriaDefinition criteria);
|
||||
}
|
||||
|
||||
/**
|
||||
* Contract for specifying {@code DELETE} options leading to the exchange.
|
||||
*/
|
||||
interface DeleteSpec {
|
||||
|
||||
/**
|
||||
* Perform the SQL call and retrieve the result.
|
||||
*/
|
||||
UpdatedRowsFetchSpec fetch();
|
||||
|
||||
/**
|
||||
* Perform the SQL call and return a {@link Mono} that completes without result on statement completion.
|
||||
*
|
||||
* @return a {@link Mono} ignoring its payload (actively dropping).
|
||||
*/
|
||||
Mono<Void> then();
|
||||
}
|
||||
|
||||
/**
|
||||
* Contract for specifying parameter bindings.
|
||||
*/
|
||||
interface BindSpec<S extends BindSpec<S>> {
|
||||
|
||||
/**
|
||||
* Bind a non-{@literal null} value to a parameter identified by its {@code index}. {@code value} can be either a
|
||||
* scalar value or {@link SettableValue}.
|
||||
*
|
||||
* @param index zero based index to bind the parameter to.
|
||||
* @param value must not be {@literal null}. Can be either a scalar value or {@link SettableValue}.
|
||||
*/
|
||||
S bind(int index, Object value);
|
||||
|
||||
/**
|
||||
* Bind a {@literal null} value to a parameter identified by its {@code index}.
|
||||
*
|
||||
* @param index zero based index to bind the parameter to.
|
||||
* @param type must not be {@literal null}.
|
||||
*/
|
||||
S bindNull(int index, Class<?> type);
|
||||
|
||||
/**
|
||||
* Bind a non-{@literal null} value to a parameter identified by its {@code name}. {@code value} can be either a
|
||||
* scalar value or {@link SettableValue}.
|
||||
*
|
||||
* @param name must not be {@literal null} or empty.
|
||||
* @param value must not be {@literal null}. Can be either a scalar value or {@link SettableValue}.
|
||||
*/
|
||||
S bind(String name, Object value);
|
||||
|
||||
/**
|
||||
* Bind a {@literal null} value to a parameter identified by its {@code name}.
|
||||
*
|
||||
* @param name must not be {@literal null} or empty.
|
||||
* @param type must not be {@literal null}.
|
||||
*/
|
||||
S bindNull(String name, Class<?> type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Contract for applying a {@link StatementFilterFunction}.
|
||||
*
|
||||
* @since 1.1
|
||||
*/
|
||||
interface StatementFilterSpec<S extends StatementFilterSpec<S>> {
|
||||
|
||||
/**
|
||||
* Add the given filter to the end of the filter chain.
|
||||
*
|
||||
* @param filter the filter to be added to the chain.
|
||||
*/
|
||||
default S filter(Function<? super Statement, ? extends Statement> filter) {
|
||||
|
||||
Assert.notNull(filter, "Statement FilterFunction must not be null!");
|
||||
|
||||
return filter((statement, next) -> next.execute(filter.apply(statement)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given filter to the end of the filter chain.
|
||||
*
|
||||
* @param filter the filter to be added to the chain.
|
||||
*/
|
||||
S filter(StatementFilterFunction filter);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,187 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2021 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.data.r2dbc.core;
|
||||
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import io.r2dbc.spi.Statement;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.r2dbc.core.DatabaseClient.Builder;
|
||||
import org.springframework.data.r2dbc.dialect.DialectResolver;
|
||||
import org.springframework.data.r2dbc.dialect.R2dbcDialect;
|
||||
import org.springframework.data.r2dbc.support.R2dbcExceptionSubclassTranslator;
|
||||
import org.springframework.data.r2dbc.support.R2dbcExceptionTranslator;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link DatabaseClient.Builder}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class DefaultDatabaseClientBuilder implements DatabaseClient.Builder {
|
||||
|
||||
private @Nullable ConnectionFactory connectionFactory;
|
||||
|
||||
private @Nullable R2dbcExceptionTranslator exceptionTranslator;
|
||||
|
||||
private ExecuteFunction executeFunction = Statement::execute;
|
||||
|
||||
private ReactiveDataAccessStrategy accessStrategy;
|
||||
|
||||
private boolean namedParameters = true;
|
||||
|
||||
private ProjectionFactory projectionFactory;
|
||||
|
||||
DefaultDatabaseClientBuilder() {}
|
||||
|
||||
DefaultDatabaseClientBuilder(DefaultDatabaseClientBuilder other) {
|
||||
|
||||
Assert.notNull(other, "DefaultDatabaseClientBuilder must not be null!");
|
||||
|
||||
this.connectionFactory = other.connectionFactory;
|
||||
this.exceptionTranslator = other.exceptionTranslator;
|
||||
this.executeFunction = other.executeFunction;
|
||||
this.accessStrategy = other.accessStrategy;
|
||||
this.namedParameters = other.namedParameters;
|
||||
this.projectionFactory = other.projectionFactory;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.DatabaseClient.Builder#connectionFactory(io.r2dbc.spi.ConnectionFactory)
|
||||
*/
|
||||
@Override
|
||||
public Builder connectionFactory(ConnectionFactory factory) {
|
||||
|
||||
Assert.notNull(factory, "ConnectionFactory must not be null!");
|
||||
|
||||
this.connectionFactory = factory;
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.DatabaseClient.Builder#exceptionTranslator(org.springframework.data.r2dbc.support.R2dbcExceptionTranslator)
|
||||
*/
|
||||
@Override
|
||||
public Builder exceptionTranslator(R2dbcExceptionTranslator exceptionTranslator) {
|
||||
|
||||
Assert.notNull(exceptionTranslator, "R2dbcExceptionTranslator must not be null!");
|
||||
|
||||
this.exceptionTranslator = exceptionTranslator;
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.DatabaseClient.Builder#executeFunction(org.springframework.data.r2dbc.core.ExecuteFunction)
|
||||
*/
|
||||
@Override
|
||||
public Builder executeFunction(ExecuteFunction executeFunction) {
|
||||
|
||||
Assert.notNull(executeFunction, "ExecuteFunction must not be null!");
|
||||
|
||||
this.executeFunction = executeFunction;
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.DatabaseClient.Builder#dataAccessStrategy(org.springframework.data.r2dbc.function.ReactiveDataAccessStrategy)
|
||||
*/
|
||||
@Override
|
||||
public Builder dataAccessStrategy(ReactiveDataAccessStrategy accessStrategy) {
|
||||
|
||||
Assert.notNull(accessStrategy, "ReactiveDataAccessStrategy must not be null!");
|
||||
|
||||
this.accessStrategy = accessStrategy;
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.DatabaseClient.Builder#namedParameters(boolean)
|
||||
*/
|
||||
@Override
|
||||
public Builder namedParameters(boolean enabled) {
|
||||
|
||||
this.namedParameters = enabled;
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.DatabaseClient.Builder#projectionFactory(ProjectionFactory)
|
||||
*/
|
||||
@Override
|
||||
public Builder projectionFactory(ProjectionFactory factory) {
|
||||
|
||||
Assert.notNull(factory, "ProjectionFactory must not be null!");
|
||||
|
||||
this.projectionFactory = factory;
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.DatabaseClient.Builder#build()
|
||||
*/
|
||||
@Override
|
||||
public DatabaseClient build() {
|
||||
|
||||
R2dbcExceptionTranslator exceptionTranslator = this.exceptionTranslator;
|
||||
|
||||
if (exceptionTranslator == null) {
|
||||
exceptionTranslator = new R2dbcExceptionSubclassTranslator();
|
||||
}
|
||||
|
||||
ReactiveDataAccessStrategy accessStrategy = this.accessStrategy;
|
||||
|
||||
if (accessStrategy == null) {
|
||||
|
||||
R2dbcDialect dialect = DialectResolver.getDialect(this.connectionFactory);
|
||||
accessStrategy = new DefaultReactiveDataAccessStrategy(dialect);
|
||||
}
|
||||
|
||||
return new DefaultDatabaseClient(this.connectionFactory, exceptionTranslator, executeFunction, accessStrategy,
|
||||
namedParameters, projectionFactory, new DefaultDatabaseClientBuilder(this));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#clone()
|
||||
*/
|
||||
@Override
|
||||
public DatabaseClient.Builder clone() {
|
||||
return new DefaultDatabaseClientBuilder(this);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.DatabaseClient.Builder#apply(java.util.function.Consumer)
|
||||
*/
|
||||
@Override
|
||||
public DatabaseClient.Builder apply(Consumer<DatabaseClient.Builder> builderConsumer) {
|
||||
Assert.notNull(builderConsumer, "BuilderConsumer must not be null");
|
||||
|
||||
builderConsumer.accept(this);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2021 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.data.r2dbc.core;
|
||||
|
||||
import io.r2dbc.spi.Connection;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link FetchSpec}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class DefaultFetchSpec<T> implements FetchSpec<T> {
|
||||
|
||||
private final ConnectionAccessor connectionAccessor;
|
||||
private final String sql;
|
||||
private final Function<Connection, Flux<T>> resultFunction;
|
||||
private final Function<Connection, Mono<Integer>> updatedRowsFunction;
|
||||
|
||||
DefaultFetchSpec(ConnectionAccessor connectionAccessor, String sql, Function<Connection, Flux<T>> resultFunction,
|
||||
Function<Connection, Mono<Integer>> updatedRowsFunction) {
|
||||
this.connectionAccessor = connectionAccessor;
|
||||
this.sql = sql;
|
||||
this.resultFunction = resultFunction;
|
||||
this.updatedRowsFunction = updatedRowsFunction;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.FetchSpec#one()
|
||||
*/
|
||||
@Override
|
||||
public Mono<T> one() {
|
||||
|
||||
return all().buffer(2) //
|
||||
.flatMap(it -> {
|
||||
|
||||
if (it.isEmpty()) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
if (it.size() > 1) {
|
||||
return Mono.error(new IncorrectResultSizeDataAccessException(
|
||||
String.format("Query [%s] returned non unique result.", this.sql), 1));
|
||||
}
|
||||
|
||||
return Mono.just(it.get(0));
|
||||
}).next();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.FetchSpec#first()
|
||||
*/
|
||||
@Override
|
||||
public Mono<T> first() {
|
||||
return all().next();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.FetchSpec#all()
|
||||
*/
|
||||
@Override
|
||||
public Flux<T> all() {
|
||||
return connectionAccessor.inConnectionMany(resultFunction);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.FetchSpec#rowsUpdated()
|
||||
*/
|
||||
@Override
|
||||
public Mono<Integer> rowsUpdated() {
|
||||
return connectionAccessor.inConnection(updatedRowsFunction);
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,6 @@ import org.springframework.data.r2dbc.convert.R2dbcCustomConversions;
|
||||
import org.springframework.data.r2dbc.dialect.R2dbcDialect;
|
||||
import org.springframework.data.r2dbc.mapping.OutboundRow;
|
||||
import org.springframework.data.r2dbc.mapping.R2dbcMappingContext;
|
||||
import org.springframework.data.r2dbc.mapping.SettableValue;
|
||||
import org.springframework.data.r2dbc.query.UpdateMapper;
|
||||
import org.springframework.data.r2dbc.support.ArrayUtils;
|
||||
import org.springframework.data.relational.core.dialect.ArrayColumns;
|
||||
@@ -46,6 +45,7 @@ import org.springframework.data.relational.core.mapping.RelationalPersistentProp
|
||||
import org.springframework.data.relational.core.sql.SqlIdentifier;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.r2dbc.core.Parameter;
|
||||
import org.springframework.r2dbc.core.PreparedOperation;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
@@ -64,7 +64,7 @@ public class DefaultReactiveDataAccessStrategy implements ReactiveDataAccessStra
|
||||
private final UpdateMapper updateMapper;
|
||||
private final MappingContext<RelationalPersistentEntity<?>, ? extends RelationalPersistentProperty> mappingContext;
|
||||
private final StatementMapper statementMapper;
|
||||
private final NamedParameterExpander expander;
|
||||
private final NamedParameterExpander expander = new NamedParameterExpander();
|
||||
|
||||
/**
|
||||
* Creates a new {@link DefaultReactiveDataAccessStrategy} given {@link R2dbcDialect} and optional
|
||||
@@ -115,24 +115,11 @@ public class DefaultReactiveDataAccessStrategy implements ReactiveDataAccessStra
|
||||
* @param dialect the {@link R2dbcDialect} to use.
|
||||
* @param converter must not be {@literal null}.
|
||||
*/
|
||||
public DefaultReactiveDataAccessStrategy(R2dbcDialect dialect, R2dbcConverter converter) {
|
||||
this(dialect, converter, new NamedParameterExpander());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link DefaultReactiveDataAccessStrategy} given {@link R2dbcDialect} and {@link R2dbcConverter}.
|
||||
*
|
||||
* @param dialect the {@link R2dbcDialect} to use.
|
||||
* @param converter must not be {@literal null}.
|
||||
* @param expander must not be {@literal null}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public DefaultReactiveDataAccessStrategy(R2dbcDialect dialect, R2dbcConverter converter,
|
||||
NamedParameterExpander expander) {
|
||||
public DefaultReactiveDataAccessStrategy(R2dbcDialect dialect, R2dbcConverter converter) {
|
||||
|
||||
Assert.notNull(dialect, "Dialect must not be null");
|
||||
Assert.notNull(converter, "RelationalConverter must not be null");
|
||||
Assert.notNull(expander, "NamedParameterExpander must not be null");
|
||||
|
||||
this.converter = converter;
|
||||
this.updateMapper = new UpdateMapper(dialect, converter);
|
||||
@@ -143,7 +130,6 @@ public class DefaultReactiveDataAccessStrategy implements ReactiveDataAccessStra
|
||||
RenderContextFactory factory = new RenderContextFactory(dialect);
|
||||
this.statementMapper = new DefaultStatementMapper(dialect, factory.createRenderContext(), this.updateMapper,
|
||||
this.mappingContext);
|
||||
this.expander = expander;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -270,15 +256,6 @@ public class DefaultReactiveDataAccessStrategy implements ReactiveDataAccessStra
|
||||
actualType);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.ReactiveDataAccessStrategy#getBindValue(SettableValue)
|
||||
*/
|
||||
@Override
|
||||
public SettableValue getBindValue(SettableValue value) {
|
||||
return this.updateMapper.getBindValue(value);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.ReactiveDataAccessStrategy#getBindValue(Parameter)
|
||||
@@ -306,10 +283,10 @@ public class DefaultReactiveDataAccessStrategy implements ReactiveDataAccessStra
|
||||
|
||||
List<String> parameterNames = this.expander.getParameterNames(query);
|
||||
|
||||
Map<String, SettableValue> namedBindings = new LinkedHashMap<>(parameterNames.size());
|
||||
Map<String, Parameter> namedBindings = new LinkedHashMap<>(parameterNames.size());
|
||||
for (String parameterName : parameterNames) {
|
||||
|
||||
SettableValue value = parameterProvider.getParameter(parameterNames.indexOf(parameterName), parameterName);
|
||||
Parameter value = parameterProvider.getParameter(parameterNames.indexOf(parameterName), parameterName);
|
||||
|
||||
if (value == null) {
|
||||
throw new InvalidDataAccessApiUsageException(
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2021 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.data.r2dbc.core;
|
||||
|
||||
import io.r2dbc.spi.Connection;
|
||||
import io.r2dbc.spi.Result;
|
||||
import io.r2dbc.spi.Row;
|
||||
import io.r2dbc.spi.RowMetadata;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
|
||||
/**
|
||||
* Default {@link SqlResult} implementation.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class DefaultSqlResult<T> implements FetchSpec<T> {
|
||||
|
||||
private final static FetchSpec<?> EMPTY = new FetchSpec<Object>() {
|
||||
|
||||
@Override
|
||||
public Mono<Object> one() {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Object> first() {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<Object> all() {
|
||||
return Flux.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Integer> rowsUpdated() {
|
||||
return Mono.just(0);
|
||||
}
|
||||
};
|
||||
|
||||
private final ConnectionAccessor connectionAccessor;
|
||||
private final String sql;
|
||||
private final Function<Connection, Flux<Result>> resultFunction;
|
||||
private final Function<Connection, Mono<Integer>> updatedRowsFunction;
|
||||
private final FetchSpec<T> fetchSpec;
|
||||
|
||||
DefaultSqlResult(ConnectionAccessor connectionAccessor, String sql, Function<Connection, Flux<Result>> resultFunction,
|
||||
Function<Connection, Mono<Integer>> updatedRowsFunction, BiFunction<Row, RowMetadata, T> mappingFunction) {
|
||||
|
||||
this.sql = sql;
|
||||
this.connectionAccessor = connectionAccessor;
|
||||
this.resultFunction = resultFunction;
|
||||
this.updatedRowsFunction = updatedRowsFunction;
|
||||
|
||||
this.fetchSpec = new DefaultFetchSpec<>(connectionAccessor, sql, new SqlFunction<Connection, Flux<T>>() {
|
||||
@Override
|
||||
public Flux<T> apply(Connection connection) {
|
||||
return resultFunction.apply(connection).flatMap(result -> result.map(mappingFunction));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSql() {
|
||||
return sql;
|
||||
}
|
||||
}, new SqlFunction<Connection, Mono<Integer>>() {
|
||||
@Override
|
||||
public Mono<Integer> apply(Connection connection) {
|
||||
return updatedRowsFunction.apply(connection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSql() {
|
||||
return sql;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an empty {@link SqlResult}.
|
||||
*
|
||||
* @param <R> value type of the {@code SqlResult}.
|
||||
* @return a {@code SqlResult}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <R> FetchSpec<R> empty() {
|
||||
return (FetchSpec<R>) EMPTY;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.SqlResult#map(java.util.function.BiFunction)
|
||||
*/
|
||||
public <R> FetchSpec<R> map(BiFunction<Row, RowMetadata, R> mappingFunction) {
|
||||
return new DefaultSqlResult<>(connectionAccessor, sql, resultFunction, updatedRowsFunction, mappingFunction);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.FetchSpec#one()
|
||||
*/
|
||||
@Override
|
||||
public Mono<T> one() {
|
||||
return fetchSpec.one();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.FetchSpec#first()
|
||||
*/
|
||||
@Override
|
||||
public Mono<T> first() {
|
||||
return fetchSpec.first();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.FetchSpec#all()
|
||||
*/
|
||||
@Override
|
||||
public Flux<T> all() {
|
||||
return fetchSpec.all();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.FetchSpec#rowsUpdated()
|
||||
*/
|
||||
@Override
|
||||
public Mono<Integer> rowsUpdated() {
|
||||
return fetchSpec.rowsUpdated();
|
||||
}
|
||||
|
||||
/**
|
||||
* Union type combining {@link Function} and {@link SqlProvider} to expose the SQL that is related to the underlying
|
||||
* action.
|
||||
*
|
||||
* @param <T> the type of the input to the function.
|
||||
* @param <R> the type of the result of the function.
|
||||
*/
|
||||
interface SqlFunction<T, R> extends Function<T, R>, SqlProvider {}
|
||||
}
|
||||
@@ -20,7 +20,6 @@ import java.util.List;
|
||||
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.r2dbc.convert.R2dbcConverter;
|
||||
import org.springframework.data.r2dbc.dialect.BindTarget;
|
||||
import org.springframework.data.r2dbc.dialect.R2dbcDialect;
|
||||
import org.springframework.data.r2dbc.query.BoundAssignments;
|
||||
import org.springframework.data.r2dbc.query.BoundCondition;
|
||||
@@ -34,7 +33,9 @@ import org.springframework.data.relational.core.sql.InsertBuilder.InsertValuesWi
|
||||
import org.springframework.data.relational.core.sql.render.RenderContext;
|
||||
import org.springframework.data.relational.core.sql.render.SqlRenderer;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.r2dbc.core.PreparedOperation;
|
||||
import org.springframework.r2dbc.core.binding.BindMarkers;
|
||||
import org.springframework.r2dbc.core.binding.BindTarget;
|
||||
import org.springframework.r2dbc.core.binding.Bindings;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -349,10 +350,6 @@ class DefaultStatementMapper implements StatementMapper {
|
||||
this.bindings.apply(to);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindTo(org.springframework.r2dbc.core.binding.BindTarget to) {
|
||||
this.bindings.apply(to);
|
||||
}
|
||||
}
|
||||
|
||||
class DefaultTypedStatementMapper<T> implements TypedStatementMapper<T> {
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020-2021 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.data.r2dbc.core;
|
||||
|
||||
import io.r2dbc.spi.Result;
|
||||
import io.r2dbc.spi.Statement;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
/**
|
||||
* Represents a function that executes a {@link io.r2dbc.spi.Statement} for a (delayed) {@link io.r2dbc.spi.Result}
|
||||
* stream.
|
||||
* <p>
|
||||
* Note that discarded {@link Result} objects must be consumed according to the R2DBC spec via either
|
||||
* {@link Result#getRowsUpdated()} or {@link Result#map(BiFunction)}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 1.1
|
||||
* @see Statement#execute()
|
||||
* @deprecated since 1.2, use Spring's {@link org.springframework.r2dbc.core.ExecuteFunction} support instead.
|
||||
*/
|
||||
@Deprecated
|
||||
@FunctionalInterface
|
||||
public interface ExecuteFunction extends org.springframework.r2dbc.core.ExecuteFunction {
|
||||
|
||||
/**
|
||||
* Execute the given {@link Statement} for a stream of {@link Result}s.
|
||||
*
|
||||
* @param statement the request to execute.
|
||||
* @return the delayed result stream.
|
||||
*/
|
||||
Publisher<? extends Result> execute(Statement statement);
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2021 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.data.r2dbc.core;
|
||||
|
||||
/**
|
||||
* Contract for fetching results.
|
||||
*
|
||||
* @param <T> row result type.
|
||||
* @author Mark Paluch
|
||||
* @see RowsFetchSpec
|
||||
* @see UpdatedRowsFetchSpec
|
||||
* @deprecated since 1.2, use Spring's {@link org.springframework.r2dbc.core.FetchSpec} support instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface FetchSpec<T> extends RowsFetchSpec<T>, UpdatedRowsFetchSpec {}
|
||||
@@ -18,13 +18,13 @@ package org.springframework.data.r2dbc.core;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.r2dbc.mapping.SettableValue;
|
||||
import org.springframework.data.util.Streamable;
|
||||
import org.springframework.r2dbc.core.Parameter;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link BindParameterSource} implementation that holds a given {@link Map} of parameters encapsulated as
|
||||
* {@link SettableValue}.
|
||||
* {@link Parameter}.
|
||||
* <p>
|
||||
* This class is intended for passing in a simple Map of parameter values to the methods of the
|
||||
* {@link NamedParameterExpander} class.
|
||||
@@ -32,10 +32,9 @@ import org.springframework.util.Assert;
|
||||
* @author Mark Paluch
|
||||
* @deprecated since 1.2, use Spring's org.springframework.r2dbc.core.MapBindParameterSource support instead.
|
||||
*/
|
||||
@Deprecated
|
||||
class MapBindParameterSource implements BindParameterSource {
|
||||
|
||||
private final Map<String, SettableValue> values;
|
||||
private final Map<String, Parameter> values;
|
||||
|
||||
/**
|
||||
* Creates a new empty {@link MapBindParameterSource}.
|
||||
@@ -45,11 +44,11 @@ class MapBindParameterSource implements BindParameterSource {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link MapBindParameterSource} given {@link Map} of {@link SettableValue}.
|
||||
* Creates a new {@link MapBindParameterSource} given {@link Map} of {@link Parameter}.
|
||||
*
|
||||
* @param values the parameter mapping.
|
||||
*/
|
||||
MapBindParameterSource(Map<String, SettableValue> values) {
|
||||
MapBindParameterSource(Map<String, Parameter> values) {
|
||||
|
||||
Assert.notNull(values, "Values must not be null");
|
||||
|
||||
@@ -68,7 +67,7 @@ class MapBindParameterSource implements BindParameterSource {
|
||||
Assert.notNull(paramName, "Parameter name must not be null!");
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
|
||||
this.values.put(paramName, SettableValue.fromOrEmpty(value, value.getClass()));
|
||||
this.values.put(paramName, Parameter.fromOrEmpty(value, value.getClass()));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -93,7 +92,7 @@ class MapBindParameterSource implements BindParameterSource {
|
||||
|
||||
Assert.notNull(paramName, "Parameter name must not be null!");
|
||||
|
||||
SettableValue settableValue = this.values.get(paramName);
|
||||
Parameter settableValue = this.values.get(paramName);
|
||||
if (settableValue != null) {
|
||||
return settableValue.getType();
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ import java.util.Map;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.data.r2dbc.dialect.BindMarkersFactory;
|
||||
import org.springframework.r2dbc.core.PreparedOperation;
|
||||
import org.springframework.r2dbc.core.binding.BindMarkersFactory;
|
||||
|
||||
/**
|
||||
* SQL translation support allowing the use of named parameters rather than native placeholders.
|
||||
@@ -39,7 +40,7 @@ import org.springframework.data.r2dbc.dialect.BindMarkersFactory;
|
||||
* @deprecated since 1.2, without replacement.
|
||||
*/
|
||||
@Deprecated
|
||||
public class NamedParameterExpander {
|
||||
class NamedParameterExpander {
|
||||
|
||||
/**
|
||||
* Default maximum number of entries for the SQL cache: 256.
|
||||
@@ -126,11 +127,6 @@ public class NamedParameterExpander {
|
||||
*/
|
||||
public PreparedOperation<String> expand(String sql, BindMarkersFactory bindMarkersFactory,
|
||||
BindParameterSource paramSource) {
|
||||
return expand(sql, (org.springframework.r2dbc.core.binding.BindMarkersFactory) bindMarkersFactory, paramSource);
|
||||
}
|
||||
|
||||
PreparedOperation<String> expand(String sql,
|
||||
org.springframework.r2dbc.core.binding.BindMarkersFactory bindMarkersFactory, BindParameterSource paramSource) {
|
||||
|
||||
ParsedSql parsedSql = getParsedSql(sql);
|
||||
|
||||
|
||||
@@ -26,10 +26,11 @@ import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.r2dbc.dialect.BindTarget;
|
||||
import org.springframework.r2dbc.core.PreparedOperation;
|
||||
import org.springframework.r2dbc.core.binding.BindMarker;
|
||||
import org.springframework.r2dbc.core.binding.BindMarkers;
|
||||
import org.springframework.r2dbc.core.binding.BindMarkersFactory;
|
||||
import org.springframework.r2dbc.core.binding.BindTarget;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -581,13 +582,9 @@ abstract class NamedParameterUtils {
|
||||
return this.expandedSql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindTo(BindTarget target) {
|
||||
bindTo((org.springframework.r2dbc.core.binding.BindTarget) target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindTo(org.springframework.r2dbc.core.binding.BindTarget target) {
|
||||
public void bindTo(BindTarget target) {
|
||||
|
||||
for (String namedParameter : this.parameterSource.getParameterNames()) {
|
||||
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.core;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.data.r2dbc.dialect.BindTarget;
|
||||
|
||||
/**
|
||||
* Extension to {@link QueryOperation} for a prepared SQL query {@link Supplier} with bound parameters. Contains
|
||||
* parameter bindings that can be {@link #bindTo bound} bound to a {@link BindTarget}.
|
||||
* <p>
|
||||
* Can be executed with {@link org.springframework.data.r2dbc.core.DatabaseClient}.
|
||||
* </p>
|
||||
*
|
||||
* @param <T> underlying operation source.
|
||||
* @author Mark Paluch
|
||||
* @see org.springframework.data.r2dbc.core.DatabaseClient#execute(Supplier)
|
||||
* @deprecated since 1.2, use Spring R2DBC's {@link org.springframework.r2dbc.core.PreparedOperation} support instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface PreparedOperation<T> extends QueryOperation, org.springframework.r2dbc.core.PreparedOperation<T> {
|
||||
|
||||
/**
|
||||
* @return the query source, such as a statement/criteria object.
|
||||
*/
|
||||
T getSource();
|
||||
|
||||
/**
|
||||
* Apply bindings to {@link BindTarget}.
|
||||
*
|
||||
* @param target the target to apply bindings to.
|
||||
*/
|
||||
void bindTo(BindTarget target);
|
||||
|
||||
@Override
|
||||
default String get() {
|
||||
return toQuery();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.core;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Interface declaring a query operation that can be represented with a query string. This interface is typically
|
||||
* implemented by classes representing a SQL operation such as {@code SELECT}, {@code INSERT}, and such.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @see PreparedOperation
|
||||
* @deprecated since 1.2, use Spring R2DBC's {@link org.springframework.r2dbc.core.QueryOperation} support instead.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
@Deprecated
|
||||
public interface QueryOperation extends Supplier<String>, org.springframework.r2dbc.core.QueryOperation {
|
||||
|
||||
/**
|
||||
* Returns the string-representation of this operation to be used with {@link io.r2dbc.spi.Statement} creation.
|
||||
*
|
||||
* @return the operation as SQL string.
|
||||
* @see io.r2dbc.spi.Connection#createStatement(String)
|
||||
*/
|
||||
String toQuery();
|
||||
|
||||
@Override
|
||||
default String get() {
|
||||
return toQuery();
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.r2dbc.core;
|
||||
|
||||
import io.r2dbc.spi.Connection;
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import io.r2dbc.spi.Row;
|
||||
import io.r2dbc.spi.RowMetadata;
|
||||
@@ -29,7 +28,6 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
@@ -72,11 +70,9 @@ import org.springframework.data.relational.core.sql.Table;
|
||||
import org.springframework.data.util.ProxyUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.r2dbc.core.DatabaseClient;
|
||||
import org.springframework.r2dbc.core.FetchSpec;
|
||||
import org.springframework.r2dbc.core.Parameter;
|
||||
import org.springframework.r2dbc.core.PreparedOperation;
|
||||
import org.springframework.r2dbc.core.RowsFetchSpec;
|
||||
import org.springframework.r2dbc.core.StatementFilterFunction;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -131,21 +127,10 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
|
||||
* @param dialect the dialect to use, must not be {@literal null}.
|
||||
* @since 1.2
|
||||
*/
|
||||
public R2dbcEntityTemplate(org.springframework.r2dbc.core.DatabaseClient databaseClient, R2dbcDialect dialect) {
|
||||
public R2dbcEntityTemplate(DatabaseClient databaseClient, R2dbcDialect dialect) {
|
||||
this(databaseClient, new DefaultReactiveDataAccessStrategy(dialect));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link R2dbcEntityTemplate} given {@link DatabaseClient}.
|
||||
*
|
||||
* @param databaseClient must not be {@literal null}.
|
||||
* @deprecated since 1.2, use {@link #R2dbcEntityTemplate(DatabaseClient, R2dbcDialect)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public R2dbcEntityTemplate(org.springframework.data.r2dbc.core.DatabaseClient databaseClient) {
|
||||
this(databaseClient, getDataAccessStrategy(databaseClient));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link R2dbcEntityTemplate} given {@link DatabaseClient}, {@link R2dbcDialect} and
|
||||
* {@link R2dbcConverter}.
|
||||
@@ -155,8 +140,7 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
|
||||
* @param converter the dialect to use, must not be {@literal null}.
|
||||
* @since 1.2
|
||||
*/
|
||||
public R2dbcEntityTemplate(org.springframework.r2dbc.core.DatabaseClient databaseClient, R2dbcDialect dialect,
|
||||
R2dbcConverter converter) {
|
||||
public R2dbcEntityTemplate(DatabaseClient databaseClient, R2dbcDialect dialect, R2dbcConverter converter) {
|
||||
this(databaseClient, new DefaultReactiveDataAccessStrategy(dialect, converter));
|
||||
}
|
||||
|
||||
@@ -166,7 +150,7 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
|
||||
* @param databaseClient must not be {@literal null}.
|
||||
* @since 1.2
|
||||
*/
|
||||
public R2dbcEntityTemplate(org.springframework.r2dbc.core.DatabaseClient databaseClient,
|
||||
public R2dbcEntityTemplate(DatabaseClient databaseClient,
|
||||
ReactiveDataAccessStrategy strategy) {
|
||||
|
||||
Assert.notNull(databaseClient, "DatabaseClient must not be null");
|
||||
@@ -178,18 +162,6 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
|
||||
this.projectionFactory = new SpelAwareProxyProjectionFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link R2dbcEntityTemplate} given {@link DatabaseClient} and {@link ReactiveDataAccessStrategy}.
|
||||
*
|
||||
* @param databaseClient must not be {@literal null}.
|
||||
* @deprecated since 1.2, use {@link #R2dbcEntityTemplate(DatabaseClient, ReactiveDataAccessStrategy)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public R2dbcEntityTemplate(org.springframework.data.r2dbc.core.DatabaseClient databaseClient,
|
||||
ReactiveDataAccessStrategy strategy) {
|
||||
this(new DatabaseClientAdapter(databaseClient), strategy);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#getDatabaseClient()
|
||||
@@ -641,11 +613,9 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
|
||||
}
|
||||
|
||||
return statement.returnGeneratedValues(dataAccessStrategy.renderForGeneratedValues(identifierColumns.get(0)));
|
||||
})
|
||||
.map(this.dataAccessStrategy.getConverter().populateIdIfNecessary(entity)) //
|
||||
}).map(this.dataAccessStrategy.getConverter().populateIdIfNecessary(entity)) //
|
||||
.all() //
|
||||
.last(entity)
|
||||
.flatMap(saved -> maybeCallAfterSave(saved, outboundRow, tableName));
|
||||
.last(entity).flatMap(saved -> maybeCallAfterSave(saved, outboundRow, tableName));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -914,149 +884,6 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
|
||||
return executeSpec.map(rowMapper);
|
||||
}
|
||||
|
||||
private static ReactiveDataAccessStrategy getDataAccessStrategy(
|
||||
org.springframework.data.r2dbc.core.DatabaseClient databaseClient) {
|
||||
|
||||
Assert.notNull(databaseClient, "DatabaseClient must not be null");
|
||||
|
||||
if (databaseClient instanceof DefaultDatabaseClient) {
|
||||
|
||||
DefaultDatabaseClient client = (DefaultDatabaseClient) databaseClient;
|
||||
return client.getDataAccessStrategy();
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Cannot obtain ReactiveDataAccessStrategy");
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapter to adapt our deprecated {@link org.springframework.data.r2dbc.core.DatabaseClient} into Spring R2DBC
|
||||
* {@link DatabaseClient}.
|
||||
*/
|
||||
private static class DatabaseClientAdapter implements DatabaseClient {
|
||||
|
||||
private final org.springframework.data.r2dbc.core.DatabaseClient delegate;
|
||||
|
||||
private DatabaseClientAdapter(org.springframework.data.r2dbc.core.DatabaseClient delegate) {
|
||||
|
||||
Assert.notNull(delegate, "DatabaseClient must not be null");
|
||||
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConnectionFactory getConnectionFactory() {
|
||||
return delegate.getConnectionFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GenericExecuteSpec sql(String sql) {
|
||||
return new GenericExecuteSpecAdapter(delegate.execute(sql));
|
||||
}
|
||||
|
||||
@Override
|
||||
public GenericExecuteSpec sql(Supplier<String> sqlSupplier) {
|
||||
return new GenericExecuteSpecAdapter(delegate.execute(sqlSupplier));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Mono<T> inConnection(Function<Connection, Mono<T>> action) throws DataAccessException {
|
||||
return ((ConnectionAccessor) delegate).inConnection(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Flux<T> inConnectionMany(Function<Connection, Flux<T>> action) throws DataAccessException {
|
||||
return ((ConnectionAccessor) delegate).inConnectionMany(action);
|
||||
}
|
||||
|
||||
static class GenericExecuteSpecAdapter implements GenericExecuteSpec {
|
||||
|
||||
private final org.springframework.data.r2dbc.core.DatabaseClient.GenericExecuteSpec delegate;
|
||||
|
||||
public GenericExecuteSpecAdapter(org.springframework.data.r2dbc.core.DatabaseClient.GenericExecuteSpec delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GenericExecuteSpec bind(int index, Object value) {
|
||||
return new GenericExecuteSpecAdapter(delegate.bind(index, value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public GenericExecuteSpec bindNull(int index, Class<?> type) {
|
||||
return new GenericExecuteSpecAdapter(delegate.bindNull(index, type));
|
||||
}
|
||||
|
||||
@Override
|
||||
public GenericExecuteSpec bind(String name, Object value) {
|
||||
return new GenericExecuteSpecAdapter(delegate.bind(name, value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public GenericExecuteSpec bindNull(String name, Class<?> type) {
|
||||
return new GenericExecuteSpecAdapter(delegate.bindNull(name, type));
|
||||
}
|
||||
|
||||
@Override
|
||||
public GenericExecuteSpec filter(StatementFilterFunction filter) {
|
||||
return new GenericExecuteSpecAdapter(delegate.filter(filter::filter));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R> RowsFetchSpec<R> map(BiFunction<Row, RowMetadata, R> mappingFunction) {
|
||||
return new RowFetchSpecAdapter<>(delegate.map(mappingFunction));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchSpec<Map<String, Object>> fetch() {
|
||||
return new FetchSpecAdapter<>(delegate.fetch());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> then() {
|
||||
return delegate.then();
|
||||
}
|
||||
}
|
||||
|
||||
private static class RowFetchSpecAdapter<T> implements RowsFetchSpec<T> {
|
||||
|
||||
private final org.springframework.data.r2dbc.core.RowsFetchSpec<T> delegate;
|
||||
|
||||
RowFetchSpecAdapter(org.springframework.data.r2dbc.core.RowsFetchSpec<T> delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<T> one() {
|
||||
return delegate.one();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<T> first() {
|
||||
return delegate.first();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<T> all() {
|
||||
return delegate.all();
|
||||
}
|
||||
}
|
||||
|
||||
private static class FetchSpecAdapter<T> extends RowFetchSpecAdapter<T> implements FetchSpec<T> {
|
||||
|
||||
private final org.springframework.data.r2dbc.core.FetchSpec<T> delegate;
|
||||
|
||||
FetchSpecAdapter(org.springframework.data.r2dbc.core.FetchSpec<T> delegate) {
|
||||
super(delegate);
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Integer> rowsUpdated() {
|
||||
return delegate.rowsUpdated();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link RowsFetchSpec} adapter emitting values from {@link Optional} if they exist.
|
||||
*
|
||||
|
||||
@@ -23,7 +23,6 @@ import java.util.function.BiFunction;
|
||||
|
||||
import org.springframework.data.r2dbc.convert.R2dbcConverter;
|
||||
import org.springframework.data.r2dbc.mapping.OutboundRow;
|
||||
import org.springframework.data.r2dbc.mapping.SettableValue;
|
||||
import org.springframework.data.relational.core.sql.IdentifierProcessing;
|
||||
import org.springframework.data.relational.core.sql.SqlIdentifier;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -33,8 +32,8 @@ import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Data access strategy that generalizes convenience operations using mapped entities. Typically used internally by
|
||||
* {@link DatabaseClient} and repository support. SQL creation is limited to single-table operations and single-column
|
||||
* primary keys.
|
||||
* {@link R2dbcEntityOperations} and repository support. SQL creation is limited to single-table operations and
|
||||
* single-column primary keys.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Jens Schauder
|
||||
@@ -65,17 +64,6 @@ public interface ReactiveDataAccessStrategy {
|
||||
*/
|
||||
OutboundRow getOutboundRow(Object object);
|
||||
|
||||
/**
|
||||
* Return a potentially converted {@link SettableValue} for strategies that support type conversion.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
* @since 1.1
|
||||
* @deprecated since 1.2, use {@link #getBindValue(Parameter)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
SettableValue getBindValue(SettableValue value);
|
||||
|
||||
/**
|
||||
* Return a potentially converted {@link Parameter} for strategies that support type conversion.
|
||||
*
|
||||
@@ -159,7 +147,7 @@ public interface ReactiveDataAccessStrategy {
|
||||
interface NamedParameterProvider {
|
||||
|
||||
/**
|
||||
* Returns the {@link SettableValue value} for a parameter identified either by name or by index.
|
||||
* Returns the {@link Parameter value} for a parameter identified either by name or by index.
|
||||
*
|
||||
* @param index parameter index according the parameter discovery order.
|
||||
* @param name name of the parameter.
|
||||
@@ -167,7 +155,7 @@ public interface ReactiveDataAccessStrategy {
|
||||
* {@link org.springframework.dao.InvalidDataAccessApiUsageException} in named parameter processing.
|
||||
*/
|
||||
@Nullable
|
||||
SettableValue getParameter(int index, String name);
|
||||
Parameter getParameter(int index, String name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2021 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.data.r2dbc.core;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* Contract for fetching tabular results.
|
||||
*
|
||||
* @param <T> row result type.
|
||||
* @author Mark Paluch
|
||||
* @deprecated since 1.2, use Spring's {@link org.springframework.r2dbc.core.RowsFetchSpec} support instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface RowsFetchSpec<T> {
|
||||
|
||||
/**
|
||||
* Get exactly zero or one result.
|
||||
*
|
||||
* @return {@link Mono#empty()} if no match found. Never {@literal null}.
|
||||
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException if more than one match found.
|
||||
*/
|
||||
Mono<T> one();
|
||||
|
||||
/**
|
||||
* Get the first or no result.
|
||||
*
|
||||
* @return {@link Mono#empty()} if no match found. Never {@literal null}.
|
||||
*/
|
||||
Mono<T> first();
|
||||
|
||||
/**
|
||||
* Get all matching elements.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
Flux<T> all();
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.core;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by objects that can provide SQL strings.
|
||||
* <p>
|
||||
* Typically implemented by objects that want to expose the SQL they use to create their statements, to allow for better
|
||||
* contextual information in case of exceptions.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Mark Paluch
|
||||
* @deprecated since 1.2, use Spring's {@link org.springframework.r2dbc.core.SqlProvider} support instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface SqlProvider extends org.springframework.r2dbc.core.SqlProvider {
|
||||
|
||||
/**
|
||||
* Return the SQL string for this object, i.e. typically the SQL used for creating statements.
|
||||
*
|
||||
* @return the SQL string, or {@literal null}.
|
||||
*/
|
||||
@Nullable
|
||||
String getSql();
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020-2021 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.data.r2dbc.core;
|
||||
|
||||
import io.r2dbc.spi.Result;
|
||||
import io.r2dbc.spi.Statement;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Represents a function that filters an {@link ExecuteFunction execute function}.
|
||||
* <p>
|
||||
* The filter is executed when a {@link org.reactivestreams.Subscriber} subscribes to the {@link Publisher} returned by
|
||||
* the {@link DatabaseClient}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 1.1
|
||||
* @see ExecuteFunction
|
||||
* @deprecated since 1.2, use Spring's {@link org.springframework.r2dbc.core.StatementFilterFunction} support instead.
|
||||
*/
|
||||
@Deprecated
|
||||
@FunctionalInterface
|
||||
public interface StatementFilterFunction {
|
||||
|
||||
/**
|
||||
* Apply this filter to the given {@link Statement} and {@link ExecuteFunction}.
|
||||
* <p>
|
||||
* The given {@link ExecuteFunction} represents the next entity in the chain, to be invoked via
|
||||
* {@link ExecuteFunction#execute(Statement)} invoked} in order to proceed with the exchange, or not invoked to
|
||||
* shortcut the chain.
|
||||
*
|
||||
* @param statement the current {@link Statement}.
|
||||
* @param next the next exchange function in the chain.
|
||||
* @return the filtered {@link Result}s.
|
||||
*/
|
||||
Publisher<? extends Result> filter(Statement statement, ExecuteFunction next);
|
||||
|
||||
/**
|
||||
* Return a composed filter function that first applies this filter, and then applies the given {@code "after"}
|
||||
* filter.
|
||||
*
|
||||
* @param afterFilter the filter to apply after this filter.
|
||||
* @return the composed filter.
|
||||
*/
|
||||
default StatementFilterFunction andThen(StatementFilterFunction afterFilter) {
|
||||
|
||||
Assert.notNull(afterFilter, "StatementFilterFunction must not be null");
|
||||
|
||||
return (request, next) -> filter(request, afterRequest -> afterFilter.filter(afterRequest, next));
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020-2021 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.data.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 1.1
|
||||
* @deprecated since 1.2, use Spring's org.springframework.r2dbc.core.StatementFilterFunctions support instead.
|
||||
*/
|
||||
@Deprecated
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,6 @@ import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.r2dbc.convert.R2dbcConverter;
|
||||
import org.springframework.data.r2dbc.dialect.R2dbcDialect;
|
||||
import org.springframework.data.r2dbc.mapping.SettableValue;
|
||||
import org.springframework.data.relational.core.query.Criteria;
|
||||
import org.springframework.data.relational.core.query.CriteriaDefinition;
|
||||
import org.springframework.data.relational.core.sql.Expression;
|
||||
@@ -477,19 +476,6 @@ public interface StatementMapper {
|
||||
return new InsertSpec(table, Collections.emptyMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate a column with a {@link SettableValue} and create a new {@link InsertSpec}.
|
||||
*
|
||||
* @param column
|
||||
* @param value
|
||||
* @return the {@link InsertSpec}.
|
||||
* @deprecated since 1.2, use {@link #withColumn(String, Parameter)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public InsertSpec withColumn(String column, SettableValue value) {
|
||||
return withColumn(SqlIdentifier.unquoted(column), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate a column with a {@link Parameter} and create a new {@link InsertSpec}.
|
||||
*
|
||||
@@ -502,19 +488,6 @@ public interface StatementMapper {
|
||||
return withColumn(SqlIdentifier.unquoted(column), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate a column with a {@link SettableValue} and create a new {@link InsertSpec}.
|
||||
*
|
||||
* @param column
|
||||
* @param value
|
||||
* @return the {@link InsertSpec}.
|
||||
* @deprecated since 1.2, use {@link #withColumn(SqlIdentifier, Parameter)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public InsertSpec withColumn(SqlIdentifier column, SettableValue value) {
|
||||
return withColumn(column, value.toParameter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate a column with a {@link Parameter} and create a new {@link InsertSpec}.
|
||||
*
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2021 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.data.r2dbc.core;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* Contract for fetching the number of affected rows.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @deprecated since 1.2, use Spring's {@link org.springframework.r2dbc.core.UpdatedRowsFetchSpec} support instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface UpdatedRowsFetchSpec {
|
||||
|
||||
/**
|
||||
* Get the number of updated rows.
|
||||
*
|
||||
* @return {@link Mono} emitting the number of updated rows. Never {@literal null}.
|
||||
*/
|
||||
Mono<Integer> rowsUpdated();
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package org.springframework.data.r2dbc.dialect;
|
||||
|
||||
import io.r2dbc.spi.Statement;
|
||||
|
||||
/**
|
||||
* A bind marker represents a single bindable parameter within a query. Bind markers are dialect-specific and provide a
|
||||
* {@link #getPlaceholder() placeholder} that is used in the actual query.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @see Statement#bind
|
||||
* @see BindMarkers
|
||||
* @see BindMarkersFactory
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC. Use {@link org.springframework.r2dbc.core.binding.BindMarker}
|
||||
* instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface BindMarker extends org.springframework.r2dbc.core.binding.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 {@literal null}. Use {@link #bindNull(BindTarget, Class)} for
|
||||
* {@literal null} values.
|
||||
* @see Statement#bind
|
||||
*/
|
||||
void bind(BindTarget bindTarget, Object value);
|
||||
|
||||
/**
|
||||
* Bind a {@literal null} value to the {@link Statement} using the underlying binding strategy.
|
||||
*
|
||||
* @param bindTarget the target to bind the value to.
|
||||
* @param valueType value type, must not be {@literal null}.
|
||||
* @see Statement#bindNull
|
||||
*/
|
||||
void bindNull(BindTarget bindTarget, Class<?> valueType);
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package org.springframework.data.r2dbc.dialect;
|
||||
|
||||
/**
|
||||
* Bind markers represent placeholders in SQL queries for substitution for an actual parameter. Using bind markers
|
||||
* allows creating safe queries so query strings are not required to contain escaped values but rather the driver
|
||||
* encodes parameter in the appropriate representation.
|
||||
* <p>
|
||||
* {@link BindMarkers} is stateful and can be only used for a single binding pass of one or more parameters. It
|
||||
* maintains bind indexes/bind parameter names.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @see BindMarker
|
||||
* @see BindMarkersFactory
|
||||
* @see io.r2dbc.spi.Statement#bind
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC. Use {@link org.springframework.r2dbc.core.binding.BindMarkers}
|
||||
* instead.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
@Deprecated
|
||||
public interface BindMarkers extends org.springframework.r2dbc.core.binding.BindMarkers {
|
||||
|
||||
/**
|
||||
* Creates a new {@link BindMarker}.
|
||||
*
|
||||
* @return a new {@link BindMarker}.
|
||||
*/
|
||||
BindMarker next();
|
||||
|
||||
/**
|
||||
* Creates a new {@link BindMarker} that accepts a {@code hint}. Implementations are allowed to consider/ignore/filter
|
||||
* the name hint to create more expressive bind markers.
|
||||
*
|
||||
* @param hint an optional name hint that can be used as part of the bind marker.
|
||||
* @return a new {@link BindMarker}.
|
||||
*/
|
||||
default BindMarker next(String hint) {
|
||||
return next();
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020-2021 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.data.r2dbc.dialect;
|
||||
|
||||
import org.springframework.r2dbc.core.binding.BindMarker;
|
||||
import org.springframework.r2dbc.core.binding.BindMarkers;
|
||||
import org.springframework.r2dbc.core.binding.BindTarget;
|
||||
|
||||
/**
|
||||
* Adapter to use Spring R2DBC's {@link org.springframework.r2dbc.core.binding.BindMarkers} exposing it as
|
||||
* {@link org.springframework.data.r2dbc.dialect.BindMarkers}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 1.2
|
||||
*/
|
||||
class BindMarkersAdapter implements org.springframework.data.r2dbc.dialect.BindMarkers {
|
||||
|
||||
private final BindMarkers delegate;
|
||||
|
||||
BindMarkersAdapter(BindMarkers delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.springframework.data.r2dbc.dialect.BindMarker next() {
|
||||
return new BindMarkerAdapter(delegate.next());
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.springframework.data.r2dbc.dialect.BindMarker next(String hint) {
|
||||
return new BindMarkerAdapter(delegate.next());
|
||||
}
|
||||
|
||||
static class BindMarkerAdapter implements org.springframework.data.r2dbc.dialect.BindMarker {
|
||||
|
||||
private final BindMarker delegate;
|
||||
|
||||
BindMarkerAdapter(BindMarker delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlaceholder() {
|
||||
return delegate.getPlaceholder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(org.springframework.data.r2dbc.dialect.BindTarget bindTarget, Object value) {
|
||||
delegate.bind(bindTarget, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindNull(org.springframework.data.r2dbc.dialect.BindTarget bindTarget, Class<?> valueType) {
|
||||
delegate.bindNull(bindTarget, valueType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(BindTarget bindTarget, Object value) {
|
||||
delegate.bind(bindTarget, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindNull(BindTarget bindTarget, Class<?> valueType) {
|
||||
delegate.bindNull(bindTarget, valueType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return delegate.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
package org.springframework.data.r2dbc.dialect;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* This class creates new {@link BindMarkers} instances to bind parameter for a specific {@link io.r2dbc.spi.Statement}.
|
||||
* <p>
|
||||
* Bind markers can be typically represented as placeholder and identifier. Placeholders are used within the query to
|
||||
* execute so the underlying database system can substitute the placeholder with the actual value. Identifiers are used
|
||||
* in R2DBC drivers to bind a value to a bind marker. Identifiers are typically a part of an entire bind marker when
|
||||
* using indexed or named bind markers.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @see BindMarkers
|
||||
* @see io.r2dbc.spi.Statement
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC. Use {@link org.springframework.r2dbc.core.binding.BindMarkersFactory}
|
||||
* instead.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
@Deprecated
|
||||
public interface BindMarkersFactory extends org.springframework.r2dbc.core.binding.BindMarkersFactory {
|
||||
|
||||
/**
|
||||
* Create a new {@link BindMarkers} instance.
|
||||
*
|
||||
* @return a new {@link BindMarkers} instance.
|
||||
*/
|
||||
BindMarkers create();
|
||||
|
||||
/**
|
||||
* Return whether the {@link BindMarkersFactory} uses identifiable placeholders.
|
||||
*
|
||||
* @return whether the {@link BindMarkersFactory} uses identifiable placeholders. {@literal false} if multiple
|
||||
* placeholders cannot be distinguished by just the {@link BindMarker#getPlaceholder() placeholder}
|
||||
* identifier.
|
||||
*/
|
||||
default boolean identifiablePlaceholders() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create index-based {@link BindMarkers} using indexes to bind parameters. Allow customization of the bind marker
|
||||
* placeholder {@code prefix} to represent the bind marker as placeholder within the query.
|
||||
*
|
||||
* @param prefix bind parameter prefix that is included in {@link BindMarker#getPlaceholder()} but not the actual
|
||||
* identifier.
|
||||
* @param beginWith the first index to use.
|
||||
* @return a {@link BindMarkersFactory} using {@code prefix} and {@code beginWith}.
|
||||
* @see io.r2dbc.spi.Statement#bindNull(int, Class)
|
||||
* @see io.r2dbc.spi.Statement#bind(int, Object)
|
||||
*/
|
||||
static BindMarkersFactory indexed(String prefix, int beginWith) {
|
||||
|
||||
Assert.notNull(prefix, "Prefix must not be null!");
|
||||
|
||||
org.springframework.r2dbc.core.binding.BindMarkersFactory factory = org.springframework.r2dbc.core.binding.BindMarkersFactory
|
||||
.indexed(prefix, beginWith);
|
||||
|
||||
return new BindMarkersFactory() {
|
||||
|
||||
@Override
|
||||
public BindMarkers create() {
|
||||
return new BindMarkersAdapter(factory.create());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean identifiablePlaceholders() {
|
||||
return factory.identifiablePlaceholders();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates anonymous, index-based bind marker using a static placeholder. Instances are bound by the ordinal position
|
||||
* ordered by the appearance of the placeholder. This implementation creates indexed bind markers using an anonymous
|
||||
* placeholder that correlates with an index.
|
||||
*
|
||||
* @param placeholder parameter placeholder.
|
||||
* @return a {@link BindMarkersFactory} using {@code placeholder}.
|
||||
* @see io.r2dbc.spi.Statement#bindNull(int, Class)
|
||||
* @see io.r2dbc.spi.Statement#bind(int, Object)
|
||||
*/
|
||||
static BindMarkersFactory anonymous(String placeholder) {
|
||||
|
||||
Assert.hasText(placeholder, "Placeholder must not be empty!");
|
||||
org.springframework.r2dbc.core.binding.BindMarkersFactory factory = org.springframework.r2dbc.core.binding.BindMarkersFactory
|
||||
.anonymous(placeholder);
|
||||
|
||||
return new BindMarkersFactory() {
|
||||
|
||||
@Override
|
||||
public BindMarkers create() {
|
||||
return new BindMarkersAdapter(factory.create());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean identifiablePlaceholders() {
|
||||
return factory.identifiablePlaceholders();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create named {@link BindMarkers} using identifiers to bind parameters. Named bind markers can support
|
||||
* {@link BindMarkers#next(String) name hints}. If no {@link BindMarkers#next(String) hint} is given, named bind
|
||||
* markers can use a counter or a random value source to generate unique bind markers.
|
||||
* <p>
|
||||
* Allow customization of the bind marker placeholder {@code prefix} and {@code namePrefix} to represent the bind
|
||||
* marker as placeholder within the query.
|
||||
*
|
||||
* @param prefix bind parameter prefix that is included in {@link BindMarker#getPlaceholder()} but not the actual
|
||||
* identifier.
|
||||
* @param namePrefix prefix for bind marker name that is included in {@link BindMarker#getPlaceholder()} and the
|
||||
* actual identifier.
|
||||
* @param maxLength maximal length of parameter names when using name hints.
|
||||
* @return a {@link BindMarkersFactory} using {@code prefix} and {@code beginWith}.
|
||||
* @see io.r2dbc.spi.Statement#bindNull(String, Class)
|
||||
* @see io.r2dbc.spi.Statement#bind(String, Object)
|
||||
*/
|
||||
static BindMarkersFactory named(String prefix, String namePrefix, int maxLength) {
|
||||
return named(prefix, namePrefix, maxLength, Function.identity());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create named {@link BindMarkers} using identifiers to bind parameters. Named bind markers can support
|
||||
* {@link BindMarkers#next(String) name hints}. If no {@link BindMarkers#next(String) hint} is given, named bind
|
||||
* markers can use a counter or a random value source to generate unique bind markers.
|
||||
*
|
||||
* @param prefix bind parameter prefix that is included in {@link BindMarker#getPlaceholder()} but not the actual
|
||||
* identifier.
|
||||
* @param namePrefix prefix for bind marker name that is included in {@link BindMarker#getPlaceholder()} and the
|
||||
* actual identifier.
|
||||
* @param maxLength maximal length of parameter names when using name hints.
|
||||
* @param hintFilterFunction filter {@link Function} to consider database-specific limitations in bind marker/variable
|
||||
* names such as ASCII chars only.
|
||||
* @return a {@link BindMarkersFactory} using {@code prefix} and {@code beginWith}.
|
||||
* @see io.r2dbc.spi.Statement#bindNull(String, Class)
|
||||
* @see io.r2dbc.spi.Statement#bind(String, Object)
|
||||
*/
|
||||
static BindMarkersFactory named(String prefix, String namePrefix, int maxLength,
|
||||
Function<String, String> hintFilterFunction) {
|
||||
|
||||
Assert.notNull(prefix, "Prefix must not be null!");
|
||||
Assert.notNull(namePrefix, "Index prefix must not be null!");
|
||||
Assert.notNull(hintFilterFunction, "Hint filter function must not be null!");
|
||||
|
||||
org.springframework.r2dbc.core.binding.BindMarkersFactory factory = org.springframework.r2dbc.core.binding.BindMarkersFactory
|
||||
.named(prefix, namePrefix, maxLength, hintFilterFunction);
|
||||
|
||||
return new BindMarkersFactory() {
|
||||
|
||||
@Override
|
||||
public BindMarkers create() {
|
||||
return new BindMarkersAdapter(factory.create());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean identifiablePlaceholders() {
|
||||
return factory.identifiablePlaceholders();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.dialect;
|
||||
|
||||
import org.springframework.data.r2dbc.core.PreparedOperation;
|
||||
|
||||
/**
|
||||
* Target to apply bindings to.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @see PreparedOperation
|
||||
* @see io.r2dbc.spi.Statement#bind
|
||||
* @see io.r2dbc.spi.Statement#bindNull
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC. Use {@link org.springframework.r2dbc.core.binding.BindTarget}
|
||||
* instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface BindTarget extends org.springframework.r2dbc.core.binding.BindTarget {
|
||||
|
||||
/**
|
||||
* Bind a value.
|
||||
*
|
||||
* @param identifier the identifier to bind to.
|
||||
* @param value the value to bind.
|
||||
*/
|
||||
void bind(String identifier, Object value);
|
||||
|
||||
/**
|
||||
* Bind a value to an index. Indexes are zero-based.
|
||||
*
|
||||
* @param index the index to bind to.
|
||||
* @param value the value to bind.
|
||||
*/
|
||||
void bind(int index, Object value);
|
||||
|
||||
/**
|
||||
* Bind a {@literal null} value.
|
||||
*
|
||||
* @param identifier the identifier to bind to.
|
||||
* @param type the type of {@literal null} value.
|
||||
*/
|
||||
void bindNull(String identifier, Class<?> type);
|
||||
|
||||
/**
|
||||
* Bind a {@literal null} value.
|
||||
*
|
||||
* @param index the index to bind to.
|
||||
* @param type the type of {@literal null} value.
|
||||
*/
|
||||
void bindNull(int index, Class<?> type);
|
||||
}
|
||||
@@ -1,289 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.dialect;
|
||||
|
||||
import io.r2dbc.spi.Statement;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Spliterator;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.springframework.data.util.Streamable;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.r2dbc.core.binding.BindMarker;
|
||||
import org.springframework.r2dbc.core.binding.BindMarkers;
|
||||
import org.springframework.r2dbc.core.binding.BindTarget;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Value object representing value and {@literal null} bindings for a {@link Statement} using {@link BindMarkers}.
|
||||
* Bindings are typically immutable.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC. Use {@link org.springframework.r2dbc.core.binding.Bindings} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class Bindings implements Streamable<Bindings.Binding> {
|
||||
|
||||
private static final Bindings EMPTY = new Bindings();
|
||||
|
||||
private final Map<BindMarker, Binding> bindings;
|
||||
|
||||
/**
|
||||
* Create empty {@link Bindings}.
|
||||
*/
|
||||
public Bindings() {
|
||||
this.bindings = Collections.emptyMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create {@link Bindings} from a {@link Map}.
|
||||
*
|
||||
* @param bindings must not be {@literal null}.
|
||||
*/
|
||||
public Bindings(Collection<Binding> bindings) {
|
||||
|
||||
Assert.notNull(bindings, "Bindings must not be null");
|
||||
|
||||
Map<BindMarker, Binding> mapping = new LinkedHashMap<>(bindings.size());
|
||||
bindings.forEach(it -> mapping.put(it.getBindMarker(), it));
|
||||
this.bindings = mapping;
|
||||
}
|
||||
|
||||
Bindings(Map<BindMarker, Binding> bindings) {
|
||||
this.bindings = bindings;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param other the object to merge with.
|
||||
* @return a new, merged {@link Bindings} object.
|
||||
*/
|
||||
public Bindings and(Bindings other) {
|
||||
return merge(this, other);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the bindings to a {@link BindTarget}.
|
||||
*
|
||||
* @param bindTarget the target to apply bindings to.
|
||||
*/
|
||||
public void apply(BindTarget bindTarget) {
|
||||
|
||||
Assert.notNull(bindTarget, "BindTarget must not be null");
|
||||
this.bindings.forEach((marker, binding) -> binding.apply(bindTarget));
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the given action for each binding of this {@link Bindings} until all bindings have been processed or the
|
||||
* action throws an exception. Actions are performed in the order of iteration (if an iteration order is specified).
|
||||
* Exceptions thrown by the action are relayed to the
|
||||
*
|
||||
* @param action The action to be performed for each {@link Binding}.
|
||||
*/
|
||||
public void forEach(Consumer<? super Binding> action) {
|
||||
this.bindings.forEach((marker, binding) -> action.accept(binding));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Iterable#iterator()
|
||||
*/
|
||||
@Override
|
||||
public Iterator<Binding> iterator() {
|
||||
return this.bindings.values().iterator();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Iterable#spliterator()
|
||||
*/
|
||||
@Override
|
||||
public Spliterator<Binding> spliterator() {
|
||||
return this.bindings.values().spliterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for value objects representing a value or a {@code NULL} binding.
|
||||
*/
|
||||
public abstract static class Binding {
|
||||
|
||||
private final BindMarker marker;
|
||||
|
||||
protected Binding(BindMarker marker) {
|
||||
this.marker = marker;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the associated {@link BindMarker}.
|
||||
*/
|
||||
public BindMarker getBindMarker() {
|
||||
return this.marker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@literal true} if there is a value present, otherwise {@literal false} for a {@code NULL} binding.
|
||||
*
|
||||
* @return {@literal true} if there is a value present, otherwise {@literal false} for a {@code NULL} binding.
|
||||
*/
|
||||
public abstract boolean hasValue();
|
||||
|
||||
/**
|
||||
* Return {@literal true} if this is is a {@code NULL} binding.
|
||||
*
|
||||
* @return {@literal true} if this is is a {@code NULL} binding.
|
||||
*/
|
||||
public boolean isNull() {
|
||||
return !hasValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this binding. Can be {@literal null} if this is a {@code NULL} binding.
|
||||
*
|
||||
* @return value of this binding. Can be {@literal null} if this is a {@code NULL} binding.
|
||||
*/
|
||||
@Nullable
|
||||
public abstract Object getValue();
|
||||
|
||||
/**
|
||||
* Applies the binding to a {@link BindTarget}.
|
||||
*
|
||||
* @param bindTarget the target to apply bindings to.
|
||||
*/
|
||||
public abstract void apply(BindTarget bindTarget);
|
||||
}
|
||||
|
||||
/**
|
||||
* Value binding.
|
||||
*/
|
||||
public static class ValueBinding extends Binding {
|
||||
|
||||
private final Object value;
|
||||
|
||||
public ValueBinding(BindMarker marker, Object value) {
|
||||
super(marker);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.query.Bindings.Binding#hasValue()
|
||||
*/
|
||||
public boolean hasValue() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.query.Bindings.Binding#getValue()
|
||||
*/
|
||||
public Object getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.query.Bindings.Binding#apply(io.r2dbc.spi.Statement)
|
||||
*/
|
||||
@Override
|
||||
public void apply(BindTarget bindTarget) {
|
||||
getBindMarker().bind(bindTarget, getValue());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code NULL} binding.
|
||||
*/
|
||||
public static class NullBinding extends Binding {
|
||||
|
||||
private final Class<?> valueType;
|
||||
|
||||
public NullBinding(BindMarker marker, Class<?> valueType) {
|
||||
super(marker);
|
||||
this.valueType = valueType;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.query.Bindings.Binding#hasValue()
|
||||
*/
|
||||
public boolean hasValue() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.query.Bindings.Binding#getValue()
|
||||
*/
|
||||
@Nullable
|
||||
public Object getValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Class<?> getValueType() {
|
||||
return this.valueType;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.query.Bindings.Binding#apply(BindTarget)
|
||||
*/
|
||||
@Override
|
||||
public void apply(BindTarget bindTarget) {
|
||||
getBindMarker().bindNull(bindTarget, getValueType());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.dialect;
|
||||
|
||||
import io.r2dbc.spi.Statement;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Mutable extension to {@link Bindings} for Value and {@literal null} bindings for a {@link Statement} using
|
||||
* {@link BindMarkers}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @deprecated since 1.2 in favor of Spring R2DBC. Use {@link org.springframework.r2dbc.core.binding.MutableBindings}
|
||||
* instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class MutableBindings extends Bindings {
|
||||
|
||||
private final BindMarkers markers;
|
||||
|
||||
/**
|
||||
* Create new {@link MutableBindings}.
|
||||
*
|
||||
* @param markers must not be {@literal null}.
|
||||
*/
|
||||
public MutableBindings(BindMarkers markers) {
|
||||
|
||||
super(new LinkedHashMap<>());
|
||||
|
||||
Assert.notNull(markers, "BindMarkers must not be null");
|
||||
|
||||
this.markers = markers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the next {@link BindMarker}. Increments {@link BindMarkers} state.
|
||||
*
|
||||
* @return the next {@link BindMarker}.
|
||||
*/
|
||||
public BindMarker nextMarker() {
|
||||
return this.markers.next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the next {@link BindMarker} with a name {@code hint}. Increments {@link BindMarkers} state.
|
||||
*
|
||||
* @param hint name hint.
|
||||
* @return the next {@link BindMarker}.
|
||||
*/
|
||||
public BindMarker nextMarker(String hint) {
|
||||
return this.markers.next(hint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind a value to {@link BindMarker}.
|
||||
*
|
||||
* @param marker must not be {@literal null}.
|
||||
* @param value must not be {@literal null}.
|
||||
* @return {@code this} {@link MutableBindings}.
|
||||
*/
|
||||
public MutableBindings bind(BindMarker marker, Object value) {
|
||||
|
||||
Assert.notNull(marker, "BindMarker must not be null");
|
||||
Assert.notNull(value, "Value must not be null");
|
||||
|
||||
getBindings().put(marker, new ValueBinding(marker, value));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind a value and return the related {@link BindMarker}. Increments {@link BindMarkers} state.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return {@code this} {@link MutableBindings}.
|
||||
*/
|
||||
public BindMarker bind(Object value) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null");
|
||||
|
||||
BindMarker marker = nextMarker();
|
||||
getBindings().put(marker, new ValueBinding(marker, value));
|
||||
|
||||
return marker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind a {@code NULL} value to {@link BindMarker}.
|
||||
*
|
||||
* @param marker must not be {@literal null}.
|
||||
* @param valueType must not be {@literal null}.
|
||||
* @return {@code this} {@link MutableBindings}.
|
||||
*/
|
||||
public MutableBindings bindNull(BindMarker marker, Class<?> valueType) {
|
||||
|
||||
Assert.notNull(marker, "BindMarker must not be null");
|
||||
Assert.notNull(valueType, "Value type must not be null");
|
||||
|
||||
getBindings().put(marker, new NullBinding(marker, valueType));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind a {@code NULL} value and return the related {@link BindMarker}. Increments {@link BindMarkers} state.
|
||||
*
|
||||
* @param valueType must not be {@literal null}.
|
||||
* @return {@code this} {@link MutableBindings}.
|
||||
*/
|
||||
public BindMarker bindNull(Class<?> valueType) {
|
||||
|
||||
Assert.notNull(valueType, "Value type must not be null");
|
||||
|
||||
BindMarker marker = nextMarker();
|
||||
getBindings().put(marker, new NullBinding(marker, valueType));
|
||||
|
||||
return marker;
|
||||
}
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2021 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.data.r2dbc.mapping;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.r2dbc.core.Parameter;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A database value that can be set in a statement.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @see OutboundRow
|
||||
* @deprecated since 1.2, use Spring R2DBC's {@link Parameter} directly.
|
||||
*/
|
||||
@Deprecated
|
||||
public class SettableValue {
|
||||
|
||||
private final Parameter parameter;
|
||||
|
||||
private SettableValue(Parameter parameter) {
|
||||
this.parameter = parameter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link SettableValue} from {@code value}.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return the {@link SettableValue} value for {@code value}.
|
||||
*/
|
||||
public static SettableValue from(Object value) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null");
|
||||
|
||||
return new SettableValue(Parameter.from(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link SettableValue} from {@code value} and {@code type}.
|
||||
*
|
||||
* @param value can be {@literal null}.
|
||||
* @param type must not be {@literal null}.
|
||||
* @return the {@link SettableValue} value for {@code value}.
|
||||
*/
|
||||
public static SettableValue fromOrEmpty(@Nullable Object value, Class<?> type) {
|
||||
return new SettableValue(Parameter.fromOrEmpty(value, type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new empty {@link SettableValue} for {@code type}.
|
||||
*
|
||||
* @return the empty {@link SettableValue} value for {@code type}.
|
||||
*/
|
||||
public static SettableValue empty(Class<?> type) {
|
||||
|
||||
Assert.notNull(type, "Type must not be null");
|
||||
|
||||
return new SettableValue(Parameter.empty(type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create a {@link SettableValue} from {@link Parameter}. Retains empty/type information.
|
||||
*
|
||||
* @param parameter the parameter to create a {@link SettableValue} from.
|
||||
* @return a new {@link SettableValue} from {@link Parameter}.
|
||||
* @since 1.4
|
||||
*/
|
||||
public static SettableValue fromParameter(Parameter parameter) {
|
||||
|
||||
Assert.notNull(parameter, "Parameter must not be null");
|
||||
|
||||
return parameter.isEmpty() ? SettableValue.empty(parameter.getType())
|
||||
: SettableValue.fromOrEmpty(parameter.getValue(), parameter.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the column value. Can be {@literal null}.
|
||||
*
|
||||
* @return the column value. Can be {@literal null}.
|
||||
* @see #hasValue()
|
||||
*/
|
||||
@Nullable
|
||||
public Object getValue() {
|
||||
return this.parameter.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the column value type. Must be also present if the {@code value} is {@literal null}.
|
||||
*
|
||||
* @return the column value type
|
||||
*/
|
||||
public Class<?> getType() {
|
||||
return this.parameter.getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this {@link SettableValue} has a value.
|
||||
*
|
||||
* @return whether this {@link SettableValue} has a value. {@literal false} if {@link #getValue()} is {@literal null}.
|
||||
*/
|
||||
public boolean hasValue() {
|
||||
return this.parameter.hasValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this {@link SettableValue} has a empty.
|
||||
*
|
||||
* @return whether this {@link SettableValue} is empty. {@literal true} if {@link #getValue()} is {@literal null}.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return this.parameter.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (!(o instanceof SettableValue))
|
||||
return false;
|
||||
SettableValue value1 = (SettableValue) o;
|
||||
return this.parameter.equals(value1.parameter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.parameter.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.parameter.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link Parameter} representing this settable value.
|
||||
* @since 1.2
|
||||
*/
|
||||
public Parameter toParameter() {
|
||||
return isEmpty() ? Parameter.empty(getType()) : Parameter.fromOrEmpty(getValue(), getType());
|
||||
}
|
||||
}
|
||||
@@ -1,705 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.query;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.relational.core.query.CriteriaDefinition;
|
||||
import org.springframework.data.relational.core.sql.SqlIdentifier;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Central class for creating queries. It follows a fluent API style so that you can easily chain together multiple
|
||||
* criteria. Static import of the {@code Criteria.property(…)} method will improve readability as in
|
||||
* {@code where(property(…).is(…)}.
|
||||
* <p>
|
||||
* The Criteria API supports composition with a {@link #empty() NULL object} and a {@link #from(List) static factory
|
||||
* method}. Example usage:
|
||||
*
|
||||
* <pre class="code">
|
||||
* Criteria.from(Criteria.where("name").is("Foo"), Criteria.from(Criteria.where("age").greaterThan(42)));
|
||||
* </pre>
|
||||
*
|
||||
* rendering:
|
||||
*
|
||||
* <pre class="code">
|
||||
* WHERE name = 'Foo' AND age > 42
|
||||
* </pre>
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Oliver Drotbohm
|
||||
* @deprecated since 1.1, use {@link org.springframework.data.relational.core.query.Criteria} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class Criteria implements CriteriaDefinition {
|
||||
|
||||
private static final Criteria EMPTY = new Criteria(SqlIdentifier.EMPTY, Comparator.INITIAL, null);
|
||||
|
||||
private final @Nullable Criteria previous;
|
||||
private final Combinator combinator;
|
||||
private final List<CriteriaDefinition> group;
|
||||
|
||||
private final @Nullable SqlIdentifier column;
|
||||
private final @Nullable Comparator comparator;
|
||||
private final @Nullable Object value;
|
||||
private final boolean ignoreCase;
|
||||
|
||||
private Criteria(SqlIdentifier column, Comparator comparator, @Nullable Object value) {
|
||||
this(null, Combinator.INITIAL, Collections.emptyList(), column, comparator, value, false);
|
||||
}
|
||||
|
||||
private Criteria(@Nullable Criteria previous, Combinator combinator, List<CriteriaDefinition> group,
|
||||
@Nullable SqlIdentifier column, @Nullable Comparator comparator, @Nullable Object value) {
|
||||
this(previous, combinator, group, column, comparator, value, false);
|
||||
}
|
||||
|
||||
private Criteria(@Nullable Criteria previous, Combinator combinator, List<CriteriaDefinition> group,
|
||||
@Nullable SqlIdentifier column, @Nullable Comparator comparator, @Nullable Object value, boolean ignoreCase) {
|
||||
|
||||
this.previous = previous;
|
||||
this.combinator = previous != null && previous.isEmpty() ? Combinator.INITIAL : combinator;
|
||||
this.group = group;
|
||||
this.column = column;
|
||||
this.comparator = comparator;
|
||||
this.value = value;
|
||||
this.ignoreCase = ignoreCase;
|
||||
}
|
||||
|
||||
private Criteria(@Nullable Criteria previous, Combinator combinator, List<CriteriaDefinition> group) {
|
||||
|
||||
this.previous = previous;
|
||||
this.combinator = previous != null && previous.isEmpty() ? Combinator.INITIAL : combinator;
|
||||
this.group = group;
|
||||
this.column = null;
|
||||
this.comparator = null;
|
||||
this.value = null;
|
||||
this.ignoreCase = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static factory method to create an empty Criteria.
|
||||
*
|
||||
* @return an empty {@link Criteria}.
|
||||
* @since 1.1
|
||||
*/
|
||||
public static Criteria empty() {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Criteria} and combine it as group with {@code AND} using the provided {@link List Criterias}.
|
||||
*
|
||||
* @return new {@link Criteria}.
|
||||
* @since 1.1
|
||||
*/
|
||||
public static Criteria from(Criteria... criteria) {
|
||||
|
||||
Assert.notNull(criteria, "Criteria must not be null");
|
||||
Assert.noNullElements(criteria, "Criteria must not contain null elements");
|
||||
|
||||
return from(Arrays.asList(criteria));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Criteria} and combine it as group with {@code AND} using the provided {@link List Criterias}.
|
||||
*
|
||||
* @return new {@link Criteria}.
|
||||
* @since 1.1
|
||||
*/
|
||||
public static Criteria from(List<Criteria> criteria) {
|
||||
|
||||
Assert.notNull(criteria, "Criteria must not be null");
|
||||
Assert.noNullElements(criteria, "Criteria must not contain null elements");
|
||||
|
||||
if (criteria.isEmpty()) {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
if (criteria.size() == 1) {
|
||||
return criteria.get(0);
|
||||
}
|
||||
|
||||
return EMPTY.and(criteria);
|
||||
}
|
||||
|
||||
/**
|
||||
* Static factory method to create a Criteria using the provided {@code column} name.
|
||||
*
|
||||
* @param column Must not be {@literal null} or empty.
|
||||
* @return a new {@link CriteriaStep} object to complete the first {@link Criteria}.
|
||||
*/
|
||||
public static CriteriaStep where(String column) {
|
||||
|
||||
Assert.hasText(column, "Column name must not be null or empty!");
|
||||
|
||||
return new DefaultCriteriaStep(SqlIdentifier.unquoted(column));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Criteria} and combine it with {@code AND} using the provided {@code column} name.
|
||||
*
|
||||
* @param column Must not be {@literal null} or empty.
|
||||
* @return a new {@link CriteriaStep} object to complete the next {@link Criteria}.
|
||||
*/
|
||||
public CriteriaStep and(String column) {
|
||||
|
||||
Assert.hasText(column, "Column name must not be null or empty!");
|
||||
|
||||
SqlIdentifier identifier = SqlIdentifier.unquoted(column);
|
||||
return new DefaultCriteriaStep(identifier) {
|
||||
@Override
|
||||
protected Criteria createCriteria(Comparator comparator, Object value) {
|
||||
return new Criteria(Criteria.this, Combinator.AND, Collections.emptyList(), identifier, comparator, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Criteria} and combine it as group with {@code AND} using the provided {@link Criteria} group.
|
||||
*
|
||||
* @param criteria criteria object.
|
||||
* @return a new {@link Criteria} object.
|
||||
* @since 1.1
|
||||
*/
|
||||
public Criteria and(Criteria criteria) {
|
||||
|
||||
Assert.notNull(criteria, "Criteria must not be null!");
|
||||
|
||||
return and(Collections.singletonList(criteria));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Criteria} and combine it as group with {@code AND} using the provided {@link Criteria} group.
|
||||
*
|
||||
* @param criteria criteria objects.
|
||||
* @return a new {@link Criteria} object.
|
||||
* @since 1.1
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public Criteria and(List<Criteria> criteria) {
|
||||
|
||||
Assert.notNull(criteria, "Criteria must not be null!");
|
||||
|
||||
return new Criteria(Criteria.this, Combinator.AND, (List) criteria);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Criteria} and combine it with {@code OR} using the provided {@code column} name.
|
||||
*
|
||||
* @param column Must not be {@literal null} or empty.
|
||||
* @return a new {@link CriteriaStep} object to complete the next {@link Criteria}.
|
||||
*/
|
||||
public CriteriaStep or(String column) {
|
||||
|
||||
Assert.hasText(column, "Column name must not be null or empty!");
|
||||
|
||||
SqlIdentifier identifier = SqlIdentifier.unquoted(column);
|
||||
return new DefaultCriteriaStep(identifier) {
|
||||
@Override
|
||||
protected Criteria createCriteria(Comparator comparator, Object value) {
|
||||
return new Criteria(Criteria.this, Combinator.OR, Collections.emptyList(), identifier, comparator, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Criteria} and combine it as group with {@code OR} using the provided {@link Criteria} group.
|
||||
*
|
||||
* @param criteria criteria object.
|
||||
* @return a new {@link Criteria} object.
|
||||
* @since 1.1
|
||||
*/
|
||||
public Criteria or(Criteria criteria) {
|
||||
|
||||
Assert.notNull(criteria, "Criteria must not be null!");
|
||||
|
||||
return or(Collections.singletonList(criteria));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Criteria} and combine it as group with {@code OR} using the provided {@link Criteria} group.
|
||||
*
|
||||
* @param criteria criteria object.
|
||||
* @return a new {@link Criteria} object.
|
||||
* @since 1.1
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public Criteria or(List<Criteria> criteria) {
|
||||
|
||||
Assert.notNull(criteria, "Criteria must not be null!");
|
||||
|
||||
return new Criteria(Criteria.this, Combinator.OR, (List) criteria);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Criteria} with the given "ignore case" flag.
|
||||
*
|
||||
* @param ignoreCase {@literal true} if comparison should be done in case-insensitive way
|
||||
* @return a new {@link Criteria} object
|
||||
*/
|
||||
public Criteria ignoreCase(boolean ignoreCase) {
|
||||
if (this.ignoreCase != ignoreCase) {
|
||||
return new Criteria(previous, combinator, group, column, comparator, value, ignoreCase);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the previous {@link Criteria} object. Can be {@literal null} if there is no previous {@link Criteria}.
|
||||
* @see #hasPrevious()
|
||||
*/
|
||||
@Nullable
|
||||
public Criteria getPrevious() {
|
||||
return previous;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@literal true} if this {@link Criteria} has a previous one.
|
||||
*/
|
||||
public boolean hasPrevious() {
|
||||
return previous != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@literal true} if this {@link Criteria} is empty.
|
||||
* @since 1.1
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
|
||||
if (!doIsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Criteria parent = this.previous;
|
||||
|
||||
while (parent != null) {
|
||||
|
||||
if (!parent.doIsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
parent = parent.previous;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean doIsEmpty() {
|
||||
|
||||
if (this.comparator == Comparator.INITIAL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.column != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (CriteriaDefinition criteria : group) {
|
||||
|
||||
if (!criteria.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@literal true} if this {@link Criteria} is empty.
|
||||
*/
|
||||
@Override
|
||||
public boolean isGroup() {
|
||||
return !this.group.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link Combinator} to combine this criteria with a previous one.
|
||||
*/
|
||||
@Override
|
||||
public Combinator getCombinator() {
|
||||
return combinator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CriteriaDefinition> getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the column/property name.
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public SqlIdentifier getColumn() {
|
||||
return column;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link Comparator}.
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public Comparator getComparator() {
|
||||
return comparator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the comparison value. Can be {@literal null}.
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether comparison should be done in case-insensitive way.
|
||||
*
|
||||
* @return {@literal true} if comparison should be done in case-insensitive way
|
||||
*/
|
||||
@Override
|
||||
public boolean isIgnoreCase() {
|
||||
return ignoreCase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface declaring terminal builder methods to build a {@link Criteria}.
|
||||
*/
|
||||
public interface CriteriaStep {
|
||||
|
||||
/**
|
||||
* Creates a {@link Criteria} using equality.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
*/
|
||||
Criteria is(Object value);
|
||||
|
||||
/**
|
||||
* Creates a {@link Criteria} using equality (is not).
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
*/
|
||||
Criteria not(Object value);
|
||||
|
||||
/**
|
||||
* Creates a {@link Criteria} using {@code IN}.
|
||||
*
|
||||
* @param values must not be {@literal null}.
|
||||
*/
|
||||
Criteria in(Object... values);
|
||||
|
||||
/**
|
||||
* Creates a {@link Criteria} using {@code IN}.
|
||||
*
|
||||
* @param values must not be {@literal null}.
|
||||
*/
|
||||
Criteria in(Collection<?> values);
|
||||
|
||||
/**
|
||||
* Creates a {@link Criteria} using {@code NOT IN}.
|
||||
*
|
||||
* @param values must not be {@literal null}.
|
||||
*/
|
||||
Criteria notIn(Object... values);
|
||||
|
||||
/**
|
||||
* Creates a {@link Criteria} using {@code NOT IN}.
|
||||
*
|
||||
* @param values must not be {@literal null}.
|
||||
*/
|
||||
Criteria notIn(Collection<?> values);
|
||||
|
||||
/**
|
||||
* Creates a {@link Criteria} using less-than ({@literal <}).
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
*/
|
||||
Criteria lessThan(Object value);
|
||||
|
||||
/**
|
||||
* Creates a {@link Criteria} using less-than or equal to ({@literal <=}).
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
*/
|
||||
Criteria lessThanOrEquals(Object value);
|
||||
|
||||
/**
|
||||
* Creates a {@link Criteria} using greater-than({@literal >}).
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
*/
|
||||
Criteria greaterThan(Object value);
|
||||
|
||||
/**
|
||||
* Creates a {@link Criteria} using greater-than or equal to ({@literal >=}).
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
*/
|
||||
Criteria greaterThanOrEquals(Object value);
|
||||
|
||||
/**
|
||||
* Creates a {@link Criteria} using {@code LIKE}.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
*/
|
||||
Criteria like(Object value);
|
||||
|
||||
/**
|
||||
* Creates a {@link Criteria} using {@code NOT LIKE}.
|
||||
*
|
||||
* @param value must not be {@literal null}
|
||||
* @return a new {@link Criteria} object
|
||||
*/
|
||||
Criteria notLike(Object value);
|
||||
|
||||
/**
|
||||
* Creates a {@link Criteria} using {@code IS NULL}.
|
||||
*/
|
||||
Criteria isNull();
|
||||
|
||||
/**
|
||||
* Creates a {@link Criteria} using {@code IS NOT NULL}.
|
||||
*/
|
||||
Criteria isNotNull();
|
||||
|
||||
/**
|
||||
* Creates a {@link Criteria} using {@code IS TRUE}.
|
||||
*
|
||||
* @return a new {@link Criteria} object
|
||||
*/
|
||||
Criteria isTrue();
|
||||
|
||||
/**
|
||||
* Creates a {@link Criteria} using {@code IS FALSE}.
|
||||
*
|
||||
* @return a new {@link Criteria} object
|
||||
*/
|
||||
Criteria isFalse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Default {@link CriteriaStep} implementation.
|
||||
*/
|
||||
static class DefaultCriteriaStep implements CriteriaStep {
|
||||
|
||||
private final SqlIdentifier property;
|
||||
|
||||
DefaultCriteriaStep(SqlIdentifier property) {
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#is(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Criteria is(Object value) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
|
||||
return createCriteria(Comparator.EQ, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#not(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Criteria not(Object value) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
|
||||
return createCriteria(Comparator.NEQ, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#in(java.lang.Object[])
|
||||
*/
|
||||
@Override
|
||||
public Criteria in(Object... values) {
|
||||
|
||||
Assert.notNull(values, "Values must not be null!");
|
||||
Assert.noNullElements(values, "Values must not contain a null value!");
|
||||
|
||||
if (values.length > 1 && values[1] instanceof Collection) {
|
||||
throw new InvalidDataAccessApiUsageException(
|
||||
"You can only pass in one argument of type " + values[1].getClass().getName());
|
||||
}
|
||||
|
||||
return createCriteria(Comparator.IN, Arrays.asList(values));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#in(java.util.Collection)
|
||||
*/
|
||||
@Override
|
||||
public Criteria in(Collection<?> values) {
|
||||
|
||||
Assert.notNull(values, "Values must not be null!");
|
||||
Assert.noNullElements(values.toArray(), "Values must not contain a null value!");
|
||||
|
||||
return createCriteria(Comparator.IN, values);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#notIn(java.lang.Object[])
|
||||
*/
|
||||
@Override
|
||||
public Criteria notIn(Object... values) {
|
||||
|
||||
Assert.notNull(values, "Values must not be null!");
|
||||
Assert.noNullElements(values, "Values must not contain a null value!");
|
||||
|
||||
if (values.length > 1 && values[1] instanceof Collection) {
|
||||
throw new InvalidDataAccessApiUsageException(
|
||||
"You can only pass in one argument of type " + values[1].getClass().getName());
|
||||
}
|
||||
|
||||
return createCriteria(Comparator.NOT_IN, Arrays.asList(values));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#notIn(java.util.Collection)
|
||||
*/
|
||||
@Override
|
||||
public Criteria notIn(Collection<?> values) {
|
||||
|
||||
Assert.notNull(values, "Values must not be null!");
|
||||
Assert.noNullElements(values.toArray(), "Values must not contain a null value!");
|
||||
|
||||
return createCriteria(Comparator.NOT_IN, values);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#lessThan(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Criteria lessThan(Object value) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
|
||||
return createCriteria(Comparator.LT, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#lessThanOrEquals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Criteria lessThanOrEquals(Object value) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
|
||||
return createCriteria(Comparator.LTE, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#greaterThan(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Criteria greaterThan(Object value) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
|
||||
return createCriteria(Comparator.GT, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#greaterThanOrEquals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Criteria greaterThanOrEquals(Object value) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
|
||||
return createCriteria(Comparator.GTE, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#like(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Criteria like(Object value) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
|
||||
return createCriteria(Comparator.LIKE, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#notLike(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Criteria notLike(Object value) {
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
return createCriteria(Comparator.NOT_LIKE, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#isNull()
|
||||
*/
|
||||
@Override
|
||||
public Criteria isNull() {
|
||||
return createCriteria(Comparator.IS_NULL, null);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#isNotNull()
|
||||
*/
|
||||
@Override
|
||||
public Criteria isNotNull() {
|
||||
return createCriteria(Comparator.IS_NOT_NULL, null);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#isTrue()
|
||||
*/
|
||||
@Override
|
||||
public Criteria isTrue() {
|
||||
return createCriteria(Comparator.IS_TRUE, null);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#isFalse()
|
||||
*/
|
||||
@Override
|
||||
public Criteria isFalse() {
|
||||
return createCriteria(Comparator.IS_FALSE, null);
|
||||
}
|
||||
|
||||
protected Criteria createCriteria(Comparator comparator, Object value) {
|
||||
return new Criteria(this.property, comparator, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,10 +30,10 @@ import org.springframework.data.mapping.PropertyReferenceException;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.r2dbc.convert.R2dbcConverter;
|
||||
import org.springframework.data.r2dbc.dialect.R2dbcDialect;
|
||||
import org.springframework.data.r2dbc.mapping.SettableValue;
|
||||
import org.springframework.data.relational.core.dialect.Escaper;
|
||||
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
|
||||
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
|
||||
import org.springframework.data.relational.core.query.Criteria;
|
||||
import org.springframework.data.relational.core.query.CriteriaDefinition;
|
||||
import org.springframework.data.relational.core.query.CriteriaDefinition.Comparator;
|
||||
import org.springframework.data.relational.core.query.ValueFunction;
|
||||
@@ -347,13 +347,7 @@ public class QueryMapper {
|
||||
Object mappedValue;
|
||||
Class<?> typeHint;
|
||||
|
||||
if (criteria.getValue() instanceof SettableValue) {
|
||||
|
||||
SettableValue settableValue = (SettableValue) criteria.getValue();
|
||||
|
||||
mappedValue = convertValue(settableValue.getValue(), propertyField.getTypeHint());
|
||||
typeHint = getTypeHint(mappedValue, actualType.getType(), settableValue);
|
||||
} else if (criteria.getValue() instanceof Parameter) {
|
||||
if (criteria.getValue() instanceof Parameter) {
|
||||
|
||||
Parameter parameter = (Parameter) criteria.getValue();
|
||||
|
||||
@@ -384,21 +378,6 @@ public class QueryMapper {
|
||||
return Escaper.DEFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Potentially convert the {@link SettableValue}.
|
||||
*
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public SettableValue getBindValue(SettableValue value) {
|
||||
|
||||
if (value.isEmpty()) {
|
||||
return SettableValue.empty(converter.getTargetType(value.getType()));
|
||||
}
|
||||
|
||||
return SettableValue.from(convertValue(value.getValue(), ClassTypeInformation.OBJECT));
|
||||
}
|
||||
|
||||
/**
|
||||
* Potentially convert the {@link Parameter}.
|
||||
*
|
||||
@@ -587,19 +566,6 @@ public class QueryMapper {
|
||||
return propertyType;
|
||||
}
|
||||
|
||||
Class<?> getTypeHint(@Nullable Object mappedValue, Class<?> propertyType, SettableValue settableValue) {
|
||||
|
||||
if (mappedValue == null || propertyType.equals(Object.class)) {
|
||||
return settableValue.getType();
|
||||
}
|
||||
|
||||
if (mappedValue.getClass().equals(settableValue.getValue().getClass())) {
|
||||
return settableValue.getType();
|
||||
}
|
||||
|
||||
return propertyType;
|
||||
}
|
||||
|
||||
Class<?> getTypeHint(@Nullable Object mappedValue, Class<?> propertyType, Parameter parameter) {
|
||||
|
||||
if (mappedValue == null || propertyType.equals(Object.class)) {
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.query;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.relational.core.sql.SqlIdentifier;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Class to easily construct SQL update assignments.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Oliver Drotbohm
|
||||
* @deprecated since 1.1, use {@link org.springframework.data.relational.core.query.Update} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class Update {
|
||||
|
||||
private static final Update EMPTY = new Update(Collections.emptyMap());
|
||||
|
||||
private final Map<SqlIdentifier, Object> columnsToUpdate;
|
||||
|
||||
private Update(Map<SqlIdentifier, Object> columnsToUpdate) {
|
||||
this.columnsToUpdate = columnsToUpdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static factory method to create an {@link Update} using the provided column.
|
||||
*
|
||||
* @param column must not be {@literal null}.
|
||||
* @param value can be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Update update(String column, @Nullable Object value) {
|
||||
return EMPTY.set(column, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a column by assigning a value.
|
||||
*
|
||||
* @param column must not be {@literal null}.
|
||||
* @param value can be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Update set(String column, @Nullable Object value) {
|
||||
|
||||
Assert.hasText(column, "Column for update must not be null or blank");
|
||||
|
||||
return addMultiFieldOperation(SqlIdentifier.unquoted(column), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a column by assigning a value.
|
||||
*
|
||||
* @param column must not be {@literal null}.
|
||||
* @param value can be {@literal null}.
|
||||
* @return
|
||||
* @since 1.1
|
||||
*/
|
||||
public Update set(SqlIdentifier column, @Nullable Object value) {
|
||||
return addMultiFieldOperation(column, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all assignments.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Map<SqlIdentifier, Object> getAssignments() {
|
||||
return Collections.unmodifiableMap(this.columnsToUpdate);
|
||||
}
|
||||
|
||||
private Update addMultiFieldOperation(SqlIdentifier key, @Nullable Object value) {
|
||||
|
||||
Assert.notNull(key, "Column for update must not be null");
|
||||
|
||||
Map<SqlIdentifier, Object> updates = new LinkedHashMap<>(this.columnsToUpdate);
|
||||
updates.put(key, value);
|
||||
|
||||
return new Update(updates);
|
||||
}
|
||||
}
|
||||
@@ -20,14 +20,10 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.r2dbc.convert.R2dbcConverter;
|
||||
import org.springframework.r2dbc.core.Parameter;
|
||||
import org.springframework.r2dbc.core.binding.BindMarkers;
|
||||
import org.springframework.r2dbc.core.binding.Bindings;
|
||||
import org.springframework.r2dbc.core.binding.MutableBindings;
|
||||
import org.springframework.data.r2dbc.dialect.R2dbcDialect;
|
||||
import org.springframework.data.r2dbc.mapping.SettableValue;
|
||||
import org.springframework.data.relational.core.dialect.Escaper;
|
||||
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
|
||||
import org.springframework.data.relational.core.query.Update;
|
||||
import org.springframework.data.relational.core.query.ValueFunction;
|
||||
import org.springframework.data.relational.core.sql.AssignValue;
|
||||
import org.springframework.data.relational.core.sql.Assignment;
|
||||
@@ -38,7 +34,11 @@ import org.springframework.data.relational.core.sql.SqlIdentifier;
|
||||
import org.springframework.data.relational.core.sql.Table;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.r2dbc.core.Parameter;
|
||||
import org.springframework.r2dbc.core.binding.BindMarker;
|
||||
import org.springframework.r2dbc.core.binding.BindMarkers;
|
||||
import org.springframework.r2dbc.core.binding.Bindings;
|
||||
import org.springframework.r2dbc.core.binding.MutableBindings;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -58,24 +58,6 @@ public class UpdateMapper extends QueryMapper {
|
||||
super(dialect, converter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a {@link Update} object to {@link BoundAssignments} and consider value/{@code NULL} {@link Bindings}.
|
||||
*
|
||||
* @param markers bind markers object, must not be {@literal null}.
|
||||
* @param update update definition to map, must not be {@literal null}.
|
||||
* @param table must not be {@literal null}.
|
||||
* @param entity related {@link RelationalPersistentEntity}, can be {@literal null}.
|
||||
* @return the mapped {@link BoundAssignments}.
|
||||
* @deprecated since 1.1, use
|
||||
* {@link #getMappedObject(BindMarkers, org.springframework.data.relational.core.query.Update, Table, RelationalPersistentEntity)}
|
||||
* instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public BoundAssignments getMappedObject(BindMarkers markers, Update update, Table table,
|
||||
@Nullable RelationalPersistentEntity<?> entity) {
|
||||
return getMappedObject(markers, update.getAssignments(), table, entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a {@link org.springframework.data.relational.core.query.Update} object to {@link BoundAssignments} and consider
|
||||
* value/{@code NULL} {@link Bindings}.
|
||||
@@ -87,8 +69,7 @@ public class UpdateMapper extends QueryMapper {
|
||||
* @return the mapped {@link BoundAssignments}.
|
||||
* @since 1.1
|
||||
*/
|
||||
public BoundAssignments getMappedObject(BindMarkers markers,
|
||||
org.springframework.data.relational.core.query.Update update, Table table,
|
||||
public BoundAssignments getMappedObject(BindMarkers markers, Update update, Table table,
|
||||
@Nullable RelationalPersistentEntity<?> entity) {
|
||||
return getMappedObject(markers, update.getAssignments(), table, entity);
|
||||
}
|
||||
@@ -130,14 +111,7 @@ public class UpdateMapper extends QueryMapper {
|
||||
Object mappedValue;
|
||||
Class<?> typeHint;
|
||||
|
||||
if (value instanceof SettableValue) {
|
||||
|
||||
SettableValue settableValue = (SettableValue) value;
|
||||
|
||||
mappedValue = convertValue(settableValue.getValue(), propertyField.getTypeHint());
|
||||
typeHint = getTypeHint(mappedValue, actualType.getType(), settableValue);
|
||||
|
||||
} else if (value instanceof Parameter) {
|
||||
if (value instanceof Parameter) {
|
||||
|
||||
Parameter parameter = (Parameter) value;
|
||||
|
||||
|
||||
@@ -116,22 +116,9 @@ public @interface EnableR2dbcRepositories {
|
||||
*/
|
||||
Class<?> repositoryBaseClass() default DefaultRepositoryBaseClass.class;
|
||||
|
||||
/**
|
||||
* Configures the name of the {@link org.springframework.data.r2dbc.core.DatabaseClient} bean to be used with the
|
||||
* repositories detected.
|
||||
*
|
||||
* @return
|
||||
* @see #entityOperationsRef()
|
||||
* @deprecated since 1.2, in favor of {@link #entityOperationsRef()}.
|
||||
*/
|
||||
@Deprecated
|
||||
String databaseClientRef() default "";
|
||||
|
||||
/**
|
||||
* Configures the name of the {@link org.springframework.data.r2dbc.core.R2dbcEntityOperations} bean to be used with
|
||||
* the repositories detected. Used as alternative to {@link #databaseClientRef()} to configure an access strategy when
|
||||
* using repositories with different database systems/dialects. If this attribute is set, then
|
||||
* {@link #databaseClientRef()} is ignored.
|
||||
* the repositories detected.
|
||||
*
|
||||
* @return
|
||||
* @since 1.1.3
|
||||
|
||||
@@ -29,7 +29,6 @@ import org.springframework.data.repository.config.RepositoryConfigurationExtensi
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport;
|
||||
import org.springframework.data.repository.config.XmlRepositoryConfigurationSource;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Reactive {@link RepositoryConfigurationExtension} for R2DBC.
|
||||
@@ -98,13 +97,7 @@ public class R2dbcRepositoryConfigurationExtension extends RepositoryConfigurati
|
||||
|
||||
AnnotationAttributes attributes = config.getAttributes();
|
||||
|
||||
String databaseClientRef = attributes.getString("databaseClientRef");
|
||||
if (StringUtils.hasText(databaseClientRef)) {
|
||||
builder.addPropertyReference("databaseClient", attributes.getString("databaseClientRef"));
|
||||
builder.addPropertyReference("dataAccessStrategy", "reactiveDataAccessStrategy");
|
||||
} else {
|
||||
builder.addPropertyReference("entityOperations", attributes.getString("entityOperationsRef"));
|
||||
}
|
||||
builder.addPropertyReference("entityOperations", attributes.getString("entityOperationsRef"));
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -128,8 +128,6 @@ class ExpressionEvaluatingParameterBinder {
|
||||
return dataAccessStrategy.getBindValue(parameter);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private org.springframework.r2dbc.core.Parameter getBindValue(org.springframework.r2dbc.core.Parameter bindValue) {
|
||||
return dataAccessStrategy.getBindValue(bindValue);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ import org.springframework.data.r2dbc.convert.R2dbcConverter;
|
||||
import org.springframework.data.r2dbc.core.R2dbcEntityOperations;
|
||||
import org.springframework.data.r2dbc.core.ReactiveDataAccessStrategy;
|
||||
import org.springframework.data.r2dbc.dialect.BindTargetBinder;
|
||||
import org.springframework.data.r2dbc.mapping.SettableValue;
|
||||
import org.springframework.data.r2dbc.repository.Query;
|
||||
import org.springframework.data.relational.repository.query.RelationalParameterAccessor;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
@@ -202,12 +201,12 @@ public class StringBasedR2dbcQuery extends AbstractR2dbcQuery {
|
||||
|
||||
if (recordedBindings.byName.containsKey(name)) {
|
||||
remainderByName.remove(name);
|
||||
return SettableValue.fromParameter(recordedBindings.byName.get(name));
|
||||
return recordedBindings.byName.get(name);
|
||||
}
|
||||
|
||||
if (recordedBindings.byIndex.containsKey(index)) {
|
||||
remainderByIndex.remove(index);
|
||||
return SettableValue.fromParameter(recordedBindings.byIndex.get(index));
|
||||
return recordedBindings.byIndex.get(index);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -253,11 +252,6 @@ public class StringBasedR2dbcQuery extends AbstractR2dbcQuery {
|
||||
|
||||
@NotNull
|
||||
private Parameter toParameter(Object value) {
|
||||
|
||||
if (value instanceof SettableValue) {
|
||||
return ((SettableValue) value).toParameter();
|
||||
}
|
||||
|
||||
return value instanceof Parameter ? (Parameter) value : Parameter.from(value);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
package org.springframework.data.r2dbc.repository.support;
|
||||
|
||||
import io.r2dbc.spi.Result;
|
||||
import io.r2dbc.spi.Statement;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.data.r2dbc.core.DatabaseClient.BindSpec;
|
||||
|
||||
/**
|
||||
* Adapter for {@link BindSpec} to be used with {@link org.springframework.data.r2dbc.dialect.BindMarker} binding.
|
||||
* Binding parameters updates the {@link BindSpec}
|
||||
*
|
||||
* @param <S> type of the bind specification.
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class BindSpecAdapter<S extends BindSpec<S>> implements Statement {
|
||||
|
||||
private S bindSpec;
|
||||
|
||||
private BindSpecAdapter(S bindSpec) {
|
||||
this.bindSpec = bindSpec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link BindSpecAdapter} for the given {@link BindSpec}.
|
||||
*
|
||||
* @param bindSpec the bind specification.
|
||||
* @param <S> type of the bind spec to retain the type through {@link #getBoundOperation()}.
|
||||
* @return {@link BindSpecAdapter} for the {@link BindSpec}.
|
||||
*/
|
||||
public static <S extends BindSpec<S>> BindSpecAdapter<S> create(S bindSpec) {
|
||||
return new BindSpecAdapter<>(bindSpec);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see io.r2dbc.spi.Statement#add()
|
||||
*/
|
||||
@Override
|
||||
public BindSpecAdapter<S> add() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see io.r2dbc.spi.Statement#execute()
|
||||
*/
|
||||
@Override
|
||||
public Publisher<? extends Result> execute() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see io.r2dbc.spi.Statement#bind(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public BindSpecAdapter<S> bind(String identifier, Object value) {
|
||||
|
||||
this.bindSpec = bindSpec.bind(identifier, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see io.r2dbc.spi.Statement#bind(int, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public BindSpecAdapter<S> bind(int index, Object value) {
|
||||
|
||||
this.bindSpec = bindSpec.bind(index, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see io.r2dbc.spi.Statement#bindNull(java.lang.Object, java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public BindSpecAdapter<S> bindNull(String identifier, Class<?> type) {
|
||||
|
||||
this.bindSpec = bindSpec.bindNull(identifier, type);
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see io.r2dbc.spi.Statement#bindNull(int, java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public BindSpecAdapter<S> bindNull(int index, Class<?> type) {
|
||||
|
||||
this.bindSpec = bindSpec.bindNull(index, type);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the bound (final) bind specification.
|
||||
*/
|
||||
public S getBoundOperation() {
|
||||
return bindSpec;
|
||||
}
|
||||
}
|
||||
@@ -105,29 +105,6 @@ public class SimpleR2dbcRepository<T, ID> implements R2dbcRepository<T, ID> {
|
||||
this.exampleMapper = new RelationalExampleMapper(converter.getMappingContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link SimpleR2dbcRepository}.
|
||||
*
|
||||
* @param entity
|
||||
* @param databaseClient
|
||||
* @param converter
|
||||
* @param accessStrategy
|
||||
* @deprecated since 1.2.
|
||||
*/
|
||||
@Deprecated
|
||||
public SimpleR2dbcRepository(RelationalEntityInformation<T, ID> entity,
|
||||
org.springframework.data.r2dbc.core.DatabaseClient databaseClient, R2dbcConverter converter,
|
||||
ReactiveDataAccessStrategy accessStrategy) {
|
||||
|
||||
this.entity = entity;
|
||||
this.entityOperations = new R2dbcEntityTemplate(databaseClient, accessStrategy);
|
||||
this.idProperty = Lazy.of(() -> converter //
|
||||
.getMappingContext() //
|
||||
.getRequiredPersistentEntity(this.entity.getJavaType()) //
|
||||
.getRequiredIdProperty());
|
||||
this.exampleMapper = new RelationalExampleMapper(converter.getMappingContext());
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Methods from ReactiveCrudRepository
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2021 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.data.r2dbc.support;
|
||||
|
||||
import io.r2dbc.spi.R2dbcException;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.r2dbc.UncategorizedR2dbcException;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base class for {@link R2dbcExceptionTranslator} implementations that allow for fallback to some other
|
||||
* {@link R2dbcExceptionTranslator}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @deprecated since 1.2. Use Spring R2DBC's
|
||||
* {@link org.springframework.r2dbc.connection.ConnectionFactoryUtils#convertR2dbcException(String, String, R2dbcException)}
|
||||
* instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class AbstractFallbackR2dbcExceptionTranslator implements R2dbcExceptionTranslator {
|
||||
|
||||
/** Logger available to subclasses */
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
@Nullable private R2dbcExceptionTranslator fallbackTranslator;
|
||||
|
||||
/**
|
||||
* Override the default SQL state fallback translator (typically a {@link R2dbcExceptionTranslator}).
|
||||
*/
|
||||
public void setFallbackTranslator(@Nullable R2dbcExceptionTranslator fallback) {
|
||||
this.fallbackTranslator = fallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the fallback exception translator, if any.
|
||||
*/
|
||||
@Nullable
|
||||
public R2dbcExceptionTranslator getFallbackTranslator() {
|
||||
return this.fallbackTranslator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-checks the arguments, calls {@link #doTranslate}, and invokes the {@link #getFallbackTranslator() fallback
|
||||
* translator} if necessary.
|
||||
*/
|
||||
@Override
|
||||
@NonNull
|
||||
public DataAccessException translate(String task, @Nullable String sql, R2dbcException ex) {
|
||||
|
||||
Assert.notNull(ex, "Cannot translate a null R2dbcException");
|
||||
|
||||
DataAccessException dae = doTranslate(task, sql, ex);
|
||||
if (dae != null) {
|
||||
// Specific exception match found.
|
||||
return dae;
|
||||
}
|
||||
|
||||
// Looking for a fallback...
|
||||
R2dbcExceptionTranslator fallback = getFallbackTranslator();
|
||||
if (fallback != null) {
|
||||
dae = fallback.translate(task, sql, ex);
|
||||
if (dae != null) {
|
||||
// Fallback exception match found.
|
||||
return dae;
|
||||
}
|
||||
}
|
||||
|
||||
// We couldn't identify it more precisely.
|
||||
return new UncategorizedR2dbcException(task, sql, ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method for actually translating the given exception.
|
||||
* <p>
|
||||
* The passed-in arguments will have been pre-checked. Furthermore, this method is allowed to return {@literal null}
|
||||
* to indicate that no exception match has been found and that fallback translation should kick in.
|
||||
*
|
||||
* @param task readable text describing the task being attempted.
|
||||
* @param sql SQL query or update that caused the problem (if known).
|
||||
* @param ex the offending {@link R2dbcException}.
|
||||
* @return the DataAccessException, wrapping the {@link R2dbcException}; or {@literal null} if no exception match
|
||||
* found.
|
||||
*/
|
||||
@Nullable
|
||||
protected abstract DataAccessException doTranslate(String task, @Nullable String sql, R2dbcException ex);
|
||||
|
||||
/**
|
||||
* Build a message {@code String} for the given {@link R2dbcException}.
|
||||
* <p>
|
||||
* To be called by translator subclasses when creating an instance of a generic
|
||||
* {@link org.springframework.dao.DataAccessException} class.
|
||||
*
|
||||
* @param task readable text describing the task being attempted.
|
||||
* @param sql the SQL statement that caused the problem.
|
||||
* @param ex the offending {@link R2dbcException}.
|
||||
* @return the message {@code String} to use.
|
||||
*/
|
||||
protected String buildMessage(String task, @Nullable String sql, R2dbcException ex) {
|
||||
|
||||
return task + "; " + //
|
||||
(sql != null //
|
||||
? "SQL [" + sql + "]; " //
|
||||
: "" //
|
||||
) + ex.getMessage();
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.support;
|
||||
|
||||
import io.r2dbc.spi.R2dbcBadGrammarException;
|
||||
import io.r2dbc.spi.R2dbcDataIntegrityViolationException;
|
||||
import io.r2dbc.spi.R2dbcException;
|
||||
import io.r2dbc.spi.R2dbcNonTransientException;
|
||||
import io.r2dbc.spi.R2dbcNonTransientResourceException;
|
||||
import io.r2dbc.spi.R2dbcPermissionDeniedException;
|
||||
import io.r2dbc.spi.R2dbcRollbackException;
|
||||
import io.r2dbc.spi.R2dbcTimeoutException;
|
||||
import io.r2dbc.spi.R2dbcTransientException;
|
||||
import io.r2dbc.spi.R2dbcTransientResourceException;
|
||||
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.dao.PermissionDeniedDataAccessException;
|
||||
import org.springframework.dao.QueryTimeoutException;
|
||||
import org.springframework.dao.TransientDataAccessResourceException;
|
||||
import org.springframework.data.r2dbc.BadSqlGrammarException;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* {@link R2dbcExceptionTranslator} implementation which analyzes the specific {@link R2dbcException} subclass thrown by
|
||||
* the R2DBC driver.
|
||||
* <p>
|
||||
* Falls back to a standard {@link SqlStateR2dbcExceptionTranslator}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @deprecated since 1.2. Use Spring R2DBC's
|
||||
* {@link org.springframework.r2dbc.connection.ConnectionFactoryUtils#convertR2dbcException(String, String, R2dbcException)}
|
||||
* instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class R2dbcExceptionSubclassTranslator extends AbstractFallbackR2dbcExceptionTranslator {
|
||||
|
||||
public R2dbcExceptionSubclassTranslator() {
|
||||
setFallbackTranslator(new SqlStateR2dbcExceptionTranslator());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.support.AbstractFallbackR2dbcExceptionTranslator#doTranslate(java.lang.String, java.lang.String, io.r2dbc.spi.R2dbcException)
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
protected DataAccessException doTranslate(String task, @Nullable String sql, R2dbcException ex) {
|
||||
|
||||
if (ex instanceof R2dbcTransientException) {
|
||||
if (ex instanceof R2dbcTransientResourceException) {
|
||||
return new TransientDataAccessResourceException(buildMessage(task, sql, ex), ex);
|
||||
}
|
||||
if (ex instanceof R2dbcRollbackException) {
|
||||
return new ConcurrencyFailureException(buildMessage(task, sql, ex), ex);
|
||||
}
|
||||
if (ex instanceof R2dbcTimeoutException) {
|
||||
return new QueryTimeoutException(buildMessage(task, sql, ex), ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (ex instanceof R2dbcNonTransientException) {
|
||||
if (ex instanceof R2dbcNonTransientResourceException) {
|
||||
return new DataAccessResourceFailureException(buildMessage(task, sql, ex), ex);
|
||||
}
|
||||
if (ex instanceof R2dbcDataIntegrityViolationException) {
|
||||
return new DataIntegrityViolationException(buildMessage(task, sql, ex), ex);
|
||||
}
|
||||
if (ex instanceof R2dbcPermissionDeniedException) {
|
||||
return new PermissionDeniedDataAccessException(buildMessage(task, sql, ex), ex);
|
||||
}
|
||||
if (ex instanceof R2dbcBadGrammarException) {
|
||||
return new BadSqlGrammarException(task, (sql != null ? sql : ""), ex);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to Spring's own R2DBC state translation...
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2021 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.data.r2dbc.support;
|
||||
|
||||
import io.r2dbc.spi.R2dbcException;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Strategy interface for translating between {@link io.r2dbc.spi.R2dbcException R2dbcExceptions} and Spring's data
|
||||
* access strategy-agnostic {@link DataAccessException} hierarchy.
|
||||
* <p>
|
||||
* Implementations can be generic (for example, using {@link io.r2dbc.spi.R2dbcException#getSqlState() SQLState} codes
|
||||
* for R2DBC) or wholly proprietary (for example, using Oracle error codes) for greater precision.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @see org.springframework.dao.DataAccessException
|
||||
* @see SqlStateR2dbcExceptionTranslator
|
||||
* @see SqlErrorCodeR2dbcExceptionTranslator
|
||||
* @deprecated since 1.2. Use Spring R2DBC's
|
||||
* {@link org.springframework.r2dbc.connection.ConnectionFactoryUtils#convertR2dbcException(String, String, R2dbcException)}
|
||||
* instead.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
@Deprecated
|
||||
public interface R2dbcExceptionTranslator {
|
||||
|
||||
/**
|
||||
* Translate the given {@link R2dbcException} into a generic {@link DataAccessException}.
|
||||
* <p>
|
||||
* The returned DataAccessException is supposed to contain the original {@link R2dbcException} as root cause. However,
|
||||
* client code may not generally rely on this due to DataAccessExceptions possibly being caused by other resource APIs
|
||||
* as well. That said, a {@code getRootCause() instanceof R2dbcException} check (and subsequent cast) is considered
|
||||
* reliable when expecting R2DBC-based access to have happened.
|
||||
*
|
||||
* @param task readable text describing the task being attempted.
|
||||
* @param sql SQL query or update that caused the problem (if known).
|
||||
* @param ex the offending {@link R2dbcException}.
|
||||
* @return the DataAccessException wrapping the {@code R2dbcException}, or {@literal null} if no translation could be
|
||||
* applied (in a custom translator; the default translators always throw an
|
||||
* {@link org.springframework.data.r2dbc.UncategorizedR2dbcException} in such a case).
|
||||
* @see org.springframework.dao.DataAccessException#getRootCause()
|
||||
*/
|
||||
@Nullable
|
||||
DataAccessException translate(String task, @Nullable String sql, R2dbcException ex);
|
||||
|
||||
}
|
||||
@@ -1,277 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2021 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.data.r2dbc.support;
|
||||
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import io.r2dbc.spi.R2dbcException;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.springframework.dao.CannotAcquireLockException;
|
||||
import org.springframework.dao.CannotSerializeTransactionException;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.dao.DeadlockLoserDataAccessException;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.dao.PermissionDeniedDataAccessException;
|
||||
import org.springframework.dao.TransientDataAccessResourceException;
|
||||
import org.springframework.data.r2dbc.BadSqlGrammarException;
|
||||
import org.springframework.data.r2dbc.InvalidResultAccessException;
|
||||
import org.springframework.jdbc.support.SQLErrorCodes;
|
||||
import org.springframework.jdbc.support.SQLErrorCodesFactory;
|
||||
import org.springframework.jdbc.support.SQLExceptionTranslator;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Implementation of {@link R2dbcExceptionTranslator} that analyzes vendor-specific error codes. More precise than an
|
||||
* implementation based on SQL state, but heavily vendor-specific.
|
||||
* <p>
|
||||
* This class applies the following matching rules:
|
||||
* <ul>
|
||||
* <li>Try custom translation implemented by any subclass. Note that this class is concrete and is typically used
|
||||
* itself, in which case this rule doesn't apply.
|
||||
* <li>Apply error code matching. Error codes are obtained from the SQLErrorCodesFactory by default. This factory loads
|
||||
* a "sql-error-codes.xml" file from the class path, defining error code mappings for database names from database
|
||||
* meta-data.
|
||||
* <li>Fallback to a fallback translator. {@link SqlStateR2dbcExceptionTranslator} is the default fallback translator,
|
||||
* analyzing the exception's SQL state only.
|
||||
* </ul>
|
||||
* <p>
|
||||
* The configuration file named "sql-error-codes.xml" is by default read from the
|
||||
* {@code org.springframework.jdbc.support} package. It can be overridden through a file of the same name in the root of
|
||||
* the class path (e.g. in the "/WEB-INF/classes" directory), as long as the Spring JDBC package is loaded from the same
|
||||
* ClassLoader.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @see SQLErrorCodesFactory
|
||||
* @see SqlStateR2dbcExceptionTranslator
|
||||
* @deprecated since 1.2. Use Spring R2DBC's
|
||||
* {@link org.springframework.r2dbc.connection.ConnectionFactoryUtils#convertR2dbcException(String, String, R2dbcException)}
|
||||
* instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class SqlErrorCodeR2dbcExceptionTranslator extends AbstractFallbackR2dbcExceptionTranslator {
|
||||
|
||||
/** Error codes used by this translator */
|
||||
@Nullable private SQLErrorCodes sqlErrorCodes;
|
||||
|
||||
/**
|
||||
* Creates a new {@link SqlErrorCodeR2dbcExceptionTranslator}. The {@link SQLErrorCodes} or
|
||||
* {@link io.r2dbc.spi.ConnectionFactory} property must be set.
|
||||
*/
|
||||
public SqlErrorCodeR2dbcExceptionTranslator() {}
|
||||
|
||||
/**
|
||||
* Create a SQL error code translator for the given DataSource. Invoking this constructor will cause a Connection to
|
||||
* be obtained from the DataSource to get the meta-data.
|
||||
*
|
||||
* @param connectionFactory {@link ConnectionFactory} to use to find meta-data and establish which error codes are
|
||||
* usable.
|
||||
* @see SQLErrorCodesFactory
|
||||
*/
|
||||
public SqlErrorCodeR2dbcExceptionTranslator(ConnectionFactory connectionFactory) {
|
||||
this();
|
||||
setConnectionFactory(connectionFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a SQL error code translator for the given database product name. Invoking this constructor will avoid
|
||||
* obtaining a Connection from the DataSource to get the meta-data.
|
||||
*
|
||||
* @param dbName the database product name that identifies the error codes entry
|
||||
* @see SQLErrorCodesFactory
|
||||
* @see java.sql.DatabaseMetaData#getDatabaseProductName()
|
||||
*/
|
||||
public SqlErrorCodeR2dbcExceptionTranslator(String dbName) {
|
||||
this();
|
||||
setDatabaseProductName(dbName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a SQLErrorCode translator given these error codes. Does not require a database meta-data lookup to be
|
||||
* performed using a connection.
|
||||
*
|
||||
* @param sec error codes
|
||||
*/
|
||||
public SqlErrorCodeR2dbcExceptionTranslator(@Nullable SQLErrorCodes sec) {
|
||||
this();
|
||||
this.sqlErrorCodes = sec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the DataSource for this translator.
|
||||
* <p>
|
||||
* Setting this property will cause a Connection to be obtained from the DataSource to get the meta-data.
|
||||
*
|
||||
* @param connectionFactory {@link ConnectionFactory} to use to find meta-data and establish which error codes are
|
||||
* usable.
|
||||
* @see SQLErrorCodesFactory#getErrorCodes(String)
|
||||
* @see io.r2dbc.spi.ConnectionFactoryMetadata#getName()
|
||||
*/
|
||||
public void setConnectionFactory(ConnectionFactory connectionFactory) {
|
||||
this.sqlErrorCodes = SQLErrorCodesFactory.getInstance().getErrorCodes(connectionFactory.getMetadata().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the database product name for this translator.
|
||||
* <p>
|
||||
* Setting this property will avoid obtaining a Connection from the DataSource to get the meta-data.
|
||||
*
|
||||
* @param dbName the database product name that identifies the error codes entry.
|
||||
* @see SQLErrorCodesFactory#getErrorCodes(String)
|
||||
* @see io.r2dbc.spi.ConnectionFactoryMetadata#getName()
|
||||
*/
|
||||
public void setDatabaseProductName(String dbName) {
|
||||
this.sqlErrorCodes = SQLErrorCodesFactory.getInstance().getErrorCodes(dbName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set custom error codes to be used for translation.
|
||||
*
|
||||
* @param sec custom error codes to use.
|
||||
*/
|
||||
public void setSqlErrorCodes(@Nullable SQLErrorCodes sec) {
|
||||
this.sqlErrorCodes = sec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the error codes used by this translator. Usually determined via a DataSource.
|
||||
*
|
||||
* @see #setConnectionFactory
|
||||
*/
|
||||
@Nullable
|
||||
public SQLErrorCodes getSqlErrorCodes() {
|
||||
return this.sqlErrorCodes;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected DataAccessException doTranslate(String task, @Nullable String sql, R2dbcException ex) {
|
||||
|
||||
R2dbcException translated = ex;
|
||||
|
||||
// First, try custom translation from overridden method.
|
||||
DataAccessException dex = customTranslate(task, sql, translated);
|
||||
if (dex != null) {
|
||||
return dex;
|
||||
}
|
||||
|
||||
// Next, try the custom SQLExceptionTranslator, if available.
|
||||
if (this.sqlErrorCodes != null) {
|
||||
SQLExceptionTranslator customTranslator = this.sqlErrorCodes.getCustomSqlExceptionTranslator();
|
||||
if (customTranslator != null) {
|
||||
DataAccessException customDex = customTranslator.translate(task, sql,
|
||||
new SQLException(ex.getMessage(), ex.getSqlState(), ex));
|
||||
if (customDex != null) {
|
||||
return customDex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check SQLErrorCodes with corresponding error code, if available.
|
||||
if (this.sqlErrorCodes != null) {
|
||||
String errorCode;
|
||||
if (this.sqlErrorCodes.isUseSqlStateForTranslation()) {
|
||||
errorCode = translated.getSqlState();
|
||||
} else {
|
||||
// Try to find R2dbcException with actual error code, looping through the causes.
|
||||
R2dbcException current = translated;
|
||||
while (current.getErrorCode() == 0 && current.getCause() instanceof R2dbcException) {
|
||||
current = (R2dbcException) current.getCause();
|
||||
}
|
||||
errorCode = Integer.toString(current.getErrorCode());
|
||||
}
|
||||
|
||||
if (errorCode != null) {
|
||||
// Look for grouped error codes.
|
||||
if (Arrays.binarySearch(this.sqlErrorCodes.getBadSqlGrammarCodes(), errorCode) >= 0) {
|
||||
logTranslation(task, sql, translated);
|
||||
return new BadSqlGrammarException(task, (sql != null ? sql : ""), translated);
|
||||
} else if (Arrays.binarySearch(this.sqlErrorCodes.getInvalidResultSetAccessCodes(), errorCode) >= 0) {
|
||||
logTranslation(task, sql, translated);
|
||||
return new InvalidResultAccessException(task, (sql != null ? sql : ""), translated);
|
||||
} else if (Arrays.binarySearch(this.sqlErrorCodes.getDuplicateKeyCodes(), errorCode) >= 0) {
|
||||
logTranslation(task, sql, translated);
|
||||
return new DuplicateKeyException(buildMessage(task, sql, translated), translated);
|
||||
} else if (Arrays.binarySearch(this.sqlErrorCodes.getDataIntegrityViolationCodes(), errorCode) >= 0) {
|
||||
logTranslation(task, sql, translated);
|
||||
return new DataIntegrityViolationException(buildMessage(task, sql, translated), translated);
|
||||
} else if (Arrays.binarySearch(this.sqlErrorCodes.getPermissionDeniedCodes(), errorCode) >= 0) {
|
||||
logTranslation(task, sql, translated);
|
||||
return new PermissionDeniedDataAccessException(buildMessage(task, sql, translated), translated);
|
||||
} else if (Arrays.binarySearch(this.sqlErrorCodes.getDataAccessResourceFailureCodes(), errorCode) >= 0) {
|
||||
logTranslation(task, sql, translated);
|
||||
return new DataAccessResourceFailureException(buildMessage(task, sql, translated), translated);
|
||||
} else if (Arrays.binarySearch(this.sqlErrorCodes.getTransientDataAccessResourceCodes(), errorCode) >= 0) {
|
||||
logTranslation(task, sql, translated);
|
||||
return new TransientDataAccessResourceException(buildMessage(task, sql, translated), translated);
|
||||
} else if (Arrays.binarySearch(this.sqlErrorCodes.getCannotAcquireLockCodes(), errorCode) >= 0) {
|
||||
logTranslation(task, sql, translated);
|
||||
return new CannotAcquireLockException(buildMessage(task, sql, translated), translated);
|
||||
} else if (Arrays.binarySearch(this.sqlErrorCodes.getDeadlockLoserCodes(), errorCode) >= 0) {
|
||||
logTranslation(task, sql, translated);
|
||||
return new DeadlockLoserDataAccessException(buildMessage(task, sql, translated), translated);
|
||||
} else if (Arrays.binarySearch(this.sqlErrorCodes.getCannotSerializeTransactionCodes(), errorCode) >= 0) {
|
||||
logTranslation(task, sql, translated);
|
||||
return new CannotSerializeTransactionException(buildMessage(task, sql, translated), translated);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We couldn't identify it more precisely - let's hand it over to the SQLState fallback translator.
|
||||
if (logger.isDebugEnabled()) {
|
||||
String codes;
|
||||
if (this.sqlErrorCodes != null && this.sqlErrorCodes.isUseSqlStateForTranslation()) {
|
||||
codes = "SQL state '" + translated.getSqlState() + "', error code '" + translated.getErrorCode();
|
||||
} else {
|
||||
codes = "Error code '" + translated.getErrorCode() + "'";
|
||||
}
|
||||
logger.debug("Unable to translate R2dbcException with " + codes + ", will now try the fallback translator");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses can override this method to attempt a custom mapping from {@link R2dbcException} to
|
||||
* {@link DataAccessException}.
|
||||
*
|
||||
* @param task readable text describing the task being attempted
|
||||
* @param sql SQL query or update that caused the problem. May be {@literal null}.
|
||||
* @param ex the offending {@link R2dbcException}.
|
||||
* @return null if no custom translation was possible, otherwise a {@link DataAccessException} resulting from custom
|
||||
* translation. This exception should include the {@link R2dbcException} parameter as a nested root cause.
|
||||
* This implementation always returns null, meaning that the translator always falls back to the default error
|
||||
* codes.
|
||||
*/
|
||||
@Nullable
|
||||
protected DataAccessException customTranslate(String task, @Nullable String sql, R2dbcException ex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void logTranslation(String task, @Nullable String sql, R2dbcException exception) {
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
|
||||
String intro = "Translating";
|
||||
logger.debug(intro + " R2dbcException with SQL state '" + exception.getSqlState() + "', error code '"
|
||||
+ exception.getErrorCode() + "', message [" + exception.getMessage() + "]"
|
||||
+ (sql != null ? "; SQL was [" + sql + "]" : "") + " for task [" + task + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2021 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.data.r2dbc.support;
|
||||
|
||||
import io.r2dbc.spi.R2dbcException;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.dao.TransientDataAccessResourceException;
|
||||
import org.springframework.data.r2dbc.BadSqlGrammarException;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* {@link R2dbcExceptionTranslator} implementation that analyzes the SQL state in the {@link R2dbcException} based on
|
||||
* the first two digits (the SQL state "class"). Detects standard SQL state values and well-known vendor-specific SQL
|
||||
* states.
|
||||
* <p>
|
||||
* Not able to diagnose all problems, but is portable between databases and does not require special initialization (no
|
||||
* database vendor detection, etc.). For more precise translation, consider
|
||||
* {@link SqlErrorCodeR2dbcExceptionTranslator}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @see io.r2dbc.spi.R2dbcException#getSqlState()
|
||||
* @see SqlErrorCodeR2dbcExceptionTranslator
|
||||
* @deprecated since 1.2. Use Spring R2DBC's
|
||||
* {@link org.springframework.r2dbc.connection.ConnectionFactoryUtils#convertR2dbcException(String, String, R2dbcException)}
|
||||
* instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class SqlStateR2dbcExceptionTranslator extends AbstractFallbackR2dbcExceptionTranslator {
|
||||
|
||||
private static final Set<String> BAD_SQL_GRAMMAR_CODES = new HashSet<>(8);
|
||||
private static final Set<String> DATA_INTEGRITY_VIOLATION_CODES = new HashSet<>(8);
|
||||
private static final Set<String> DATA_ACCESS_RESOURCE_FAILURE_CODES = new HashSet<>(8);
|
||||
private static final Set<String> TRANSIENT_DATA_ACCESS_RESOURCE_CODES = new HashSet<>(8);
|
||||
private static final Set<String> CONCURRENCY_FAILURE_CODES = new HashSet<>(4);
|
||||
|
||||
static {
|
||||
BAD_SQL_GRAMMAR_CODES.add("07"); // Dynamic SQL error
|
||||
BAD_SQL_GRAMMAR_CODES.add("21"); // Cardinality violation
|
||||
BAD_SQL_GRAMMAR_CODES.add("2A"); // Syntax error direct SQL
|
||||
BAD_SQL_GRAMMAR_CODES.add("37"); // Syntax error dynamic SQL
|
||||
BAD_SQL_GRAMMAR_CODES.add("42"); // General SQL syntax error
|
||||
BAD_SQL_GRAMMAR_CODES.add("65"); // Oracle: unknown identifier
|
||||
|
||||
DATA_INTEGRITY_VIOLATION_CODES.add("01"); // Data truncation
|
||||
DATA_INTEGRITY_VIOLATION_CODES.add("02"); // No data found
|
||||
DATA_INTEGRITY_VIOLATION_CODES.add("22"); // Value out of range
|
||||
DATA_INTEGRITY_VIOLATION_CODES.add("23"); // Integrity constraint violation
|
||||
DATA_INTEGRITY_VIOLATION_CODES.add("27"); // Triggered data change violation
|
||||
DATA_INTEGRITY_VIOLATION_CODES.add("44"); // With check violation
|
||||
|
||||
DATA_ACCESS_RESOURCE_FAILURE_CODES.add("08"); // Connection exception
|
||||
DATA_ACCESS_RESOURCE_FAILURE_CODES.add("53"); // PostgreSQL: insufficient resources (e.g. disk full)
|
||||
DATA_ACCESS_RESOURCE_FAILURE_CODES.add("54"); // PostgreSQL: program limit exceeded (e.g. statement too complex)
|
||||
DATA_ACCESS_RESOURCE_FAILURE_CODES.add("57"); // DB2: out-of-memory exception / database not started
|
||||
DATA_ACCESS_RESOURCE_FAILURE_CODES.add("58"); // DB2: unexpected system error
|
||||
|
||||
TRANSIENT_DATA_ACCESS_RESOURCE_CODES.add("JW"); // Sybase: internal I/O error
|
||||
TRANSIENT_DATA_ACCESS_RESOURCE_CODES.add("JZ"); // Sybase: unexpected I/O error
|
||||
TRANSIENT_DATA_ACCESS_RESOURCE_CODES.add("S1"); // DB2: communication failure
|
||||
|
||||
CONCURRENCY_FAILURE_CODES.add("40"); // Transaction rollback
|
||||
CONCURRENCY_FAILURE_CODES.add("61"); // Oracle: deadlock
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected DataAccessException doTranslate(String task, @Nullable String sql, R2dbcException ex) {
|
||||
|
||||
// First, the getSQLState check...
|
||||
String sqlState = getSqlState(ex);
|
||||
if (sqlState != null && sqlState.length() >= 2) {
|
||||
|
||||
String classCode = sqlState.substring(0, 2);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Extracted SQL state class '" + classCode + "' from value '" + sqlState + "'");
|
||||
}
|
||||
|
||||
if (BAD_SQL_GRAMMAR_CODES.contains(classCode)) {
|
||||
return new BadSqlGrammarException(task, (sql != null ? sql : ""), ex);
|
||||
} else if (DATA_INTEGRITY_VIOLATION_CODES.contains(classCode)) {
|
||||
return new DataIntegrityViolationException(buildMessage(task, sql, ex), ex);
|
||||
} else if (DATA_ACCESS_RESOURCE_FAILURE_CODES.contains(classCode)) {
|
||||
return new DataAccessResourceFailureException(buildMessage(task, sql, ex), ex);
|
||||
} else if (TRANSIENT_DATA_ACCESS_RESOURCE_CODES.contains(classCode)) {
|
||||
return new TransientDataAccessResourceException(buildMessage(task, sql, ex), ex);
|
||||
} else if (CONCURRENCY_FAILURE_CODES.contains(classCode)) {
|
||||
return new ConcurrencyFailureException(buildMessage(task, sql, ex), ex);
|
||||
}
|
||||
}
|
||||
|
||||
// Couldn't resolve anything proper - resort to UncategorizedR2dbcException.
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the SQL state code from the supplied {@link R2dbcException exception}.
|
||||
* <p>
|
||||
* Some R2DBC drivers nest the actual exception from a batched update, so we might need to dig down into the nested
|
||||
* exception.
|
||||
*
|
||||
* @param ex the exception from which the {@link R2dbcException#getSqlState() SQL state} is to be extracted.
|
||||
* @return the SQL state code.
|
||||
*/
|
||||
@Nullable
|
||||
private String getSqlState(R2dbcException ex) {
|
||||
|
||||
String sqlState = ex.getSqlState();
|
||||
|
||||
if (sqlState == null) {
|
||||
|
||||
for (Throwable throwable : ex.getSuppressed()) {
|
||||
|
||||
if (!(throwable instanceof R2dbcException)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
R2dbcException r2dbcException = (R2dbcException) throwable;
|
||||
if (r2dbcException.getSqlState() != null) {
|
||||
sqlState = r2dbcException.getSqlState();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sqlState;
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.core
|
||||
|
||||
import org.springframework.data.r2dbc.query.Criteria
|
||||
|
||||
/**
|
||||
* Extension for [Criteria.CriteriaStep. is] providing a
|
||||
* `eq(value)` variant.
|
||||
*
|
||||
* @author Jonas Bark
|
||||
*/
|
||||
@Deprecated("Deprecated in favor of Spring Data Relational's Criteria")
|
||||
infix fun Criteria.CriteriaStep.isEquals(value: Any): Criteria =
|
||||
`is`(value)
|
||||
|
||||
/**
|
||||
* Extension for [Criteria.CriteriaStep. in] providing a
|
||||
* `isIn(value)` variant.
|
||||
*
|
||||
* @author Jonas Bark
|
||||
*/
|
||||
@Deprecated("Deprecated in favor of Spring Data Relational's Criteria")
|
||||
fun Criteria.CriteriaStep.isIn(vararg value: Any): Criteria =
|
||||
`in`(value)
|
||||
|
||||
/**
|
||||
* Extension for [Criteria.CriteriaStep. in] providing a
|
||||
* `isIn(value)` variant.
|
||||
*
|
||||
* @author Jonas Bark
|
||||
*/
|
||||
@Deprecated("Deprecated in favor of Spring Data Relational's Criteria")
|
||||
fun Criteria.CriteriaStep.isIn(values: Collection<Any>): Criteria =
|
||||
`in`(values)
|
||||
|
||||
@@ -1,169 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2021 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.data.r2dbc.core
|
||||
|
||||
import kotlinx.coroutines.reactive.awaitFirstOrNull
|
||||
import org.springframework.data.r2dbc.mapping.SettableValue
|
||||
|
||||
/**
|
||||
* Coroutines variant of [DatabaseClient.GenericExecuteSpec.then].
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
@Deprecated("Deprecated in favor of Spring R2DBC's DatabaseClient")
|
||||
suspend fun DatabaseClient.GenericExecuteSpec.await() {
|
||||
then().awaitFirstOrNull()
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension for [DatabaseClient.BindSpec.bind] providing a variant leveraging reified type parameters
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Ibanga Enoobong Ime
|
||||
*/
|
||||
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
|
||||
@Deprecated("Deprecated in favor of Spring R2DBC's DatabaseClient")
|
||||
inline fun <reified T : Any> DatabaseClient.TypedExecuteSpec<*>.bind(index: Int, value: T?) = bind(index, SettableValue.fromOrEmpty(value, T::class.java))
|
||||
|
||||
/**
|
||||
* Extension for [DatabaseClient.BindSpec.bind] providing a variant leveraging reified type parameters
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Ibanga Enoobong Ime
|
||||
*/
|
||||
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
|
||||
@Deprecated("Deprecated in favor of Spring R2DBC's DatabaseClient")
|
||||
inline fun <reified T : Any> DatabaseClient.GenericExecuteSpec.bind(index: Int, value: T?) = bind(index, SettableValue.fromOrEmpty(value, T::class.java))
|
||||
|
||||
/**
|
||||
* Extension for [DatabaseClient.BindSpec.bind] providing a variant leveraging reified type parameters
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Ibanga Enoobong Ime
|
||||
*/
|
||||
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
|
||||
@Deprecated("Deprecated in favor of Spring R2DBC's DatabaseClient")
|
||||
inline fun <reified T : Any> DatabaseClient.TypedExecuteSpec<*>.bind(name: String, value: T?) = bind(name, SettableValue.fromOrEmpty(value, T::class.java))
|
||||
|
||||
/**
|
||||
* Extension for [DatabaseClient.BindSpec.bind] providing a variant leveraging reified type parameters
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Ibanga Enoobong Ime
|
||||
*/
|
||||
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
|
||||
@Deprecated("Deprecated in favor of Spring R2DBC's DatabaseClient")
|
||||
inline fun <reified T : Any> DatabaseClient.GenericExecuteSpec.bind(name: String, value: T?) = bind(name, SettableValue.fromOrEmpty(value, T::class.java))
|
||||
|
||||
/**
|
||||
* Extension for [DatabaseClient.GenericExecuteSpec. as] providing a
|
||||
* `asType<Foo>()` variant.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
@Deprecated("Deprecated in favor of Spring R2DBC's DatabaseClient")
|
||||
inline fun <reified T : Any> DatabaseClient.GenericExecuteSpec.asType(): DatabaseClient.TypedExecuteSpec<T> =
|
||||
`as`(T::class.java)
|
||||
|
||||
/**
|
||||
* Extension for [DatabaseClient.GenericSelectSpec.as] providing a
|
||||
* `asType<Foo>()` variant.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
@Deprecated("Deprecated in favor of Spring R2DBC's DatabaseClient")
|
||||
inline fun <reified T : Any> DatabaseClient.GenericSelectSpec.asType(): DatabaseClient.TypedSelectSpec<T> =
|
||||
`as`(T::class.java)
|
||||
|
||||
/**
|
||||
* Coroutines variant of [DatabaseClient.TypedExecuteSpec.then].
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
@Deprecated("Deprecated in favor of Spring R2DBC's DatabaseClient")
|
||||
suspend fun <T> DatabaseClient.TypedExecuteSpec<T>.await() {
|
||||
then().awaitFirstOrNull()
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension for [DatabaseClient.TypedExecuteSpec. as] providing a
|
||||
* `asType<Foo>()` variant.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
@Deprecated("Deprecated in favor of Spring R2DBC's DatabaseClient")
|
||||
inline fun <reified T : Any> DatabaseClient.TypedExecuteSpec<T>.asType(): DatabaseClient.TypedExecuteSpec<T> =
|
||||
`as`(T::class.java)
|
||||
|
||||
/**
|
||||
* Coroutines variant of [DatabaseClient.InsertSpec.then].
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
@Deprecated("Deprecated in favor of Spring R2DBC's DatabaseClient")
|
||||
suspend fun <T> DatabaseClient.InsertSpec<T>.await() {
|
||||
then().awaitFirstOrNull()
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension for [DatabaseClient.InsertIntoSpec.into] providing a
|
||||
* `into<Foo>()` variant.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
@Deprecated("Deprecated in favor of Spring R2DBC's DatabaseClient")
|
||||
inline fun <reified T : Any> DatabaseClient.InsertIntoSpec.into(): DatabaseClient.TypedInsertSpec<T> =
|
||||
into(T::class.java)
|
||||
|
||||
/**
|
||||
* Extension for [DatabaseClient.GenericInsertSpec.value] providing a variant leveraging reified type parameters
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
|
||||
@Deprecated("Deprecated in favor of Spring R2DBC's DatabaseClient")
|
||||
inline fun <reified T : Any> DatabaseClient.GenericInsertSpec<*>.value(name: String, value: T?) = value(name, SettableValue.fromOrEmpty(value, T::class.java))
|
||||
|
||||
|
||||
/**
|
||||
* Extension for [DatabaseClient.SelectFromSpec.from] providing a
|
||||
* `from<Foo>()` variant.
|
||||
*
|
||||
* @author Jonas Bark
|
||||
*/
|
||||
@Deprecated("Deprecated in favor of Spring R2DBC's DatabaseClient")
|
||||
inline fun <reified T : Any> DatabaseClient.SelectFromSpec.from(): DatabaseClient.TypedSelectSpec<T> =
|
||||
from(T::class.java)
|
||||
|
||||
/**
|
||||
* Extension for [DatabaseClient.UpdateTableSpec.table] providing a
|
||||
* `table<Foo>()` variant.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Deprecated("Deprecated in favor of Spring R2DBC's DatabaseClient")
|
||||
inline fun <reified T : Any> DatabaseClient.UpdateTableSpec.table(): DatabaseClient.TypedUpdateSpec<T> =
|
||||
table(T::class.java)
|
||||
|
||||
/**
|
||||
* Extension for [DatabaseClient.SelectFromSpec.from] providing a
|
||||
* `from<Foo>()` variant.
|
||||
*
|
||||
* @author Jonas Bark
|
||||
*/
|
||||
@Deprecated("Deprecated in favor of Spring R2DBC's DatabaseClient")
|
||||
inline fun <reified T : Any> DatabaseClient.DeleteFromSpec.from(): DatabaseClient.TypedDeleteSpec<T> =
|
||||
from(T::class.java)
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2021 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.data.r2dbc.core
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.reactive.asFlow
|
||||
import kotlinx.coroutines.reactive.awaitFirstOrNull
|
||||
import org.springframework.dao.EmptyResultDataAccessException
|
||||
|
||||
/**
|
||||
* Non-nullable Coroutines variant of [RowsFetchSpec.one].
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
suspend fun <T> RowsFetchSpec<T>.awaitOne(): T {
|
||||
return one().awaitFirstOrNull() ?: throw EmptyResultDataAccessException(1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Nullable Coroutines variant of [RowsFetchSpec.one].
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
suspend fun <T> RowsFetchSpec<T>.awaitOneOrNull(): T? =
|
||||
one().awaitFirstOrNull()
|
||||
|
||||
/**
|
||||
* Non-nullable Coroutines variant of [RowsFetchSpec.first].
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
suspend fun <T> RowsFetchSpec<T>.awaitFirst(): T {
|
||||
return first().awaitFirstOrNull() ?: throw EmptyResultDataAccessException(1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Nullable Coroutines variant of [RowsFetchSpec.first].
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
suspend fun <T> RowsFetchSpec<T>.awaitFirstOrNull(): T? =
|
||||
first().awaitFirstOrNull()
|
||||
|
||||
/**
|
||||
* Coroutines [Flow] variant of [RowsFetchSpec.all].
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
fun <T : Any> RowsFetchSpec<T>.flow(): Flow<T> = all().asFlow()
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.core
|
||||
|
||||
import kotlinx.coroutines.reactive.awaitSingle
|
||||
|
||||
/**
|
||||
* Coroutines variant of [UpdatedRowsFetchSpec.rowsUpdated].
|
||||
*
|
||||
* @author Fred Montariol
|
||||
*/
|
||||
suspend fun UpdatedRowsFetchSpec.awaitRowsUpdated(): Int =
|
||||
rowsUpdated().awaitSingle()
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import io.r2dbc.spi.Connection;
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link DelegatingConnectionFactory}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class DelegatingConnectionFactoryUnitTests {
|
||||
|
||||
ConnectionFactory delegate = mock(ConnectionFactory.class);
|
||||
Connection connectionMock = mock(Connection.class);
|
||||
|
||||
DelegatingConnectionFactory connectionFactory = new ExampleConnectionFactory(delegate);
|
||||
|
||||
@Test // gh-107
|
||||
public void shouldDelegateGetConnection() {
|
||||
|
||||
Mono<Connection> connectionMono = Mono.just(connectionMock);
|
||||
when(delegate.create()).thenReturn((Mono) connectionMono);
|
||||
|
||||
assertThat(connectionFactory.create()).isSameAs(connectionMono);
|
||||
}
|
||||
|
||||
@Test // gh-107
|
||||
public void shouldDelegateUnwrapWithoutImplementing() {
|
||||
assertThat(connectionFactory.unwrap()).isSameAs(delegate);
|
||||
}
|
||||
|
||||
static class ExampleConnectionFactory extends DelegatingConnectionFactory {
|
||||
|
||||
ExampleConnectionFactory(ConnectionFactory targetConnectionFactory) {
|
||||
super(targetConnectionFactory);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import io.r2dbc.h2.H2Connection;
|
||||
import io.r2dbc.spi.Connection;
|
||||
import io.r2dbc.spi.ConnectionFactoryMetadata;
|
||||
import io.r2dbc.spi.IsolationLevel;
|
||||
import io.r2dbc.spi.R2dbcNonTransientResourceException;
|
||||
import io.r2dbc.spi.Wrapped;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link SingleConnectionConnectionFactory}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class SingleConnectionConnectionFactoryUnitTests {
|
||||
|
||||
@Test // gh-204
|
||||
void shouldAllocateSameConnection() {
|
||||
|
||||
SingleConnectionConnectionFactory factory = new SingleConnectionConnectionFactory("r2dbc:h2:mem:///foo", false);
|
||||
|
||||
Mono<? extends Connection> cf1 = factory.create();
|
||||
Mono<? extends Connection> cf2 = factory.create();
|
||||
|
||||
Connection c1 = cf1.block();
|
||||
Connection c2 = cf2.block();
|
||||
|
||||
assertThat(c1).isSameAs(c2);
|
||||
factory.destroy();
|
||||
}
|
||||
|
||||
@Test // gh-204
|
||||
void shouldApplyAutoCommit() {
|
||||
|
||||
SingleConnectionConnectionFactory factory = new SingleConnectionConnectionFactory("r2dbc:h2:mem:///foo", false);
|
||||
factory.setAutoCommit(false);
|
||||
|
||||
factory.create().as(StepVerifier::create).consumeNextWith(actual -> {
|
||||
assertThat(actual.isAutoCommit()).isFalse();
|
||||
}).verifyComplete();
|
||||
|
||||
factory.setAutoCommit(true);
|
||||
|
||||
factory.create().as(StepVerifier::create).consumeNextWith(actual -> {
|
||||
assertThat(actual.isAutoCommit()).isTrue();
|
||||
}).verifyComplete();
|
||||
|
||||
factory.destroy();
|
||||
}
|
||||
|
||||
@Test // gh-204
|
||||
void shouldSuppressClose() {
|
||||
|
||||
SingleConnectionConnectionFactory factory = new SingleConnectionConnectionFactory("r2dbc:h2:mem:///foo", true);
|
||||
|
||||
Connection connection = factory.create().block();
|
||||
|
||||
StepVerifier.create(connection.close()).verifyComplete();
|
||||
assertThat(connection).isInstanceOf(Wrapped.class);
|
||||
assertThat(((Wrapped) connection).unwrap()).isInstanceOf(H2Connection.class);
|
||||
|
||||
StepVerifier.create(connection.setTransactionIsolationLevel(IsolationLevel.READ_COMMITTED)) //
|
||||
.verifyComplete();
|
||||
factory.destroy();
|
||||
}
|
||||
|
||||
@Test // gh-204
|
||||
void shouldNotSuppressClose() {
|
||||
|
||||
SingleConnectionConnectionFactory factory = new SingleConnectionConnectionFactory("r2dbc:h2:mem:///foo", false);
|
||||
|
||||
Connection connection = factory.create().block();
|
||||
|
||||
StepVerifier.create(connection.close()).verifyComplete();
|
||||
|
||||
StepVerifier.create(connection.setTransactionIsolationLevel(IsolationLevel.READ_COMMITTED))
|
||||
.verifyError(R2dbcNonTransientResourceException.class);
|
||||
factory.destroy();
|
||||
}
|
||||
|
||||
@Test // gh-204
|
||||
void releaseConnectionShouldCloseUnrelatedConnection() {
|
||||
|
||||
Connection connectionMock = mock(Connection.class);
|
||||
Connection otherConnection = mock(Connection.class);
|
||||
ConnectionFactoryMetadata metadata = mock(ConnectionFactoryMetadata.class);
|
||||
when(otherConnection.close()).thenReturn(Mono.empty());
|
||||
|
||||
SingleConnectionConnectionFactory factory = new SingleConnectionConnectionFactory(connectionMock, metadata, false);
|
||||
|
||||
factory.create().as(StepVerifier::create).expectNextCount(1).verifyComplete();
|
||||
|
||||
ConnectionFactoryUtils.releaseConnection(otherConnection, factory) //
|
||||
.as(StepVerifier::create) //
|
||||
.verifyComplete();
|
||||
|
||||
verify(otherConnection).close();
|
||||
}
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import io.r2dbc.spi.Connection;
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.transaction.reactive.TransactionalOperator;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link TransactionAwareConnectionFactoryProxy}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class TransactionAwareConnectionFactoryProxyUnitTests {
|
||||
|
||||
ConnectionFactory connectionFactoryMock = mock(ConnectionFactory.class);
|
||||
Connection connectionMock1 = mock(Connection.class);
|
||||
Connection connectionMock2 = mock(Connection.class);
|
||||
Connection connectionMock3 = mock(Connection.class);
|
||||
|
||||
private R2dbcTransactionManager tm;
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
|
||||
when(connectionFactoryMock.create()).thenReturn((Mono) Mono.just(connectionMock1),
|
||||
(Mono) Mono.just(connectionMock2), (Mono) Mono.just(connectionMock3));
|
||||
tm = new R2dbcTransactionManager(connectionFactoryMock);
|
||||
}
|
||||
|
||||
@Test // gh-107
|
||||
public void createShouldProxyConnection() {
|
||||
|
||||
new TransactionAwareConnectionFactoryProxy(connectionFactoryMock).create() //
|
||||
.as(StepVerifier::create) //
|
||||
.consumeNextWith(connection -> {
|
||||
assertThat(connection).isInstanceOf(ConnectionProxy.class);
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test // gh-107
|
||||
public void unwrapShouldReturnTargetConnection() {
|
||||
|
||||
new TransactionAwareConnectionFactoryProxy(connectionFactoryMock).create() //
|
||||
.map(ConnectionProxy.class::cast).as(StepVerifier::create) //
|
||||
.consumeNextWith(proxy -> {
|
||||
assertThat(proxy.unwrap()).isEqualTo(connectionMock1);
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test // gh-107
|
||||
public void unwrapShouldReturnTargetConnectionEvenWhenClosed() {
|
||||
|
||||
when(connectionMock1.close()).thenReturn(Mono.empty());
|
||||
|
||||
new TransactionAwareConnectionFactoryProxy(connectionFactoryMock).create() //
|
||||
.map(ConnectionProxy.class::cast).flatMap(it -> Mono.from(it.close()).then(Mono.just(it)))
|
||||
.as(StepVerifier::create) //
|
||||
.consumeNextWith(proxy -> {
|
||||
assertThat(proxy.unwrap()).isEqualTo(connectionMock1);
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test // gh-107
|
||||
public void getTargetConnectionShouldReturnTargetConnection() {
|
||||
|
||||
new TransactionAwareConnectionFactoryProxy(connectionFactoryMock).create() //
|
||||
.map(ConnectionProxy.class::cast).as(StepVerifier::create) //
|
||||
.consumeNextWith(proxy -> {
|
||||
assertThat(proxy.getTargetConnection()).isEqualTo(connectionMock1);
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test // gh-107
|
||||
public void getTargetConnectionShouldThrowsErrorEvenWhenClosed() {
|
||||
|
||||
when(connectionMock1.close()).thenReturn(Mono.empty());
|
||||
|
||||
new TransactionAwareConnectionFactoryProxy(connectionFactoryMock).create() //
|
||||
.map(ConnectionProxy.class::cast).flatMap(it -> Mono.from(it.close()).then(Mono.just(it)))
|
||||
.as(StepVerifier::create) //
|
||||
.consumeNextWith(proxy -> {
|
||||
assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> proxy.getTargetConnection());
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test // gh-107
|
||||
public void hashCodeShouldReturnProxyHash() {
|
||||
|
||||
new TransactionAwareConnectionFactoryProxy(connectionFactoryMock).create() //
|
||||
.map(ConnectionProxy.class::cast).as(StepVerifier::create) //
|
||||
.consumeNextWith(proxy -> {
|
||||
assertThat(proxy.hashCode()).isEqualTo(System.identityHashCode(proxy));
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test // gh-107
|
||||
public void equalsShouldCompareCorrectly() {
|
||||
|
||||
new TransactionAwareConnectionFactoryProxy(connectionFactoryMock).create() //
|
||||
.map(ConnectionProxy.class::cast).as(StepVerifier::create) //
|
||||
.consumeNextWith(proxy -> {
|
||||
assertThat(proxy.equals(proxy)).isTrue();
|
||||
assertThat(proxy.equals(connectionMock1)).isFalse();
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test // gh-107
|
||||
public void shouldEmitBoundConnection() {
|
||||
|
||||
when(connectionMock1.beginTransaction()).thenReturn(Mono.empty());
|
||||
when(connectionMock1.commitTransaction()).thenReturn(Mono.empty());
|
||||
when(connectionMock1.close()).thenReturn(Mono.empty());
|
||||
|
||||
TransactionalOperator rxtx = TransactionalOperator.create(tm);
|
||||
AtomicReference<Connection> transactionalConnection = new AtomicReference<>();
|
||||
|
||||
TransactionAwareConnectionFactoryProxy proxyCf = new TransactionAwareConnectionFactoryProxy(connectionFactoryMock);
|
||||
|
||||
ConnectionFactoryUtils.getConnection(connectionFactoryMock) //
|
||||
.doOnNext(transactionalConnection::set).flatMap(it -> {
|
||||
|
||||
return proxyCf.create().doOnNext(connectionFromProxy -> {
|
||||
|
||||
ConnectionProxy connectionProxy = (ConnectionProxy) connectionFromProxy;
|
||||
assertThat(connectionProxy.getTargetConnection()).isSameAs(it);
|
||||
assertThat(connectionProxy.unwrap()).isSameAs(it);
|
||||
});
|
||||
}).as(rxtx::transactional) //
|
||||
.flatMapMany(Connection::close) //
|
||||
.as(StepVerifier::create) //
|
||||
.verifyComplete();
|
||||
|
||||
verifyNoInteractions(connectionMock2);
|
||||
verifyNoInteractions(connectionMock3);
|
||||
verify(connectionFactoryMock, times(1)).create();
|
||||
}
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory.init;
|
||||
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.io.ClassRelativeResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.data.r2dbc.core.DatabaseClient;
|
||||
|
||||
/**
|
||||
* Abstract test support for {@link DatabasePopulator}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
abstract class AbstractDatabaseInitializationTests {
|
||||
|
||||
private final ClassRelativeResourceLoader resourceLoader = new ClassRelativeResourceLoader(getClass());
|
||||
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
|
||||
|
||||
@Test
|
||||
void scriptWithSingleLineCommentsAndFailedDrop() {
|
||||
|
||||
databasePopulator.addScript(resource("db-schema-failed-drop-comments.sql"));
|
||||
databasePopulator.addScript(resource("db-test-data.sql"));
|
||||
databasePopulator.setIgnoreFailedDrops(true);
|
||||
|
||||
runPopulator();
|
||||
|
||||
assertUsersDatabaseCreated("Heisenberg");
|
||||
}
|
||||
|
||||
private void runPopulator() {
|
||||
DatabasePopulatorUtils.execute(databasePopulator, getConnectionFactory()) //
|
||||
.as(StepVerifier::create) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void scriptWithStandardEscapedLiteral() {
|
||||
|
||||
databasePopulator.addScript(defaultSchema());
|
||||
databasePopulator.addScript(resource("db-test-data-escaped-literal.sql"));
|
||||
|
||||
runPopulator();
|
||||
|
||||
assertUsersDatabaseCreated("'Heisenberg'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void scriptWithMySqlEscapedLiteral() {
|
||||
|
||||
databasePopulator.addScript(defaultSchema());
|
||||
databasePopulator.addScript(resource("db-test-data-mysql-escaped-literal.sql"));
|
||||
|
||||
runPopulator();
|
||||
|
||||
assertUsersDatabaseCreated("\\$Heisenberg\\$");
|
||||
}
|
||||
|
||||
@Test
|
||||
void scriptWithMultipleStatements() {
|
||||
|
||||
databasePopulator.addScript(defaultSchema());
|
||||
databasePopulator.addScript(resource("db-test-data-multiple.sql"));
|
||||
|
||||
runPopulator();
|
||||
|
||||
assertUsersDatabaseCreated("Heisenberg", "Jesse");
|
||||
}
|
||||
|
||||
@Test
|
||||
void scriptWithMultipleStatementsAndLongSeparator() {
|
||||
|
||||
databasePopulator.addScript(defaultSchema());
|
||||
databasePopulator.addScript(resource("db-test-data-endings.sql"));
|
||||
databasePopulator.setSeparator("@@");
|
||||
|
||||
runPopulator();
|
||||
|
||||
assertUsersDatabaseCreated("Heisenberg", "Jesse");
|
||||
}
|
||||
|
||||
abstract ConnectionFactory getConnectionFactory();
|
||||
|
||||
Resource resource(String path) {
|
||||
return resourceLoader.getResource(path);
|
||||
}
|
||||
|
||||
Resource defaultSchema() {
|
||||
return resource("db-schema.sql");
|
||||
}
|
||||
|
||||
Resource usersSchema() {
|
||||
return resource("users-schema.sql");
|
||||
}
|
||||
|
||||
void assertUsersDatabaseCreated(String... lastNames) {
|
||||
assertUsersDatabaseCreated(getConnectionFactory(), lastNames);
|
||||
}
|
||||
|
||||
void assertUsersDatabaseCreated(ConnectionFactory connectionFactory, String... lastNames) {
|
||||
|
||||
DatabaseClient client = DatabaseClient.create(connectionFactory);
|
||||
|
||||
for (String lastName : lastNames) {
|
||||
|
||||
client.execute("select count(0) from users where last_name = :name") //
|
||||
.bind("name", lastName) //
|
||||
.map((row, metadata) -> row.get(0)) //
|
||||
.first() //
|
||||
.map(it -> ((Number) it).intValue()) //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(1).as("Did not find user with last name [" + lastName + "].") //
|
||||
.verifyComplete();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory.init;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import io.r2dbc.spi.Connection;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link CompositeDatabasePopulator}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class CompositeDatabasePopulatorTests {
|
||||
|
||||
private final Connection mockedConnection = mock(Connection.class);
|
||||
|
||||
private final DatabasePopulator mockedDatabasePopulator1 = mock(DatabasePopulator.class);
|
||||
|
||||
private final DatabasePopulator mockedDatabasePopulator2 = mock(DatabasePopulator.class);
|
||||
|
||||
@BeforeEach
|
||||
void before() {
|
||||
|
||||
when(mockedDatabasePopulator1.populate(mockedConnection)).thenReturn(Mono.empty());
|
||||
when(mockedDatabasePopulator2.populate(mockedConnection)).thenReturn(Mono.empty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void addPopulators() {
|
||||
|
||||
CompositeDatabasePopulator populator = new CompositeDatabasePopulator();
|
||||
populator.addPopulators(mockedDatabasePopulator1, mockedDatabasePopulator2);
|
||||
|
||||
populator.populate(mockedConnection).as(StepVerifier::create).verifyComplete();
|
||||
|
||||
verify(mockedDatabasePopulator1, times(1)).populate(mockedConnection);
|
||||
verify(mockedDatabasePopulator2, times(1)).populate(mockedConnection);
|
||||
}
|
||||
|
||||
@Test
|
||||
void setPopulatorsWithMultiple() {
|
||||
|
||||
CompositeDatabasePopulator populator = new CompositeDatabasePopulator();
|
||||
populator.setPopulators(mockedDatabasePopulator1, mockedDatabasePopulator2); // multiple
|
||||
|
||||
populator.populate(mockedConnection).as(StepVerifier::create).verifyComplete();
|
||||
|
||||
verify(mockedDatabasePopulator1, times(1)).populate(mockedConnection);
|
||||
verify(mockedDatabasePopulator2, times(1)).populate(mockedConnection);
|
||||
}
|
||||
|
||||
@Test
|
||||
void setPopulatorsForOverride() {
|
||||
|
||||
CompositeDatabasePopulator populator = new CompositeDatabasePopulator();
|
||||
populator.setPopulators(mockedDatabasePopulator1);
|
||||
populator.setPopulators(mockedDatabasePopulator2); // override
|
||||
|
||||
populator.populate(mockedConnection).as(StepVerifier::create).verifyComplete();
|
||||
|
||||
verify(mockedDatabasePopulator1, times(0)).populate(mockedConnection);
|
||||
verify(mockedDatabasePopulator2, times(1)).populate(mockedConnection);
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructWithVarargs() {
|
||||
|
||||
CompositeDatabasePopulator populator = new CompositeDatabasePopulator(mockedDatabasePopulator1,
|
||||
mockedDatabasePopulator2);
|
||||
|
||||
populator.populate(mockedConnection).as(StepVerifier::create).verifyComplete();
|
||||
|
||||
verify(mockedDatabasePopulator1, times(1)).populate(mockedConnection);
|
||||
verify(mockedDatabasePopulator2, times(1)).populate(mockedConnection);
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructWithCollection() {
|
||||
|
||||
Set<DatabasePopulator> populators = new LinkedHashSet<>();
|
||||
populators.add(mockedDatabasePopulator1);
|
||||
populators.add(mockedDatabasePopulator2);
|
||||
|
||||
CompositeDatabasePopulator populator = new CompositeDatabasePopulator(populators);
|
||||
populator.populate(mockedConnection).as(StepVerifier::create).verifyComplete();
|
||||
|
||||
verify(mockedDatabasePopulator1, times(1)).populate(mockedConnection);
|
||||
verify(mockedDatabasePopulator2, times(1)).populate(mockedConnection);
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory.init;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import io.r2dbc.spi.test.MockConnection;
|
||||
import io.r2dbc.spi.test.MockConnectionFactory;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ConnectionFactoryInitializer}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class ConnectionFactoryInitializerUnitTests {
|
||||
|
||||
private final AtomicBoolean called = new AtomicBoolean();
|
||||
private final DatabasePopulator populator = mock(DatabasePopulator.class);
|
||||
private final MockConnection connection = MockConnection.builder().build();
|
||||
private final MockConnectionFactory connectionFactory = MockConnectionFactory.builder().connection(connection)
|
||||
.build();
|
||||
|
||||
@Test // gh-216
|
||||
void shouldInitializeConnectionFactory() {
|
||||
|
||||
when(populator.populate(any())).thenReturn(Mono.<Void> empty().doOnSubscribe(subscription -> called.set(true)));
|
||||
|
||||
ConnectionFactoryInitializer initializer = new ConnectionFactoryInitializer();
|
||||
initializer.setConnectionFactory(connectionFactory);
|
||||
initializer.setDatabasePopulator(populator);
|
||||
|
||||
initializer.afterPropertiesSet();
|
||||
|
||||
assertThat(called).isTrue();
|
||||
}
|
||||
|
||||
@Test // gh-216
|
||||
void shouldCleanConnectionFactory() {
|
||||
|
||||
when(populator.populate(any())).thenReturn(Mono.<Void> empty().doOnSubscribe(subscription -> called.set(true)));
|
||||
|
||||
ConnectionFactoryInitializer initializer = new ConnectionFactoryInitializer();
|
||||
initializer.setConnectionFactory(connectionFactory);
|
||||
initializer.setDatabaseCleaner(populator);
|
||||
|
||||
initializer.afterPropertiesSet();
|
||||
initializer.destroy();
|
||||
|
||||
assertThat(called).isTrue();
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory.init;
|
||||
|
||||
import io.r2dbc.spi.ConnectionFactories;
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link DatabasePopulator} using H2.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class H2DatabasePopulatorIntegrationTests extends AbstractDatabaseInitializationTests {
|
||||
|
||||
private final UUID databaseName = UUID.randomUUID();
|
||||
|
||||
private final ConnectionFactory connectionFactory = ConnectionFactories
|
||||
.get("r2dbc:h2:mem:///" + databaseName + "?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE");
|
||||
|
||||
@Override
|
||||
ConnectionFactory getConnectionFactory() {
|
||||
return this.connectionFactory;
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRunScript() {
|
||||
|
||||
databasePopulator.addScript(usersSchema());
|
||||
databasePopulator.addScript(resource("db-test-data-h2.sql"));
|
||||
// Set statement separator to double newline so that ";" is not
|
||||
// considered a statement separator within the source code of the
|
||||
// aliased function 'REVERSE'.
|
||||
databasePopulator.setSeparator("\n\n");
|
||||
|
||||
DatabasePopulatorUtils.execute(databasePopulator, connectionFactory).as(StepVerifier::create).verifyComplete();
|
||||
|
||||
assertUsersDatabaseCreated(connectionFactory, "White");
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory.init;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ResourceDatabasePopulator}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class ResourceDatabasePopulatorUnitTests {
|
||||
|
||||
private static final Resource script1 = mock(Resource.class);
|
||||
private static final Resource script2 = mock(Resource.class);
|
||||
private static final Resource script3 = mock(Resource.class);
|
||||
|
||||
@Test
|
||||
void constructWithNullResource() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new ResourceDatabasePopulator((Resource) null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructWithNullResourceArray() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new ResourceDatabasePopulator((Resource[]) null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructWithResource() {
|
||||
|
||||
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator(script1);
|
||||
assertThat(databasePopulator.scripts.size()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructWithMultipleResources() {
|
||||
|
||||
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator(script1, script2);
|
||||
assertThat(databasePopulator.scripts.size()).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructWithMultipleResourcesAndThenAddScript() {
|
||||
|
||||
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator(script1, script2);
|
||||
assertThat(databasePopulator.scripts.size()).isEqualTo(2);
|
||||
|
||||
databasePopulator.addScript(script3);
|
||||
assertThat(databasePopulator.scripts.size()).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void addScriptsWithNullResource() {
|
||||
|
||||
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> databasePopulator.addScripts((Resource) null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void addScriptsWithNullResourceArray() {
|
||||
|
||||
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> databasePopulator.addScripts((Resource[]) null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void setScriptsWithNullResource() {
|
||||
|
||||
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> databasePopulator.setScripts((Resource) null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void setScriptsWithNullResourceArray() {
|
||||
|
||||
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> databasePopulator.setScripts((Resource[]) null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void setScriptsAndThenAddScript() {
|
||||
|
||||
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
|
||||
assertThat(databasePopulator.scripts.size()).isEqualTo(0);
|
||||
|
||||
databasePopulator.setScripts(script1, script2);
|
||||
assertThat(databasePopulator.scripts.size()).isEqualTo(2);
|
||||
|
||||
databasePopulator.addScript(script3);
|
||||
assertThat(databasePopulator.scripts.size()).isEqualTo(3);
|
||||
}
|
||||
}
|
||||
@@ -1,205 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory.init;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.assertj.core.util.Strings;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.core.io.support.EncodedResource;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ScriptUtils}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class ScriptUtilsUnitTests {
|
||||
|
||||
@Test
|
||||
void splitSqlScriptDelimitedWithSemicolon() {
|
||||
|
||||
String rawStatement1 = "insert into customer (id, name)\nvalues (1, 'Rod ; Johnson'), (2, 'Adrian \n Collier')";
|
||||
String cleanedStatement1 = "insert into customer (id, name) values (1, 'Rod ; Johnson'), (2, 'Adrian \n Collier')";
|
||||
String rawStatement2 = "insert into orders(id, order_date, customer_id)\nvalues (1, '2008-01-02', 2)";
|
||||
String cleanedStatement2 = "insert into orders(id, order_date, customer_id) values (1, '2008-01-02', 2)";
|
||||
String rawStatement3 = "insert into orders(id, order_date, customer_id) values (1, '2008-01-02', 2)";
|
||||
String cleanedStatement3 = "insert into orders(id, order_date, customer_id) values (1, '2008-01-02', 2)";
|
||||
|
||||
String script = Strings.join(rawStatement1, rawStatement2, rawStatement3).with(";");
|
||||
|
||||
List<String> statements = new ArrayList<>();
|
||||
ScriptUtils.splitSqlScript(script, ";", statements);
|
||||
|
||||
assertThat(statements).hasSize(3).containsSequence(cleanedStatement1, cleanedStatement2, cleanedStatement3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void splitSqlScriptDelimitedWithNewLine() {
|
||||
|
||||
String statement1 = "insert into customer (id, name) values (1, 'Rod ; Johnson'), (2, 'Adrian \n Collier')";
|
||||
String statement2 = "insert into orders(id, order_date, customer_id) values (1, '2008-01-02', 2)";
|
||||
String statement3 = "insert into orders(id, order_date, customer_id) values (1, '2008-01-02', 2)";
|
||||
|
||||
String script = Strings.join(statement1, statement2, statement3).with("\n");
|
||||
|
||||
List<String> statements = new ArrayList<>();
|
||||
ScriptUtils.splitSqlScript(script, "\n", statements);
|
||||
|
||||
assertThat(statements).hasSize(3).containsSequence(statement1, statement2, statement3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void splitSqlScriptDelimitedWithNewLineButDefaultDelimiterSpecified() {
|
||||
|
||||
String statement1 = "do something";
|
||||
String statement2 = "do something else";
|
||||
|
||||
char delim = '\n';
|
||||
String script = statement1 + delim + statement2 + delim;
|
||||
|
||||
List<String> statements = new ArrayList<>();
|
||||
|
||||
ScriptUtils.splitSqlScript(script, ScriptUtils.DEFAULT_STATEMENT_SEPARATOR, statements);
|
||||
|
||||
assertThat(statements).hasSize(1).contains(script.replace('\n', ' '));
|
||||
}
|
||||
|
||||
@Test
|
||||
void splitScriptWithSingleQuotesNestedInsideDoubleQuotes() {
|
||||
|
||||
String statement1 = "select '1' as \"Dogbert's owner's\" from dual";
|
||||
String statement2 = "select '2' as \"Dilbert's\" from dual";
|
||||
|
||||
char delim = ';';
|
||||
String script = statement1 + delim + statement2 + delim;
|
||||
|
||||
List<String> statements = new ArrayList<>();
|
||||
ScriptUtils.splitSqlScript(script, ';', statements);
|
||||
|
||||
assertThat(statements).hasSize(2).containsSequence(statement1, statement2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void readAndSplitScriptWithMultipleNewlinesAsSeparator() {
|
||||
|
||||
String script = readScript("db-test-data-multi-newline.sql");
|
||||
List<String> statements = new ArrayList<>();
|
||||
ScriptUtils.splitSqlScript(script, "\n\n", statements);
|
||||
|
||||
String statement1 = "insert into users (last_name) values ('Walter')";
|
||||
String statement2 = "insert into users (last_name) values ('Jesse')";
|
||||
|
||||
assertThat(statements.size()).as("wrong number of statements").isEqualTo(2);
|
||||
assertThat(statements.get(0)).as("statement 1 not split correctly").isEqualTo(statement1);
|
||||
assertThat(statements.get(1)).as("statement 2 not split correctly").isEqualTo(statement2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void readAndSplitScriptContainingComments() {
|
||||
String script = readScript("test-data-with-comments.sql");
|
||||
splitScriptContainingComments(script);
|
||||
}
|
||||
|
||||
@Test
|
||||
void readAndSplitScriptContainingCommentsWithWindowsLineEnding() {
|
||||
String script = readScript("test-data-with-comments.sql").replaceAll("\n", "\r\n");
|
||||
splitScriptContainingComments(script);
|
||||
}
|
||||
|
||||
private void splitScriptContainingComments(String script) {
|
||||
|
||||
List<String> statements = new ArrayList<>();
|
||||
ScriptUtils.splitSqlScript(script, ';', statements);
|
||||
|
||||
String statement1 = "insert into customer (id, name) values (1, 'Rod; Johnson'), (2, 'Adrian Collier')";
|
||||
String statement2 = "insert into orders(id, order_date, customer_id) values (1, '2008-01-02', 2)";
|
||||
String statement3 = "insert into orders(id, order_date, customer_id) values (1, '2008-01-02', 2)";
|
||||
String statement4 = "INSERT INTO persons( person_id , name) VALUES( 1 , 'Name' )";
|
||||
|
||||
assertThat(statements).hasSize(4).containsSequence(statement1, statement2, statement3, statement4);
|
||||
}
|
||||
|
||||
@Test
|
||||
void readAndSplitScriptContainingCommentsWithLeadingTabs() {
|
||||
|
||||
String script = readScript("test-data-with-comments-and-leading-tabs.sql");
|
||||
List<String> statements = new ArrayList<>();
|
||||
ScriptUtils.splitSqlScript(script, ';', statements);
|
||||
|
||||
String statement1 = "insert into customer (id, name) values (1, 'Walter White')";
|
||||
String statement2 = "insert into orders(id, order_date, customer_id) values (1, '2013-06-08', 1)";
|
||||
String statement3 = "insert into orders(id, order_date, customer_id) values (2, '2013-06-08', 1)";
|
||||
|
||||
assertThat(statements).hasSize(3).containsSequence(statement1, statement2, statement3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void readAndSplitScriptContainingMultiLineComments() {
|
||||
|
||||
String script = readScript("test-data-with-multi-line-comments.sql");
|
||||
List<String> statements = new ArrayList<>();
|
||||
ScriptUtils.splitSqlScript(script, ';', statements);
|
||||
|
||||
String statement1 = "INSERT INTO users(first_name, last_name) VALUES('Walter', 'White')";
|
||||
String statement2 = "INSERT INTO users(first_name, last_name) VALUES( 'Jesse' , 'Pinkman' )";
|
||||
|
||||
assertThat(statements).hasSize(2).containsSequence(statement1, statement2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void readAndSplitScriptContainingMultiLineNestedComments() {
|
||||
|
||||
String script = readScript("test-data-with-multi-line-nested-comments.sql");
|
||||
List<String> statements = new ArrayList<>();
|
||||
ScriptUtils.splitSqlScript(script, ';', statements);
|
||||
|
||||
String statement1 = "INSERT INTO users(first_name, last_name) VALUES('Walter', 'White')";
|
||||
String statement2 = "INSERT INTO users(first_name, last_name) VALUES( 'Jesse' , 'Pinkman' )";
|
||||
|
||||
assertThat(statements).hasSize(2).containsSequence(statement1, statement2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void containsDelimiters() {
|
||||
|
||||
assertThat(ScriptUtils.containsSqlScriptDelimiters("select 1\n select ';'", ";")).isFalse();
|
||||
assertThat(ScriptUtils.containsSqlScriptDelimiters("select 1; select 2", ";")).isTrue();
|
||||
|
||||
assertThat(ScriptUtils.containsSqlScriptDelimiters("select 1; select '\\n\n';", "\n")).isFalse();
|
||||
assertThat(ScriptUtils.containsSqlScriptDelimiters("select 1\n select 2", "\n")).isTrue();
|
||||
|
||||
assertThat(ScriptUtils.containsSqlScriptDelimiters("select 1\n select 2", "\n\n")).isFalse();
|
||||
assertThat(ScriptUtils.containsSqlScriptDelimiters("select 1\n\n select 2", "\n\n")).isTrue();
|
||||
|
||||
// MySQL style escapes '\\'
|
||||
assertThat(
|
||||
ScriptUtils.containsSqlScriptDelimiters("insert into users(first_name, last_name)\nvalues('a\\\\', 'b;')", ";"))
|
||||
.isFalse();
|
||||
assertThat(ScriptUtils.containsSqlScriptDelimiters(
|
||||
"insert into users(first_name, last_name)\nvalues('Charles', 'd\\'Artagnan'); select 1;", ";")).isTrue();
|
||||
}
|
||||
|
||||
private String readScript(String path) {
|
||||
EncodedResource resource = new EncodedResource(new ClassPathResource(path, getClass()));
|
||||
return ScriptUtils.readScript(resource, new DefaultDataBufferFactory()).block();
|
||||
}
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory.lookup;
|
||||
|
||||
import static java.util.Collections.*;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
import reactor.util.context.Context;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link AbstractRoutingConnectionFactory}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class AbstractRoutingConnectionFactoryUnitTests {
|
||||
|
||||
private static final String ROUTING_KEY = "routingKey";
|
||||
|
||||
@Mock ConnectionFactory defaultConnectionFactory;
|
||||
@Mock ConnectionFactory routedConnectionFactory;
|
||||
|
||||
private DummyRoutingConnectionFactory connectionFactory;
|
||||
|
||||
@BeforeEach
|
||||
void before() {
|
||||
|
||||
connectionFactory = new DummyRoutingConnectionFactory();
|
||||
connectionFactory.setDefaultTargetConnectionFactory(defaultConnectionFactory);
|
||||
}
|
||||
|
||||
@Test // gh-98
|
||||
void shouldDetermineRoutedFactory() {
|
||||
|
||||
connectionFactory.setTargetConnectionFactories(singletonMap("key", routedConnectionFactory));
|
||||
connectionFactory.setConnectionFactoryLookup(new MapConnectionFactoryLookup());
|
||||
connectionFactory.afterPropertiesSet();
|
||||
|
||||
connectionFactory.determineTargetConnectionFactory() //
|
||||
.subscriberContext(Context.of(ROUTING_KEY, "key")) //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(routedConnectionFactory) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // gh-98
|
||||
void shouldFallbackToDefaultConnectionFactory() {
|
||||
|
||||
connectionFactory.setTargetConnectionFactories(singletonMap("key", routedConnectionFactory));
|
||||
connectionFactory.afterPropertiesSet();
|
||||
|
||||
connectionFactory.determineTargetConnectionFactory() //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(defaultConnectionFactory) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // gh-98
|
||||
void initializationShouldFailUnsupportedLookupKey() {
|
||||
|
||||
connectionFactory.setTargetConnectionFactories(singletonMap("key", new Object()));
|
||||
|
||||
assertThatThrownBy(() -> connectionFactory.afterPropertiesSet()).isInstanceOf(IllegalArgumentException.class);
|
||||
}
|
||||
|
||||
@Test // gh-98
|
||||
void initializationShouldFailUnresolvableKey() {
|
||||
|
||||
connectionFactory.setTargetConnectionFactories(singletonMap("key", "value"));
|
||||
connectionFactory.setConnectionFactoryLookup(new MapConnectionFactoryLookup());
|
||||
|
||||
assertThatThrownBy(() -> connectionFactory.afterPropertiesSet()) //
|
||||
.isInstanceOf(ConnectionFactoryLookupFailureException.class) //
|
||||
.hasMessageContaining("No ConnectionFactory with name 'value' registered");
|
||||
}
|
||||
|
||||
@Test // gh-98
|
||||
void unresolvableConnectionFactoryRetrievalShouldFail() {
|
||||
|
||||
connectionFactory.setLenientFallback(false);
|
||||
connectionFactory.setConnectionFactoryLookup(new MapConnectionFactoryLookup());
|
||||
connectionFactory.setTargetConnectionFactories(singletonMap("key", routedConnectionFactory));
|
||||
connectionFactory.afterPropertiesSet();
|
||||
|
||||
connectionFactory.determineTargetConnectionFactory() //
|
||||
.subscriberContext(Context.of(ROUTING_KEY, "unknown")) //
|
||||
.as(StepVerifier::create) //
|
||||
.verifyError(IllegalStateException.class);
|
||||
}
|
||||
|
||||
@Test // gh-98
|
||||
void connectionFactoryRetrievalWithUnknownLookupKeyShouldReturnDefaultConnectionFactory() {
|
||||
|
||||
connectionFactory.setTargetConnectionFactories(singletonMap("key", routedConnectionFactory));
|
||||
connectionFactory.setDefaultTargetConnectionFactory(defaultConnectionFactory);
|
||||
connectionFactory.afterPropertiesSet();
|
||||
|
||||
connectionFactory.determineTargetConnectionFactory() //
|
||||
.subscriberContext(Context.of(ROUTING_KEY, "unknown")) //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(defaultConnectionFactory) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // gh-98
|
||||
void connectionFactoryRetrievalWithoutLookupKeyShouldReturnDefaultConnectionFactory() {
|
||||
|
||||
connectionFactory.setTargetConnectionFactories(singletonMap("key", routedConnectionFactory));
|
||||
connectionFactory.setDefaultTargetConnectionFactory(defaultConnectionFactory);
|
||||
connectionFactory.setLenientFallback(false);
|
||||
connectionFactory.afterPropertiesSet();
|
||||
|
||||
connectionFactory.determineTargetConnectionFactory() //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(defaultConnectionFactory) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // gh-98
|
||||
void shouldLookupFromMap() {
|
||||
|
||||
MapConnectionFactoryLookup lookup = new MapConnectionFactoryLookup("lookup-key", routedConnectionFactory);
|
||||
|
||||
connectionFactory.setConnectionFactoryLookup(lookup);
|
||||
connectionFactory.setTargetConnectionFactories(singletonMap("my-key", "lookup-key"));
|
||||
connectionFactory.afterPropertiesSet();
|
||||
|
||||
connectionFactory.determineTargetConnectionFactory() //
|
||||
.subscriberContext(Context.of(ROUTING_KEY, "my-key")) //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(routedConnectionFactory) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // gh-98
|
||||
void shouldAllowModificationsAfterInitialization() {
|
||||
|
||||
MapConnectionFactoryLookup lookup = new MapConnectionFactoryLookup();
|
||||
|
||||
connectionFactory.setConnectionFactoryLookup(lookup);
|
||||
connectionFactory.setTargetConnectionFactories(lookup.getConnectionFactories());
|
||||
connectionFactory.afterPropertiesSet();
|
||||
|
||||
connectionFactory.determineTargetConnectionFactory() //
|
||||
.subscriberContext(Context.of(ROUTING_KEY, "lookup-key")) //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(defaultConnectionFactory) //
|
||||
.verifyComplete();
|
||||
|
||||
lookup.addConnectionFactory("lookup-key", routedConnectionFactory);
|
||||
connectionFactory.afterPropertiesSet();
|
||||
|
||||
connectionFactory.determineTargetConnectionFactory() //
|
||||
.subscriberContext(Context.of(ROUTING_KEY, "lookup-key")) //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(routedConnectionFactory) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
static class DummyRoutingConnectionFactory extends AbstractRoutingConnectionFactory {
|
||||
|
||||
@Override
|
||||
protected Mono<Object> determineCurrentLookupKey() {
|
||||
return Mono.subscriberContext().filter(it -> it.hasKey(ROUTING_KEY)).map(it -> it.get(ROUTING_KEY));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory.lookup;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link BeanFactoryConnectionFactoryLookup}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class BeanFactoryConnectionFactoryLookupUnitTests {
|
||||
|
||||
private static final String CONNECTION_FACTORY_BEAN_NAME = "connectionFactory";
|
||||
|
||||
@Mock BeanFactory beanFactory;
|
||||
|
||||
@Test // gh-98
|
||||
void shouldLookupConnectionFactory() {
|
||||
|
||||
DummyConnectionFactory expectedConnectionFactory = new DummyConnectionFactory();
|
||||
when(beanFactory.getBean(CONNECTION_FACTORY_BEAN_NAME, ConnectionFactory.class))
|
||||
.thenReturn(expectedConnectionFactory);
|
||||
|
||||
BeanFactoryConnectionFactoryLookup lookup = new BeanFactoryConnectionFactoryLookup();
|
||||
lookup.setBeanFactory(beanFactory);
|
||||
|
||||
ConnectionFactory connectionFactory = lookup.getConnectionFactory(CONNECTION_FACTORY_BEAN_NAME);
|
||||
|
||||
assertThat(connectionFactory).isNotNull();
|
||||
assertThat(connectionFactory).isSameAs(expectedConnectionFactory);
|
||||
}
|
||||
|
||||
@Test // gh-98
|
||||
void shouldLookupWhereBeanFactoryYieldsNonConnectionFactoryType() {
|
||||
|
||||
BeanFactory beanFactory = mock(BeanFactory.class);
|
||||
|
||||
when(beanFactory.getBean(CONNECTION_FACTORY_BEAN_NAME, ConnectionFactory.class)).thenThrow(
|
||||
new BeanNotOfRequiredTypeException(CONNECTION_FACTORY_BEAN_NAME, ConnectionFactory.class, String.class));
|
||||
|
||||
BeanFactoryConnectionFactoryLookup lookup = new BeanFactoryConnectionFactoryLookup(beanFactory);
|
||||
|
||||
assertThatExceptionOfType(ConnectionFactoryLookupFailureException.class)
|
||||
.isThrownBy(() -> lookup.getConnectionFactory(CONNECTION_FACTORY_BEAN_NAME));
|
||||
}
|
||||
|
||||
@Test // gh-98
|
||||
void shouldLookupWhereBeanFactoryHasNotBeenSupplied() {
|
||||
|
||||
BeanFactoryConnectionFactoryLookup lookup = new BeanFactoryConnectionFactoryLookup();
|
||||
|
||||
assertThatThrownBy(() -> lookup.getConnectionFactory(CONNECTION_FACTORY_BEAN_NAME))
|
||||
.isInstanceOf(IllegalStateException.class);
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory.lookup;
|
||||
|
||||
import io.r2dbc.spi.Connection;
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import io.r2dbc.spi.ConnectionFactoryMetadata;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
/**
|
||||
* Stub, do-nothing {@link ConnectionFactory} implementation.
|
||||
* <p>
|
||||
* All methods throw {@link UnsupportedOperationException}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class DummyConnectionFactory implements ConnectionFactory {
|
||||
|
||||
@Override
|
||||
public Publisher<? extends Connection> create() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConnectionFactoryMetadata getMetadata() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.data.r2dbc.connectionfactory.lookup;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link MapConnectionFactoryLookup}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class MapConnectionFactoryLookupUnitTests {
|
||||
|
||||
private static final String CONNECTION_FACTORY_NAME = "connectionFactory";
|
||||
|
||||
@Test // gh-98
|
||||
public void getConnectionFactorysReturnsUnmodifiableMap() {
|
||||
|
||||
MapConnectionFactoryLookup lookup = new MapConnectionFactoryLookup();
|
||||
Map<String, ConnectionFactory> connectionFactories = lookup.getConnectionFactories();
|
||||
|
||||
assertThatThrownBy(() -> connectionFactories.put("", new DummyConnectionFactory()))
|
||||
.isInstanceOf(UnsupportedOperationException.class);
|
||||
}
|
||||
|
||||
@Test // gh-98
|
||||
public void shouldLookupConnectionFactory() {
|
||||
|
||||
Map<String, ConnectionFactory> connectionFactories = new HashMap<>();
|
||||
DummyConnectionFactory expectedConnectionFactory = new DummyConnectionFactory();
|
||||
|
||||
connectionFactories.put(CONNECTION_FACTORY_NAME, expectedConnectionFactory);
|
||||
MapConnectionFactoryLookup lookup = new MapConnectionFactoryLookup();
|
||||
|
||||
lookup.setConnectionFactories(connectionFactories);
|
||||
|
||||
ConnectionFactory connectionFactory = lookup.getConnectionFactory(CONNECTION_FACTORY_NAME);
|
||||
|
||||
assertThat(connectionFactory).isNotNull();
|
||||
assertThat(connectionFactory).isSameAs(expectedConnectionFactory);
|
||||
}
|
||||
|
||||
@Test // gh-98
|
||||
public void addingConnectionFactoryPermitsOverride() {
|
||||
|
||||
Map<String, ConnectionFactory> connectionFactories = new HashMap<>();
|
||||
DummyConnectionFactory overriddenConnectionFactory = new DummyConnectionFactory();
|
||||
DummyConnectionFactory expectedConnectionFactory = new DummyConnectionFactory();
|
||||
connectionFactories.put(CONNECTION_FACTORY_NAME, overriddenConnectionFactory);
|
||||
|
||||
MapConnectionFactoryLookup lookup = new MapConnectionFactoryLookup();
|
||||
|
||||
lookup.setConnectionFactories(connectionFactories);
|
||||
lookup.addConnectionFactory(CONNECTION_FACTORY_NAME, expectedConnectionFactory);
|
||||
|
||||
ConnectionFactory connectionFactory = lookup.getConnectionFactory(CONNECTION_FACTORY_NAME);
|
||||
|
||||
assertThat(connectionFactory).isNotNull();
|
||||
assertThat(connectionFactory).isSameAs(expectedConnectionFactory);
|
||||
}
|
||||
|
||||
@Test // gh-98
|
||||
@SuppressWarnings("unchecked")
|
||||
public void getConnectionFactoryWhereSuppliedMapHasNonConnectionFactoryTypeUnderSpecifiedKey() {
|
||||
|
||||
Map connectionFactories = new HashMap<>();
|
||||
connectionFactories.put(CONNECTION_FACTORY_NAME, new Object());
|
||||
MapConnectionFactoryLookup lookup = new MapConnectionFactoryLookup(connectionFactories);
|
||||
|
||||
assertThatThrownBy(() -> lookup.getConnectionFactory(CONNECTION_FACTORY_NAME))
|
||||
.isInstanceOf(ClassCastException.class);
|
||||
}
|
||||
|
||||
@Test // gh-98
|
||||
public void getConnectionFactoryWhereSuppliedMapHasNoEntryForSpecifiedKey() {
|
||||
|
||||
MapConnectionFactoryLookup lookup = new MapConnectionFactoryLookup();
|
||||
|
||||
assertThatThrownBy(() -> lookup.getConnectionFactory(CONNECTION_FACTORY_NAME))
|
||||
.isInstanceOf(ConnectionFactoryLookupFailureException.class);
|
||||
}
|
||||
}
|
||||
@@ -1,527 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2021 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.data.r2dbc.core;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.springframework.data.domain.Sort.Order.*;
|
||||
import static org.springframework.data.r2dbc.query.Criteria.*;
|
||||
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import lombok.Data;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.assertj.core.api.Condition;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.r2dbc.testing.R2dbcIntegrationTestSupport;
|
||||
import org.springframework.data.relational.core.mapping.Table;
|
||||
import org.springframework.data.relational.core.query.Criteria;
|
||||
import org.springframework.data.relational.core.query.Update;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link DatabaseClient}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Mingyuan Wu
|
||||
*/
|
||||
public abstract class AbstractDatabaseClientIntegrationTests extends R2dbcIntegrationTestSupport {
|
||||
|
||||
private ConnectionFactory connectionFactory;
|
||||
|
||||
private JdbcTemplate jdbc;
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
|
||||
connectionFactory = createConnectionFactory();
|
||||
|
||||
jdbc = createJdbcTemplate(createDataSource());
|
||||
|
||||
try {
|
||||
jdbc.execute("DROP TABLE legoset");
|
||||
} catch (DataAccessException e) {}
|
||||
jdbc.execute(getCreateTableStatement());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link DataSource} to be used in this test.
|
||||
*
|
||||
* @return the {@link DataSource} to be used in this test.
|
||||
*/
|
||||
protected abstract DataSource createDataSource();
|
||||
|
||||
/**
|
||||
* Creates a {@link ConnectionFactory} to be used in this test.
|
||||
*
|
||||
* @return the {@link ConnectionFactory} to be used in this test.
|
||||
*/
|
||||
protected abstract ConnectionFactory createConnectionFactory();
|
||||
|
||||
/**
|
||||
* Returns the the CREATE TABLE statement for table {@code legoset} with the following three columns:
|
||||
* <ul>
|
||||
* <li>id integer (primary key), not null</li>
|
||||
* <li>name varchar(255), nullable</li>
|
||||
* <li>manual integer, nullable</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return the CREATE TABLE statement for table {@code legoset} with three columns.
|
||||
*/
|
||||
protected abstract String getCreateTableStatement();
|
||||
|
||||
/**
|
||||
* Get a parameterized {@code INSERT INTO legoset} statement setting id, name, and manual values.
|
||||
*/
|
||||
protected String getInsertIntoLegosetStatement() {
|
||||
return "INSERT INTO legoset (id, name, manual) VALUES(:id, :name, :manual)";
|
||||
}
|
||||
|
||||
@Test // gh-2
|
||||
public void executeInsert() {
|
||||
|
||||
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
|
||||
|
||||
databaseClient.execute(getInsertIntoLegosetStatement()) //
|
||||
.bind("id", 42055) //
|
||||
.bind("name", "SCHAUFELRADBAGGER") //
|
||||
.bindNull("manual", Integer.class) //
|
||||
.fetch().rowsUpdated() //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(1) //
|
||||
.verifyComplete();
|
||||
|
||||
assertThat(jdbc.queryForMap("SELECT id, name, manual FROM legoset")).hasEntrySatisfying("id", numberOf(42055));
|
||||
}
|
||||
|
||||
@Test // gh-2
|
||||
public void shouldTranslateDuplicateKeyException() {
|
||||
|
||||
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
|
||||
|
||||
executeInsert();
|
||||
|
||||
databaseClient.execute(getInsertIntoLegosetStatement()) //
|
||||
.bind("id", 42055) //
|
||||
.bind("name", "SCHAUFELRADBAGGER") //
|
||||
.bindNull("manual", Integer.class) //
|
||||
.fetch().rowsUpdated() //
|
||||
.as(StepVerifier::create) //
|
||||
.expectErrorSatisfies(exception -> assertThat(exception) //
|
||||
.isInstanceOf(DataIntegrityViolationException.class) //
|
||||
.hasMessageContaining("execute; SQL [INSERT INTO legoset")) //
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test // gh-2
|
||||
public void executeSelect() {
|
||||
|
||||
jdbc.execute("INSERT INTO legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)");
|
||||
|
||||
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
|
||||
|
||||
databaseClient.execute("SELECT id, name, manual FROM legoset") //
|
||||
.as(LegoSet.class) //
|
||||
.fetch().all() //
|
||||
.as(StepVerifier::create) //
|
||||
.consumeNextWith(actual -> {
|
||||
|
||||
assertThat(actual.getId()).isEqualTo(42055);
|
||||
assertThat(actual.getName()).isEqualTo("SCHAUFELRADBAGGER");
|
||||
assertThat(actual.getManual()).isEqualTo(12);
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test // gh-2
|
||||
public void executeSelectNamedParameters() {
|
||||
|
||||
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
|
||||
|
||||
databaseClient.execute("SELECT id, name, manual FROM legoset WHERE name = :name or name = :name") //
|
||||
.bind("name", "unknown").as(LegoSet.class) //
|
||||
.fetch().all() //
|
||||
.as(StepVerifier::create) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // gh-2
|
||||
public void insert() {
|
||||
|
||||
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
|
||||
|
||||
databaseClient.insert().into("legoset")//
|
||||
.value("id", 42055) //
|
||||
.value("name", "SCHAUFELRADBAGGER") //
|
||||
.nullValue("manual", Integer.class) //
|
||||
.fetch() //
|
||||
.rowsUpdated() //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(1) //
|
||||
.verifyComplete();
|
||||
|
||||
assertThat(jdbc.queryForMap("SELECT id, name, manual FROM legoset")).hasEntrySatisfying("id", numberOf(42055));
|
||||
}
|
||||
|
||||
@Test // gh-2
|
||||
public void insertWithoutResult() {
|
||||
|
||||
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
|
||||
|
||||
databaseClient.insert().into("legoset")//
|
||||
.value("id", 42055) //
|
||||
.value("name", "SCHAUFELRADBAGGER") //
|
||||
.nullValue("manual", Integer.class) //
|
||||
.then() //
|
||||
.as(StepVerifier::create) //
|
||||
.verifyComplete();
|
||||
|
||||
assertThat(jdbc.queryForMap("SELECT id, name, manual FROM legoset")).hasEntrySatisfying("id", numberOf(42055));
|
||||
}
|
||||
|
||||
@Test // gh-2
|
||||
public void insertTypedObject() {
|
||||
|
||||
LegoSet legoSet = new LegoSet();
|
||||
legoSet.setId(42055);
|
||||
legoSet.setName("SCHAUFELRADBAGGER");
|
||||
legoSet.setManual(12);
|
||||
|
||||
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
|
||||
|
||||
databaseClient.insert().into(LegoSet.class)//
|
||||
.using(legoSet) //
|
||||
.fetch() //
|
||||
.rowsUpdated() //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(1) //
|
||||
.verifyComplete();
|
||||
|
||||
assertThat(jdbc.queryForMap("SELECT id, name, manual FROM legoset")).hasEntrySatisfying("id", numberOf(42055));
|
||||
}
|
||||
|
||||
@Test // gh-2
|
||||
public void insertTypedObjectWithBinary() {
|
||||
|
||||
LegoSet legoSet = new LegoSet();
|
||||
legoSet.setId(42055);
|
||||
legoSet.setName("SCHAUFELRADBAGGER");
|
||||
legoSet.setManual(12);
|
||||
legoSet.setCert(new byte[] { 1, 2, 3, 4, 5 });
|
||||
|
||||
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
|
||||
|
||||
databaseClient.insert().into(LegoSet.class)//
|
||||
.using(legoSet) //
|
||||
.fetch() //
|
||||
.rowsUpdated() //
|
||||
.then() //
|
||||
.as(StepVerifier::create) //
|
||||
.verifyComplete();
|
||||
|
||||
databaseClient.select().from(LegoSet.class) //
|
||||
.matching(where("name").is("SCHAUFELRADBAGGER")) //
|
||||
.fetch() //
|
||||
.first() //
|
||||
.as(StepVerifier::create) //
|
||||
.assertNext(actual -> {
|
||||
|
||||
assertThat(actual.getCert()).isEqualTo(new byte[] { 1, 2, 3, 4, 5 });
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test // gh-64
|
||||
public void update() {
|
||||
|
||||
jdbc.execute("INSERT INTO legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)");
|
||||
|
||||
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
|
||||
|
||||
databaseClient.update().table("legoset")//
|
||||
.using(Update.update("name", "Lego")) //
|
||||
.matching(Criteria.where("id").is(42055)) //
|
||||
.fetch() //
|
||||
.rowsUpdated() //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(1) //
|
||||
.verifyComplete();
|
||||
|
||||
assertThat(jdbc.queryForMap("SELECT name, manual FROM legoset")).containsEntry("name", "Lego");
|
||||
}
|
||||
|
||||
@Test // gh-64
|
||||
public void updateWithoutResult() {
|
||||
|
||||
jdbc.execute("INSERT INTO legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)");
|
||||
|
||||
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
|
||||
|
||||
databaseClient.update().table("legoset")//
|
||||
.using(Update.update("name", "Lego")) //
|
||||
.then() //
|
||||
.as(StepVerifier::create) //
|
||||
.verifyComplete();
|
||||
|
||||
assertThat(jdbc.queryForMap("SELECT name, manual FROM legoset")).containsEntry("name", "Lego");
|
||||
}
|
||||
|
||||
@Test // gh-64
|
||||
public void updateTypedObject() {
|
||||
|
||||
jdbc.execute("INSERT INTO legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)");
|
||||
|
||||
LegoSet legoSet = new LegoSet();
|
||||
legoSet.setId(42055);
|
||||
legoSet.setName("Lego");
|
||||
legoSet.setManual(null);
|
||||
|
||||
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
|
||||
|
||||
databaseClient.update() //
|
||||
.table(LegoSet.class) //
|
||||
.using(legoSet) //
|
||||
.fetch() //
|
||||
.rowsUpdated() //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(1) //
|
||||
.verifyComplete();
|
||||
|
||||
assertThat(jdbc.queryForMap("SELECT name, manual FROM legoset")).containsEntry("name", "Lego");
|
||||
}
|
||||
|
||||
@Test // gh-64
|
||||
public void deleteUntyped() {
|
||||
|
||||
jdbc.execute("INSERT INTO legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)");
|
||||
jdbc.execute("INSERT INTO legoset (id, name, manual) VALUES(42064, 'FORSCHUNGSSCHIFF', 13)");
|
||||
|
||||
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
|
||||
|
||||
databaseClient.delete() //
|
||||
.from("legoset") //
|
||||
.matching(where("id").is(42055)) //
|
||||
.fetch() //
|
||||
.rowsUpdated() //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(1).verifyComplete();
|
||||
|
||||
assertThat(jdbc.queryForList("SELECT id AS count FROM legoset")).hasSize(1);
|
||||
}
|
||||
|
||||
@Test // gh-64
|
||||
public void deleteTyped() {
|
||||
|
||||
jdbc.execute("INSERT INTO legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)");
|
||||
jdbc.execute("INSERT INTO legoset (id, name, manual) VALUES(42064, 'FORSCHUNGSSCHIFF', 13)");
|
||||
|
||||
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
|
||||
|
||||
databaseClient.delete() //
|
||||
.from(LegoSet.class) //
|
||||
.matching(where("id").is(42055)) //
|
||||
.then() //
|
||||
.as(StepVerifier::create) //
|
||||
.verifyComplete();
|
||||
|
||||
assertThat(jdbc.queryForList("SELECT id AS count FROM legoset")).hasSize(1);
|
||||
}
|
||||
|
||||
@Test // gh-2
|
||||
public void selectAsMap() {
|
||||
|
||||
jdbc.execute("INSERT INTO legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)");
|
||||
|
||||
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
|
||||
|
||||
databaseClient.select().from(LegoSet.class) //
|
||||
.project("id", "name", "manual") //
|
||||
.orderBy(Sort.by("id")) //
|
||||
.fetch() //
|
||||
.all() //
|
||||
.as(StepVerifier::create) //
|
||||
.assertNext(actual -> {
|
||||
assertThat(actual.getId()).isEqualTo(42055);
|
||||
assertThat(actual.getName()).isEqualTo("SCHAUFELRADBAGGER");
|
||||
assertThat(actual.getManual()).isEqualTo(12);
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test // gh-8
|
||||
public void selectExtracting() {
|
||||
|
||||
jdbc.execute("INSERT INTO legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)");
|
||||
|
||||
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
|
||||
|
||||
databaseClient.select().from("legoset") //
|
||||
.project("id", "name", "manual") //
|
||||
.orderBy(Sort.by("id")) //
|
||||
.map((r) -> r.get("id", Integer.class)) //
|
||||
.all() //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(42055) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // gh-109
|
||||
public void selectSimpleTypeProjection() {
|
||||
|
||||
jdbc.execute("INSERT INTO legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)");
|
||||
|
||||
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
|
||||
|
||||
databaseClient.execute("SELECT COUNT(*) FROM legoset") //
|
||||
.as(Long.class) //
|
||||
.fetch() //
|
||||
.all() //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(1L) //
|
||||
.verifyComplete();
|
||||
|
||||
databaseClient.execute("SELECT name FROM legoset") //
|
||||
.as(String.class) //
|
||||
.fetch() //
|
||||
.one() //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext("SCHAUFELRADBAGGER") //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // gh-8
|
||||
public void selectWithCriteria() {
|
||||
|
||||
jdbc.execute("INSERT INTO legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)");
|
||||
|
||||
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
|
||||
|
||||
databaseClient.select().from("legoset") //
|
||||
.project("id", "name", "manual") //
|
||||
.orderBy(Sort.by("id")) //
|
||||
.matching(where("id").greaterThanOrEquals(42055).and("id").lessThanOrEquals(42055))
|
||||
.map((r) -> r.get("id", Integer.class)) //
|
||||
.all() //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(42055) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // gh-64
|
||||
public void selectWithCriteriaIn() {
|
||||
|
||||
jdbc.execute("INSERT INTO legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)");
|
||||
jdbc.execute("INSERT INTO legoset (id, name, manual) VALUES(42064, 'FORSCHUNGSSCHIFF', 13)");
|
||||
jdbc.execute("INSERT INTO legoset (id, name, manual) VALUES(42068, 'FLUGHAFEN-LÖSCHFAHRZEUG', 13)");
|
||||
|
||||
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
|
||||
|
||||
databaseClient.select().from(LegoSet.class) //
|
||||
.orderBy(Sort.by("id")) //
|
||||
.matching(where("id").in(42055, 42064)) //
|
||||
.map((r, md) -> r.get("id", Integer.class)) //
|
||||
.all() //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(42055) //
|
||||
.expectNext(42064) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // gh-2
|
||||
public void selectOrderByIdDesc() {
|
||||
|
||||
jdbc.execute("INSERT INTO legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)");
|
||||
jdbc.execute("INSERT INTO legoset (id, name, manual) VALUES(42064, 'FORSCHUNGSSCHIFF', 13)");
|
||||
jdbc.execute("INSERT INTO legoset (id, name, manual) VALUES(42068, 'FLUGHAFEN-LÖSCHFAHRZEUG', 13)");
|
||||
|
||||
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
|
||||
|
||||
databaseClient.select().from(LegoSet.class) //
|
||||
.orderBy(Sort.by(desc("id"))) //
|
||||
.fetch().all() //
|
||||
.map(LegoSet::getId) //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(42068, 42064, 42055) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // gh-2
|
||||
public void selectOrderPaged() {
|
||||
|
||||
jdbc.execute("INSERT INTO legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)");
|
||||
jdbc.execute("INSERT INTO legoset (id, name, manual) VALUES(42064, 'FORSCHUNGSSCHIFF', 13)");
|
||||
jdbc.execute("INSERT INTO legoset (id, name, manual) VALUES(42068, 'FLUGHAFEN-LÖSCHFAHRZEUG', 13)");
|
||||
|
||||
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
|
||||
|
||||
databaseClient.select().from(LegoSet.class) //
|
||||
.orderBy(Sort.by(desc("id"))) //
|
||||
.page(PageRequest.of(2, 1)) //
|
||||
.fetch().all() //
|
||||
.map(LegoSet::getId) //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(42055) //
|
||||
.verifyComplete();
|
||||
|
||||
databaseClient.select().from(LegoSet.class) //
|
||||
.page(PageRequest.of(2, 1, Sort.by(Sort.Direction.ASC, "id"))) //
|
||||
.fetch().all() //
|
||||
.map(LegoSet::getId) //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(42068) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // gh-2
|
||||
public void selectTypedLater() {
|
||||
|
||||
jdbc.execute("INSERT INTO legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)");
|
||||
jdbc.execute("INSERT INTO legoset (id, name, manual) VALUES(42064, 'FORSCHUNGSSCHIFF', 13)");
|
||||
jdbc.execute("INSERT INTO legoset (id, name, manual) VALUES(42068, 'FLUGHAFEN-LÖSCHFAHRZEUG', 13)");
|
||||
|
||||
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
|
||||
|
||||
databaseClient.select().from("legoset") //
|
||||
.orderBy(Sort.by(desc("id"))) //
|
||||
.as(LegoSet.class) //
|
||||
.fetch().all() //
|
||||
.map(LegoSet::getId) //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(42068, 42064, 42055) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
private Condition<? super Object> numberOf(int expected) {
|
||||
return new Condition<>(it -> {
|
||||
return it instanceof Number && ((Number) it).intValue() == expected;
|
||||
}, "Number %d", expected);
|
||||
}
|
||||
|
||||
@Data
|
||||
@Table("legoset")
|
||||
static class LegoSet {
|
||||
|
||||
@Id int id;
|
||||
String name;
|
||||
Integer manual;
|
||||
byte[] cert;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user