Browse Source
Removal of the connectionfactory package and deprecated core classes that were migrated into Spring Framework R2DBC. Closes #712pull/1188/head
134 changed files with 86 additions and 15886 deletions
@ -1,60 +0,0 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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); |
||||
} |
||||
} |
||||
@ -1,63 +0,0 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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); |
||||
} |
||||
} |
||||
@ -1,154 +0,0 @@
@@ -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 {} |
||||
} |
||||
@ -1,48 +0,0 @@
@@ -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 @@
@@ -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 {} |
||||
@ -1,54 +0,0 @@
@@ -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 @@
@@ -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(); |
||||
} |
||||
} |
||||
@ -1,52 +0,0 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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; |
||||
} |
||||
} |
||||
@ -1,35 +0,0 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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); |
||||
} |
||||
} |
||||
} |
||||
@ -1,99 +0,0 @@
@@ -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); |
||||
} |
||||
} |
||||
@ -1,103 +0,0 @@
@@ -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; |
||||
} |
||||
} |
||||
@ -1,125 +0,0 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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
Loading…
Reference in new issue