Browse Source

Remove deprecated `DatabaseClient`, `connectionfactory` package and other deprecated code

Removal of the connectionfactory package and deprecated core classes that were migrated into Spring Framework R2DBC.

Closes #712
pull/1188/head
Mark Paluch 4 years ago
parent
commit
dbc4365eef
No known key found for this signature in database
GPG Key ID: 4406B84C1661DCD1
  1. 6
      src/main/asciidoc/reference/mapping.adoc
  2. 60
      src/main/java/org/springframework/data/r2dbc/BadSqlGrammarException.java
  3. 82
      src/main/java/org/springframework/data/r2dbc/InvalidResultAccessException.java
  4. 60
      src/main/java/org/springframework/data/r2dbc/UncategorizedR2dbcException.java
  5. 206
      src/main/java/org/springframework/data/r2dbc/connectionfactory/ConnectionFactoryUtils.java
  6. 47
      src/main/java/org/springframework/data/r2dbc/connectionfactory/ConnectionHandle.java
  7. 171
      src/main/java/org/springframework/data/r2dbc/connectionfactory/ConnectionHolder.java
  8. 43
      src/main/java/org/springframework/data/r2dbc/connectionfactory/ConnectionProxy.java
  9. 90
      src/main/java/org/springframework/data/r2dbc/connectionfactory/DelegatingConnectionFactory.java
  10. 83
      src/main/java/org/springframework/data/r2dbc/connectionfactory/R2dbcTransactionManager.java
  11. 58
      src/main/java/org/springframework/data/r2dbc/connectionfactory/SimpleConnectionHandle.java
  12. 295
      src/main/java/org/springframework/data/r2dbc/connectionfactory/SingleConnectionConnectionFactory.java
  13. 46
      src/main/java/org/springframework/data/r2dbc/connectionfactory/SmartConnectionFactory.java
  14. 198
      src/main/java/org/springframework/data/r2dbc/connectionfactory/TransactionAwareConnectionFactoryProxy.java
  15. 41
      src/main/java/org/springframework/data/r2dbc/connectionfactory/init/CannotReadScriptException.java
  16. 102
      src/main/java/org/springframework/data/r2dbc/connectionfactory/init/CompositeDatabasePopulator.java
  17. 113
      src/main/java/org/springframework/data/r2dbc/connectionfactory/init/ConnectionFactoryInitializer.java
  18. 45
      src/main/java/org/springframework/data/r2dbc/connectionfactory/init/DatabasePopulator.java
  19. 61
      src/main/java/org/springframework/data/r2dbc/connectionfactory/init/DatabasePopulatorUtils.java
  20. 283
      src/main/java/org/springframework/data/r2dbc/connectionfactory/init/ResourceDatabasePopulator.java
  21. 49
      src/main/java/org/springframework/data/r2dbc/connectionfactory/init/ScriptException.java
  22. 58
      src/main/java/org/springframework/data/r2dbc/connectionfactory/init/ScriptParseException.java
  23. 57
      src/main/java/org/springframework/data/r2dbc/connectionfactory/init/ScriptStatementFailedException.java
  24. 540
      src/main/java/org/springframework/data/r2dbc/connectionfactory/init/ScriptUtils.java
  25. 49
      src/main/java/org/springframework/data/r2dbc/connectionfactory/init/UncategorizedScriptException.java
  26. 7
      src/main/java/org/springframework/data/r2dbc/connectionfactory/init/package-info.java
  27. 248
      src/main/java/org/springframework/data/r2dbc/connectionfactory/lookup/AbstractRoutingConnectionFactory.java
  28. 93
      src/main/java/org/springframework/data/r2dbc/connectionfactory/lookup/BeanFactoryConnectionFactoryLookup.java
  29. 39
      src/main/java/org/springframework/data/r2dbc/connectionfactory/lookup/ConnectionFactoryLookup.java
  30. 50
      src/main/java/org/springframework/data/r2dbc/connectionfactory/lookup/ConnectionFactoryLookupFailureException.java
  31. 124
      src/main/java/org/springframework/data/r2dbc/connectionfactory/lookup/MapConnectionFactoryLookup.java
  32. 56
      src/main/java/org/springframework/data/r2dbc/connectionfactory/lookup/SingleConnectionFactoryLookup.java
  33. 7
      src/main/java/org/springframework/data/r2dbc/connectionfactory/lookup/package-info.java
  34. 7
      src/main/java/org/springframework/data/r2dbc/connectionfactory/package-info.java
  35. 90
      src/main/java/org/springframework/data/r2dbc/convert/ColumnMapRowMapper.java
  36. 2
      src/main/java/org/springframework/data/r2dbc/core/BindParameterSource.java
  37. 63
      src/main/java/org/springframework/data/r2dbc/core/ConnectionAccessor.java
  38. 929
      src/main/java/org/springframework/data/r2dbc/core/DatabaseClient.java
  39. 1704
      src/main/java/org/springframework/data/r2dbc/core/DefaultDatabaseClient.java
  40. 187
      src/main/java/org/springframework/data/r2dbc/core/DefaultDatabaseClientBuilder.java
  41. 91
      src/main/java/org/springframework/data/r2dbc/core/DefaultFetchSpec.java
  42. 33
      src/main/java/org/springframework/data/r2dbc/core/DefaultReactiveDataAccessStrategy.java
  43. 154
      src/main/java/org/springframework/data/r2dbc/core/DefaultSqlResult.java
  44. 7
      src/main/java/org/springframework/data/r2dbc/core/DefaultStatementMapper.java
  45. 48
      src/main/java/org/springframework/data/r2dbc/core/ExecuteFunction.java
  46. 28
      src/main/java/org/springframework/data/r2dbc/core/FetchSpec.java
  47. 15
      src/main/java/org/springframework/data/r2dbc/core/MapBindParameterSource.java
  48. 10
      src/main/java/org/springframework/data/r2dbc/core/NamedParameterExpander.java
  49. 9
      src/main/java/org/springframework/data/r2dbc/core/NamedParameterUtils.java
  50. 54
      src/main/java/org/springframework/data/r2dbc/core/PreparedOperation.java
  51. 44
      src/main/java/org/springframework/data/r2dbc/core/QueryOperation.java
  52. 183
      src/main/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplate.java
  53. 20
      src/main/java/org/springframework/data/r2dbc/core/ReactiveDataAccessStrategy.java
  54. 52
      src/main/java/org/springframework/data/r2dbc/core/RowsFetchSpec.java
  55. 40
      src/main/java/org/springframework/data/r2dbc/core/SqlProvider.java
  56. 66
      src/main/java/org/springframework/data/r2dbc/core/StatementFilterFunction.java
  57. 48
      src/main/java/org/springframework/data/r2dbc/core/StatementFilterFunctions.java
  58. 27
      src/main/java/org/springframework/data/r2dbc/core/StatementMapper.java
  59. 35
      src/main/java/org/springframework/data/r2dbc/core/UpdatedRowsFetchSpec.java
  60. 44
      src/main/java/org/springframework/data/r2dbc/dialect/BindMarker.java
  61. 39
      src/main/java/org/springframework/data/r2dbc/dialect/BindMarkers.java
  62. 85
      src/main/java/org/springframework/data/r2dbc/dialect/BindMarkersAdapter.java
  63. 165
      src/main/java/org/springframework/data/r2dbc/dialect/BindMarkersFactory.java
  64. 64
      src/main/java/org/springframework/data/r2dbc/dialect/BindTarget.java
  65. 289
      src/main/java/org/springframework/data/r2dbc/dialect/Bindings.java
  66. 135
      src/main/java/org/springframework/data/r2dbc/dialect/MutableBindings.java
  67. 154
      src/main/java/org/springframework/data/r2dbc/mapping/SettableValue.java
  68. 705
      src/main/java/org/springframework/data/r2dbc/query/Criteria.java
  69. 38
      src/main/java/org/springframework/data/r2dbc/query/QueryMapper.java
  70. 99
      src/main/java/org/springframework/data/r2dbc/query/Update.java
  71. 40
      src/main/java/org/springframework/data/r2dbc/query/UpdateMapper.java
  72. 15
      src/main/java/org/springframework/data/r2dbc/repository/config/EnableR2dbcRepositories.java
  73. 9
      src/main/java/org/springframework/data/r2dbc/repository/config/R2dbcRepositoryConfigurationExtension.java
  74. 2
      src/main/java/org/springframework/data/r2dbc/repository/query/ExpressionEvaluatingParameterBinder.java
  75. 10
      src/main/java/org/springframework/data/r2dbc/repository/query/StringBasedR2dbcQuery.java
  76. 103
      src/main/java/org/springframework/data/r2dbc/repository/support/BindSpecAdapter.java
  77. 23
      src/main/java/org/springframework/data/r2dbc/repository/support/SimpleR2dbcRepository.java
  78. 125
      src/main/java/org/springframework/data/r2dbc/support/AbstractFallbackR2dbcExceptionTranslator.java
  79. 95
      src/main/java/org/springframework/data/r2dbc/support/R2dbcExceptionSubclassTranslator.java
  80. 61
      src/main/java/org/springframework/data/r2dbc/support/R2dbcExceptionTranslator.java
  81. 277
      src/main/java/org/springframework/data/r2dbc/support/SqlErrorCodeR2dbcExceptionTranslator.java
  82. 148
      src/main/java/org/springframework/data/r2dbc/support/SqlStateR2dbcExceptionTranslator.java
  83. 49
      src/main/kotlin/org/springframework/data/r2dbc/core/CriteriaStepExtensions.kt
  84. 169
      src/main/kotlin/org/springframework/data/r2dbc/core/DatabaseClientExtensions.kt
  85. 62
      src/main/kotlin/org/springframework/data/r2dbc/core/RowsFetchSpecExtensions.kt
  86. 26
      src/main/kotlin/org/springframework/data/r2dbc/core/UpdatedRowsFetchSpecExtensions.kt
  87. 60
      src/test/java/org/springframework/data/r2dbc/connectionfactory/DelegatingConnectionFactoryUnitTests.java
  88. 121
      src/test/java/org/springframework/data/r2dbc/connectionfactory/SingleConnectionConnectionFactoryUnitTests.java
  89. 163
      src/test/java/org/springframework/data/r2dbc/connectionfactory/TransactionAwareConnectionFactoryProxyUnitTests.java
  90. 134
      src/test/java/org/springframework/data/r2dbc/connectionfactory/init/AbstractDatabaseInitializationTests.java
  91. 112
      src/test/java/org/springframework/data/r2dbc/connectionfactory/init/CompositeDatabasePopulatorTests.java
  92. 70
      src/test/java/org/springframework/data/r2dbc/connectionfactory/init/ConnectionFactoryInitializerUnitTests.java
  93. 57
      src/test/java/org/springframework/data/r2dbc/connectionfactory/init/H2DatabasePopulatorIntegrationTests.java
  94. 110
      src/test/java/org/springframework/data/r2dbc/connectionfactory/init/ResourceDatabasePopulatorUnitTests.java
  95. 205
      src/test/java/org/springframework/data/r2dbc/connectionfactory/init/ScriptUtilsUnitTests.java
  96. 190
      src/test/java/org/springframework/data/r2dbc/connectionfactory/lookup/AbstractRoutingConnectionFactoryUnitTests.java
  97. 81
      src/test/java/org/springframework/data/r2dbc/connectionfactory/lookup/BeanFactoryConnectionFactoryLookupUnitTests.java
  98. 42
      src/test/java/org/springframework/data/r2dbc/connectionfactory/lookup/DummyConnectionFactory.java
  99. 102
      src/test/java/org/springframework/data/r2dbc/connectionfactory/lookup/MapConnectionFactoryLookupUnitTests.java
  100. 527
      src/test/java/org/springframework/data/r2dbc/core/AbstractDatabaseClientIntegrationTests.java
  101. Some files were not shown because too many files have changed in this diff Show More

6
src/main/asciidoc/reference/mapping.adoc

@ -283,9 +283,9 @@ public class PersonWriteConverter implements Converter<Person, OutboundRow> { @@ -283,9 +283,9 @@ public class PersonWriteConverter implements Converter<Person, OutboundRow> {
public OutboundRow convert(Person source) {
OutboundRow row = new OutboundRow();
row.put("id", SettableValue.from(source.getId()));
row.put("name", SettableValue.from(source.getFirstName()));
row.put("age", SettableValue.from(source.getAge()));
row.put("id", Parameter.from(source.getId()));
row.put("name", Parameter.from(source.getFirstName()));
row.put("age", Parameter.from(source.getAge()));
return row;
}
}

60
src/main/java/org/springframework/data/r2dbc/BadSqlGrammarException.java

@ -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();
}
}

82
src/main/java/org/springframework/data/r2dbc/InvalidResultAccessException.java

@ -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;
}
}

60
src/main/java/org/springframework/data/r2dbc/UncategorizedR2dbcException.java

@ -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();
}
}

206
src/main/java/org/springframework/data/r2dbc/connectionfactory/ConnectionFactoryUtils.java

@ -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;
}
}

47
src/main/java/org/springframework/data/r2dbc/connectionfactory/ConnectionHandle.java

@ -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) {
}
}

171
src/main/java/org/springframework/data/r2dbc/connectionfactory/ConnectionHolder.java

@ -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;
}
}

43
src/main/java/org/springframework/data/r2dbc/connectionfactory/ConnectionProxy.java

@ -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();
}

90
src/main/java/org/springframework/data/r2dbc/connectionfactory/DelegatingConnectionFactory.java

@ -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();
}
}

83
src/main/java/org/springframework/data/r2dbc/connectionfactory/R2dbcTransactionManager.java

@ -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();
}
}

58
src/main/java/org/springframework/data/r2dbc/connectionfactory/SimpleConnectionHandle.java

@ -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;
}
}

295
src/main/java/org/springframework/data/r2dbc/connectionfactory/SingleConnectionConnectionFactory.java

@ -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();
}
}
}
}

46
src/main/java/org/springframework/data/r2dbc/connectionfactory/SmartConnectionFactory.java

@ -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);
}

198
src/main/java/org/springframework/data/r2dbc/connectionfactory/TransactionAwareConnectionFactoryProxy.java

@ -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();
}
}
}

41
src/main/java/org/springframework/data/r2dbc/connectionfactory/init/CannotReadScriptException.java

@ -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);
}
}

102
src/main/java/org/springframework/data/r2dbc/connectionfactory/init/CompositeDatabasePopulator.java

@ -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();
}
}

113
src/main/java/org/springframework/data/r2dbc/connectionfactory/init/ConnectionFactoryInitializer.java

@ -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();
}
}
}

45
src/main/java/org/springframework/data/r2dbc/connectionfactory/init/DatabasePopulator.java

@ -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;
}

61
src/main/java/org/springframework/data/r2dbc/connectionfactory/init/DatabasePopulatorUtils.java

@ -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);
});
}
}

283
src/main/java/org/springframework/data/r2dbc/connectionfactory/init/ResourceDatabasePopulator.java

@ -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 "*&#47;"}.
*
* @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);
}
}

49
src/main/java/org/springframework/data/r2dbc/connectionfactory/init/ScriptException.java

@ -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);
}
}

58
src/main/java/org/springframework/data/r2dbc/connectionfactory/init/ScriptParseException.java

@ -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);
}
}

57
src/main/java/org/springframework/data/r2dbc/connectionfactory/init/ScriptStatementFailedException.java

@ -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);
}
}

540
src/main/java/org/springframework/data/r2dbc/connectionfactory/init/ScriptUtils.java

@ -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>"*&#47;"</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 &mdash; for example, within a statement &mdash; 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 &mdash; for example, within a statement &mdash; 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();
}
}

49
src/main/java/org/springframework/data/r2dbc/connectionfactory/init/UncategorizedScriptException.java

@ -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);
}
}

7
src/main/java/org/springframework/data/r2dbc/connectionfactory/init/package-info.java

@ -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;

248
src/main/java/org/springframework/data/r2dbc/connectionfactory/lookup/AbstractRoutingConnectionFactory.java

@ -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();
}

93
src/main/java/org/springframework/data/r2dbc/connectionfactory/lookup/BeanFactoryConnectionFactoryLookup.java

@ -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);
}
}
}

39
src/main/java/org/springframework/data/r2dbc/connectionfactory/lookup/ConnectionFactoryLookup.java

@ -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;
}

50
src/main/java/org/springframework/data/r2dbc/connectionfactory/lookup/ConnectionFactoryLookupFailureException.java

@ -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);
}
}

124
src/main/java/org/springframework/data/r2dbc/connectionfactory/lookup/MapConnectionFactoryLookup.java

@ -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");
});
}
}

56
src/main/java/org/springframework/data/r2dbc/connectionfactory/lookup/SingleConnectionFactoryLookup.java

@ -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;
}
}

7
src/main/java/org/springframework/data/r2dbc/connectionfactory/lookup/package-info.java

@ -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;

7
src/main/java/org/springframework/data/r2dbc/connectionfactory/package-info.java

@ -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;

90
src/main/java/org/springframework/data/r2dbc/convert/ColumnMapRowMapper.java

@ -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);
}
}

2
src/main/java/org/springframework/data/r2dbc/core/BindParameterSource.java

@ -32,7 +32,7 @@ import org.springframework.lang.Nullable; @@ -32,7 +32,7 @@ import org.springframework.lang.Nullable;
* @deprecated since 1.2, without replacement.
*/
@Deprecated
public interface BindParameterSource {
interface BindParameterSource {
/**
* Determine whether there is a value for the specified named parameter.

63
src/main/java/org/springframework/data/r2dbc/core/ConnectionAccessor.java

@ -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;
}

929
src/main/java/org/springframework/data/r2dbc/core/DatabaseClient.java

@ -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);
}
}

1704
src/main/java/org/springframework/data/r2dbc/core/DefaultDatabaseClient.java

File diff suppressed because it is too large Load Diff

187
src/main/java/org/springframework/data/r2dbc/core/DefaultDatabaseClientBuilder.java

@ -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;
}
}

91
src/main/java/org/springframework/data/r2dbc/core/DefaultFetchSpec.java

@ -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);
}
}

33
src/main/java/org/springframework/data/r2dbc/core/DefaultReactiveDataAccessStrategy.java

@ -36,7 +36,6 @@ import org.springframework.data.r2dbc.convert.R2dbcCustomConversions; @@ -36,7 +36,6 @@ import org.springframework.data.r2dbc.convert.R2dbcCustomConversions;
import org.springframework.data.r2dbc.dialect.R2dbcDialect;
import org.springframework.data.r2dbc.mapping.OutboundRow;
import org.springframework.data.r2dbc.mapping.R2dbcMappingContext;
import org.springframework.data.r2dbc.mapping.SettableValue;
import org.springframework.data.r2dbc.query.UpdateMapper;
import org.springframework.data.r2dbc.support.ArrayUtils;
import org.springframework.data.relational.core.dialect.ArrayColumns;
@ -46,6 +45,7 @@ import org.springframework.data.relational.core.mapping.RelationalPersistentProp @@ -46,6 +45,7 @@ import org.springframework.data.relational.core.mapping.RelationalPersistentProp
import org.springframework.data.relational.core.sql.SqlIdentifier;
import org.springframework.lang.Nullable;
import org.springframework.r2dbc.core.Parameter;
import org.springframework.r2dbc.core.PreparedOperation;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
@ -64,7 +64,7 @@ public class DefaultReactiveDataAccessStrategy implements ReactiveDataAccessStra @@ -64,7 +64,7 @@ public class DefaultReactiveDataAccessStrategy implements ReactiveDataAccessStra
private final UpdateMapper updateMapper;
private final MappingContext<RelationalPersistentEntity<?>, ? extends RelationalPersistentProperty> mappingContext;
private final StatementMapper statementMapper;
private final NamedParameterExpander expander;
private final NamedParameterExpander expander = new NamedParameterExpander();
/**
* Creates a new {@link DefaultReactiveDataAccessStrategy} given {@link R2dbcDialect} and optional
@ -115,24 +115,11 @@ public class DefaultReactiveDataAccessStrategy implements ReactiveDataAccessStra @@ -115,24 +115,11 @@ public class DefaultReactiveDataAccessStrategy implements ReactiveDataAccessStra
* @param dialect the {@link R2dbcDialect} to use.
* @param converter must not be {@literal null}.
*/
public DefaultReactiveDataAccessStrategy(R2dbcDialect dialect, R2dbcConverter converter) {
this(dialect, converter, new NamedParameterExpander());
}
/**
* Creates a new {@link DefaultReactiveDataAccessStrategy} given {@link R2dbcDialect} and {@link R2dbcConverter}.
*
* @param dialect the {@link R2dbcDialect} to use.
* @param converter must not be {@literal null}.
* @param expander must not be {@literal null}.
*/
@SuppressWarnings("unchecked")
public DefaultReactiveDataAccessStrategy(R2dbcDialect dialect, R2dbcConverter converter,
NamedParameterExpander expander) {
public DefaultReactiveDataAccessStrategy(R2dbcDialect dialect, R2dbcConverter converter) {
Assert.notNull(dialect, "Dialect must not be null");
Assert.notNull(converter, "RelationalConverter must not be null");
Assert.notNull(expander, "NamedParameterExpander must not be null");
this.converter = converter;
this.updateMapper = new UpdateMapper(dialect, converter);
@ -143,7 +130,6 @@ public class DefaultReactiveDataAccessStrategy implements ReactiveDataAccessStra @@ -143,7 +130,6 @@ public class DefaultReactiveDataAccessStrategy implements ReactiveDataAccessStra
RenderContextFactory factory = new RenderContextFactory(dialect);
this.statementMapper = new DefaultStatementMapper(dialect, factory.createRenderContext(), this.updateMapper,
this.mappingContext);
this.expander = expander;
}
/*
@ -270,15 +256,6 @@ public class DefaultReactiveDataAccessStrategy implements ReactiveDataAccessStra @@ -270,15 +256,6 @@ public class DefaultReactiveDataAccessStrategy implements ReactiveDataAccessStra
actualType);
}
/*
* (non-Javadoc)
* @see org.springframework.data.r2dbc.function.ReactiveDataAccessStrategy#getBindValue(SettableValue)
*/
@Override
public SettableValue getBindValue(SettableValue value) {
return this.updateMapper.getBindValue(value);
}
/*
* (non-Javadoc)
* @see org.springframework.data.r2dbc.function.ReactiveDataAccessStrategy#getBindValue(Parameter)
@ -306,10 +283,10 @@ public class DefaultReactiveDataAccessStrategy implements ReactiveDataAccessStra @@ -306,10 +283,10 @@ public class DefaultReactiveDataAccessStrategy implements ReactiveDataAccessStra
List<String> parameterNames = this.expander.getParameterNames(query);
Map<String, SettableValue> namedBindings = new LinkedHashMap<>(parameterNames.size());
Map<String, Parameter> namedBindings = new LinkedHashMap<>(parameterNames.size());
for (String parameterName : parameterNames) {
SettableValue value = parameterProvider.getParameter(parameterNames.indexOf(parameterName), parameterName);
Parameter value = parameterProvider.getParameter(parameterNames.indexOf(parameterName), parameterName);
if (value == null) {
throw new InvalidDataAccessApiUsageException(

154
src/main/java/org/springframework/data/r2dbc/core/DefaultSqlResult.java

@ -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 {}
}

7
src/main/java/org/springframework/data/r2dbc/core/DefaultStatementMapper.java

@ -20,7 +20,6 @@ import java.util.List; @@ -20,7 +20,6 @@ import java.util.List;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.r2dbc.convert.R2dbcConverter;
import org.springframework.data.r2dbc.dialect.BindTarget;
import org.springframework.data.r2dbc.dialect.R2dbcDialect;
import org.springframework.data.r2dbc.query.BoundAssignments;
import org.springframework.data.r2dbc.query.BoundCondition;
@ -34,7 +33,9 @@ import org.springframework.data.relational.core.sql.InsertBuilder.InsertValuesWi @@ -34,7 +33,9 @@ import org.springframework.data.relational.core.sql.InsertBuilder.InsertValuesWi
import org.springframework.data.relational.core.sql.render.RenderContext;
import org.springframework.data.relational.core.sql.render.SqlRenderer;
import org.springframework.lang.Nullable;
import org.springframework.r2dbc.core.PreparedOperation;
import org.springframework.r2dbc.core.binding.BindMarkers;
import org.springframework.r2dbc.core.binding.BindTarget;
import org.springframework.r2dbc.core.binding.Bindings;
import org.springframework.util.Assert;
@ -349,10 +350,6 @@ class DefaultStatementMapper implements StatementMapper { @@ -349,10 +350,6 @@ class DefaultStatementMapper implements StatementMapper {
this.bindings.apply(to);
}
@Override
public void bindTo(org.springframework.r2dbc.core.binding.BindTarget to) {
this.bindings.apply(to);
}
}
class DefaultTypedStatementMapper<T> implements TypedStatementMapper<T> {

48
src/main/java/org/springframework/data/r2dbc/core/ExecuteFunction.java

@ -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);
}

28
src/main/java/org/springframework/data/r2dbc/core/FetchSpec.java

@ -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 {}

15
src/main/java/org/springframework/data/r2dbc/core/MapBindParameterSource.java

@ -18,13 +18,13 @@ package org.springframework.data.r2dbc.core; @@ -18,13 +18,13 @@ package org.springframework.data.r2dbc.core;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.data.r2dbc.mapping.SettableValue;
import org.springframework.data.util.Streamable;
import org.springframework.r2dbc.core.Parameter;
import org.springframework.util.Assert;
/**
* {@link BindParameterSource} implementation that holds a given {@link Map} of parameters encapsulated as
* {@link SettableValue}.
* {@link Parameter}.
* <p>
* This class is intended for passing in a simple Map of parameter values to the methods of the
* {@link NamedParameterExpander} class.
@ -32,10 +32,9 @@ import org.springframework.util.Assert; @@ -32,10 +32,9 @@ import org.springframework.util.Assert;
* @author Mark Paluch
* @deprecated since 1.2, use Spring's org.springframework.r2dbc.core.MapBindParameterSource support instead.
*/
@Deprecated
class MapBindParameterSource implements BindParameterSource {
private final Map<String, SettableValue> values;
private final Map<String, Parameter> values;
/**
* Creates a new empty {@link MapBindParameterSource}.
@ -45,11 +44,11 @@ class MapBindParameterSource implements BindParameterSource { @@ -45,11 +44,11 @@ class MapBindParameterSource implements BindParameterSource {
}
/**
* Creates a new {@link MapBindParameterSource} given {@link Map} of {@link SettableValue}.
* Creates a new {@link MapBindParameterSource} given {@link Map} of {@link Parameter}.
*
* @param values the parameter mapping.
*/
MapBindParameterSource(Map<String, SettableValue> values) {
MapBindParameterSource(Map<String, Parameter> values) {
Assert.notNull(values, "Values must not be null");
@ -68,7 +67,7 @@ class MapBindParameterSource implements BindParameterSource { @@ -68,7 +67,7 @@ class MapBindParameterSource implements BindParameterSource {
Assert.notNull(paramName, "Parameter name must not be null!");
Assert.notNull(value, "Value must not be null!");
this.values.put(paramName, SettableValue.fromOrEmpty(value, value.getClass()));
this.values.put(paramName, Parameter.fromOrEmpty(value, value.getClass()));
return this;
}
@ -93,7 +92,7 @@ class MapBindParameterSource implements BindParameterSource { @@ -93,7 +92,7 @@ class MapBindParameterSource implements BindParameterSource {
Assert.notNull(paramName, "Parameter name must not be null!");
SettableValue settableValue = this.values.get(paramName);
Parameter settableValue = this.values.get(paramName);
if (settableValue != null) {
return settableValue.getType();
}

10
src/main/java/org/springframework/data/r2dbc/core/NamedParameterExpander.java

@ -22,7 +22,8 @@ import java.util.Map; @@ -22,7 +22,8 @@ import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.r2dbc.dialect.BindMarkersFactory;
import org.springframework.r2dbc.core.PreparedOperation;
import org.springframework.r2dbc.core.binding.BindMarkersFactory;
/**
* SQL translation support allowing the use of named parameters rather than native placeholders.
@ -39,7 +40,7 @@ import org.springframework.data.r2dbc.dialect.BindMarkersFactory; @@ -39,7 +40,7 @@ import org.springframework.data.r2dbc.dialect.BindMarkersFactory;
* @deprecated since 1.2, without replacement.
*/
@Deprecated
public class NamedParameterExpander {
class NamedParameterExpander {
/**
* Default maximum number of entries for the SQL cache: 256.
@ -126,11 +127,6 @@ public class NamedParameterExpander { @@ -126,11 +127,6 @@ public class NamedParameterExpander {
*/
public PreparedOperation<String> expand(String sql, BindMarkersFactory bindMarkersFactory,
BindParameterSource paramSource) {
return expand(sql, (org.springframework.r2dbc.core.binding.BindMarkersFactory) bindMarkersFactory, paramSource);
}
PreparedOperation<String> expand(String sql,
org.springframework.r2dbc.core.binding.BindMarkersFactory bindMarkersFactory, BindParameterSource paramSource) {
ParsedSql parsedSql = getParsedSql(sql);

9
src/main/java/org/springframework/data/r2dbc/core/NamedParameterUtils.java

@ -26,10 +26,11 @@ import java.util.Set; @@ -26,10 +26,11 @@ import java.util.Set;
import java.util.TreeMap;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.r2dbc.dialect.BindTarget;
import org.springframework.r2dbc.core.PreparedOperation;
import org.springframework.r2dbc.core.binding.BindMarker;
import org.springframework.r2dbc.core.binding.BindMarkers;
import org.springframework.r2dbc.core.binding.BindMarkersFactory;
import org.springframework.r2dbc.core.binding.BindTarget;
import org.springframework.util.Assert;
/**
@ -581,13 +582,9 @@ abstract class NamedParameterUtils { @@ -581,13 +582,9 @@ abstract class NamedParameterUtils {
return this.expandedSql;
}
@Override
public void bindTo(BindTarget target) {
bindTo((org.springframework.r2dbc.core.binding.BindTarget) target);
}
@Override
public void bindTo(org.springframework.r2dbc.core.binding.BindTarget target) {
public void bindTo(BindTarget target) {
for (String namedParameter : this.parameterSource.getParameterNames()) {

54
src/main/java/org/springframework/data/r2dbc/core/PreparedOperation.java

@ -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();
}
}

44
src/main/java/org/springframework/data/r2dbc/core/QueryOperation.java

@ -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();
}
}

183
src/main/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplate.java

@ -15,7 +15,6 @@ @@ -15,7 +15,6 @@
*/
package org.springframework.data.r2dbc.core;
import io.r2dbc.spi.Connection;
import io.r2dbc.spi.ConnectionFactory;
import io.r2dbc.spi.Row;
import io.r2dbc.spi.RowMetadata;
@ -29,7 +28,6 @@ import java.util.Map; @@ -29,7 +28,6 @@ import java.util.Map;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.reactivestreams.Publisher;
@ -72,11 +70,9 @@ import org.springframework.data.relational.core.sql.Table; @@ -72,11 +70,9 @@ import org.springframework.data.relational.core.sql.Table;
import org.springframework.data.util.ProxyUtils;
import org.springframework.lang.Nullable;
import org.springframework.r2dbc.core.DatabaseClient;
import org.springframework.r2dbc.core.FetchSpec;
import org.springframework.r2dbc.core.Parameter;
import org.springframework.r2dbc.core.PreparedOperation;
import org.springframework.r2dbc.core.RowsFetchSpec;
import org.springframework.r2dbc.core.StatementFilterFunction;
import org.springframework.util.Assert;
/**
@ -131,21 +127,10 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw @@ -131,21 +127,10 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
* @param dialect the dialect to use, must not be {@literal null}.
* @since 1.2
*/
public R2dbcEntityTemplate(org.springframework.r2dbc.core.DatabaseClient databaseClient, R2dbcDialect dialect) {
public R2dbcEntityTemplate(DatabaseClient databaseClient, R2dbcDialect dialect) {
this(databaseClient, new DefaultReactiveDataAccessStrategy(dialect));
}
/**
* Create a new {@link R2dbcEntityTemplate} given {@link DatabaseClient}.
*
* @param databaseClient must not be {@literal null}.
* @deprecated since 1.2, use {@link #R2dbcEntityTemplate(DatabaseClient, R2dbcDialect)} instead.
*/
@Deprecated
public R2dbcEntityTemplate(org.springframework.data.r2dbc.core.DatabaseClient databaseClient) {
this(databaseClient, getDataAccessStrategy(databaseClient));
}
/**
* Create a new {@link R2dbcEntityTemplate} given {@link DatabaseClient}, {@link R2dbcDialect} and
* {@link R2dbcConverter}.
@ -155,8 +140,7 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw @@ -155,8 +140,7 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
* @param converter the dialect to use, must not be {@literal null}.
* @since 1.2
*/
public R2dbcEntityTemplate(org.springframework.r2dbc.core.DatabaseClient databaseClient, R2dbcDialect dialect,
R2dbcConverter converter) {
public R2dbcEntityTemplate(DatabaseClient databaseClient, R2dbcDialect dialect, R2dbcConverter converter) {
this(databaseClient, new DefaultReactiveDataAccessStrategy(dialect, converter));
}
@ -166,7 +150,7 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw @@ -166,7 +150,7 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
* @param databaseClient must not be {@literal null}.
* @since 1.2
*/
public R2dbcEntityTemplate(org.springframework.r2dbc.core.DatabaseClient databaseClient,
public R2dbcEntityTemplate(DatabaseClient databaseClient,
ReactiveDataAccessStrategy strategy) {
Assert.notNull(databaseClient, "DatabaseClient must not be null");
@ -178,18 +162,6 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw @@ -178,18 +162,6 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
this.projectionFactory = new SpelAwareProxyProjectionFactory();
}
/**
* Create a new {@link R2dbcEntityTemplate} given {@link DatabaseClient} and {@link ReactiveDataAccessStrategy}.
*
* @param databaseClient must not be {@literal null}.
* @deprecated since 1.2, use {@link #R2dbcEntityTemplate(DatabaseClient, ReactiveDataAccessStrategy)} instead.
*/
@Deprecated
public R2dbcEntityTemplate(org.springframework.data.r2dbc.core.DatabaseClient databaseClient,
ReactiveDataAccessStrategy strategy) {
this(new DatabaseClientAdapter(databaseClient), strategy);
}
/*
* (non-Javadoc)
* @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#getDatabaseClient()
@ -641,11 +613,9 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw @@ -641,11 +613,9 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
}
return statement.returnGeneratedValues(dataAccessStrategy.renderForGeneratedValues(identifierColumns.get(0)));
})
.map(this.dataAccessStrategy.getConverter().populateIdIfNecessary(entity)) //
}).map(this.dataAccessStrategy.getConverter().populateIdIfNecessary(entity)) //
.all() //
.last(entity)
.flatMap(saved -> maybeCallAfterSave(saved, outboundRow, tableName));
.last(entity).flatMap(saved -> maybeCallAfterSave(saved, outboundRow, tableName));
}
@SuppressWarnings("unchecked")
@ -914,149 +884,6 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw @@ -914,149 +884,6 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
return executeSpec.map(rowMapper);
}
private static ReactiveDataAccessStrategy getDataAccessStrategy(
org.springframework.data.r2dbc.core.DatabaseClient databaseClient) {
Assert.notNull(databaseClient, "DatabaseClient must not be null");
if (databaseClient instanceof DefaultDatabaseClient) {
DefaultDatabaseClient client = (DefaultDatabaseClient) databaseClient;
return client.getDataAccessStrategy();
}
throw new IllegalStateException("Cannot obtain ReactiveDataAccessStrategy");
}
/**
* Adapter to adapt our deprecated {@link org.springframework.data.r2dbc.core.DatabaseClient} into Spring R2DBC
* {@link DatabaseClient}.
*/
private static class DatabaseClientAdapter implements DatabaseClient {
private final org.springframework.data.r2dbc.core.DatabaseClient delegate;
private DatabaseClientAdapter(org.springframework.data.r2dbc.core.DatabaseClient delegate) {
Assert.notNull(delegate, "DatabaseClient must not be null");
this.delegate = delegate;
}
@Override
public ConnectionFactory getConnectionFactory() {
return delegate.getConnectionFactory();
}
@Override
public GenericExecuteSpec sql(String sql) {
return new GenericExecuteSpecAdapter(delegate.execute(sql));
}
@Override
public GenericExecuteSpec sql(Supplier<String> sqlSupplier) {
return new GenericExecuteSpecAdapter(delegate.execute(sqlSupplier));
}
@Override
public <T> Mono<T> inConnection(Function<Connection, Mono<T>> action) throws DataAccessException {
return ((ConnectionAccessor) delegate).inConnection(action);
}
@Override
public <T> Flux<T> inConnectionMany(Function<Connection, Flux<T>> action) throws DataAccessException {
return ((ConnectionAccessor) delegate).inConnectionMany(action);
}
static class GenericExecuteSpecAdapter implements GenericExecuteSpec {
private final org.springframework.data.r2dbc.core.DatabaseClient.GenericExecuteSpec delegate;
public GenericExecuteSpecAdapter(org.springframework.data.r2dbc.core.DatabaseClient.GenericExecuteSpec delegate) {
this.delegate = delegate;
}
@Override
public GenericExecuteSpec bind(int index, Object value) {
return new GenericExecuteSpecAdapter(delegate.bind(index, value));
}
@Override
public GenericExecuteSpec bindNull(int index, Class<?> type) {
return new GenericExecuteSpecAdapter(delegate.bindNull(index, type));
}
@Override
public GenericExecuteSpec bind(String name, Object value) {
return new GenericExecuteSpecAdapter(delegate.bind(name, value));
}
@Override
public GenericExecuteSpec bindNull(String name, Class<?> type) {
return new GenericExecuteSpecAdapter(delegate.bindNull(name, type));
}
@Override
public GenericExecuteSpec filter(StatementFilterFunction filter) {
return new GenericExecuteSpecAdapter(delegate.filter(filter::filter));
}
@Override
public <R> RowsFetchSpec<R> map(BiFunction<Row, RowMetadata, R> mappingFunction) {
return new RowFetchSpecAdapter<>(delegate.map(mappingFunction));
}
@Override
public FetchSpec<Map<String, Object>> fetch() {
return new FetchSpecAdapter<>(delegate.fetch());
}
@Override
public Mono<Void> then() {
return delegate.then();
}
}
private static class RowFetchSpecAdapter<T> implements RowsFetchSpec<T> {
private final org.springframework.data.r2dbc.core.RowsFetchSpec<T> delegate;
RowFetchSpecAdapter(org.springframework.data.r2dbc.core.RowsFetchSpec<T> delegate) {
this.delegate = delegate;
}
@Override
public Mono<T> one() {
return delegate.one();
}
@Override
public Mono<T> first() {
return delegate.first();
}
@Override
public Flux<T> all() {
return delegate.all();
}
}
private static class FetchSpecAdapter<T> extends RowFetchSpecAdapter<T> implements FetchSpec<T> {
private final org.springframework.data.r2dbc.core.FetchSpec<T> delegate;
FetchSpecAdapter(org.springframework.data.r2dbc.core.FetchSpec<T> delegate) {
super(delegate);
this.delegate = delegate;
}
@Override
public Mono<Integer> rowsUpdated() {
return delegate.rowsUpdated();
}
}
}
/**
* {@link RowsFetchSpec} adapter emitting values from {@link Optional} if they exist.
*

20
src/main/java/org/springframework/data/r2dbc/core/ReactiveDataAccessStrategy.java

@ -23,7 +23,6 @@ import java.util.function.BiFunction; @@ -23,7 +23,6 @@ import java.util.function.BiFunction;
import org.springframework.data.r2dbc.convert.R2dbcConverter;
import org.springframework.data.r2dbc.mapping.OutboundRow;
import org.springframework.data.r2dbc.mapping.SettableValue;
import org.springframework.data.relational.core.sql.IdentifierProcessing;
import org.springframework.data.relational.core.sql.SqlIdentifier;
import org.springframework.lang.Nullable;
@ -33,8 +32,8 @@ import org.springframework.util.Assert; @@ -33,8 +32,8 @@ import org.springframework.util.Assert;
/**
* Data access strategy that generalizes convenience operations using mapped entities. Typically used internally by
* {@link DatabaseClient} and repository support. SQL creation is limited to single-table operations and single-column
* primary keys.
* {@link R2dbcEntityOperations} and repository support. SQL creation is limited to single-table operations and
* single-column primary keys.
*
* @author Mark Paluch
* @author Jens Schauder
@ -65,17 +64,6 @@ public interface ReactiveDataAccessStrategy { @@ -65,17 +64,6 @@ public interface ReactiveDataAccessStrategy {
*/
OutboundRow getOutboundRow(Object object);
/**
* Return a potentially converted {@link SettableValue} for strategies that support type conversion.
*
* @param value must not be {@literal null}.
* @return
* @since 1.1
* @deprecated since 1.2, use {@link #getBindValue(Parameter)} instead.
*/
@Deprecated
SettableValue getBindValue(SettableValue value);
/**
* Return a potentially converted {@link Parameter} for strategies that support type conversion.
*
@ -159,7 +147,7 @@ public interface ReactiveDataAccessStrategy { @@ -159,7 +147,7 @@ public interface ReactiveDataAccessStrategy {
interface NamedParameterProvider {
/**
* Returns the {@link SettableValue value} for a parameter identified either by name or by index.
* Returns the {@link Parameter value} for a parameter identified either by name or by index.
*
* @param index parameter index according the parameter discovery order.
* @param name name of the parameter.
@ -167,7 +155,7 @@ public interface ReactiveDataAccessStrategy { @@ -167,7 +155,7 @@ public interface ReactiveDataAccessStrategy {
* {@link org.springframework.dao.InvalidDataAccessApiUsageException} in named parameter processing.
*/
@Nullable
SettableValue getParameter(int index, String name);
Parameter getParameter(int index, String name);
}
}

52
src/main/java/org/springframework/data/r2dbc/core/RowsFetchSpec.java

@ -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();
}

40
src/main/java/org/springframework/data/r2dbc/core/SqlProvider.java

@ -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();
}

66
src/main/java/org/springframework/data/r2dbc/core/StatementFilterFunction.java

@ -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));
}
}

48
src/main/java/org/springframework/data/r2dbc/core/StatementFilterFunctions.java

@ -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;
}
}

27
src/main/java/org/springframework/data/r2dbc/core/StatementMapper.java

@ -30,7 +30,6 @@ import org.springframework.data.domain.Pageable; @@ -30,7 +30,6 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.r2dbc.convert.R2dbcConverter;
import org.springframework.data.r2dbc.dialect.R2dbcDialect;
import org.springframework.data.r2dbc.mapping.SettableValue;
import org.springframework.data.relational.core.query.Criteria;
import org.springframework.data.relational.core.query.CriteriaDefinition;
import org.springframework.data.relational.core.sql.Expression;
@ -477,19 +476,6 @@ public interface StatementMapper { @@ -477,19 +476,6 @@ public interface StatementMapper {
return new InsertSpec(table, Collections.emptyMap());
}
/**
* Associate a column with a {@link SettableValue} and create a new {@link InsertSpec}.
*
* @param column
* @param value
* @return the {@link InsertSpec}.
* @deprecated since 1.2, use {@link #withColumn(String, Parameter)} instead.
*/
@Deprecated
public InsertSpec withColumn(String column, SettableValue value) {
return withColumn(SqlIdentifier.unquoted(column), value);
}
/**
* Associate a column with a {@link Parameter} and create a new {@link InsertSpec}.
*
@ -502,19 +488,6 @@ public interface StatementMapper { @@ -502,19 +488,6 @@ public interface StatementMapper {
return withColumn(SqlIdentifier.unquoted(column), value);
}
/**
* Associate a column with a {@link SettableValue} and create a new {@link InsertSpec}.
*
* @param column
* @param value
* @return the {@link InsertSpec}.
* @deprecated since 1.2, use {@link #withColumn(SqlIdentifier, Parameter)} instead.
*/
@Deprecated
public InsertSpec withColumn(SqlIdentifier column, SettableValue value) {
return withColumn(column, value.toParameter());
}
/**
* Associate a column with a {@link Parameter} and create a new {@link InsertSpec}.
*

35
src/main/java/org/springframework/data/r2dbc/core/UpdatedRowsFetchSpec.java

@ -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();
}

44
src/main/java/org/springframework/data/r2dbc/dialect/BindMarker.java

@ -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);
}

39
src/main/java/org/springframework/data/r2dbc/dialect/BindMarkers.java

@ -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();
}
}

85
src/main/java/org/springframework/data/r2dbc/dialect/BindMarkersAdapter.java

@ -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();
}
}
}

165
src/main/java/org/springframework/data/r2dbc/dialect/BindMarkersFactory.java

@ -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();
}
};
}
}

64
src/main/java/org/springframework/data/r2dbc/dialect/BindTarget.java

@ -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);
}

289
src/main/java/org/springframework/data/r2dbc/dialect/Bindings.java

@ -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());
}
}
}

135
src/main/java/org/springframework/data/r2dbc/dialect/MutableBindings.java

@ -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;
}
}

154
src/main/java/org/springframework/data/r2dbc/mapping/SettableValue.java

@ -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());
}
}

705
src/main/java/org/springframework/data/r2dbc/query/Criteria.java

@ -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);
}
}
}

38
src/main/java/org/springframework/data/r2dbc/query/QueryMapper.java

@ -30,10 +30,10 @@ import org.springframework.data.mapping.PropertyReferenceException; @@ -30,10 +30,10 @@ import org.springframework.data.mapping.PropertyReferenceException;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.r2dbc.convert.R2dbcConverter;
import org.springframework.data.r2dbc.dialect.R2dbcDialect;
import org.springframework.data.r2dbc.mapping.SettableValue;
import org.springframework.data.relational.core.dialect.Escaper;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.core.query.Criteria;
import org.springframework.data.relational.core.query.CriteriaDefinition;
import org.springframework.data.relational.core.query.CriteriaDefinition.Comparator;
import org.springframework.data.relational.core.query.ValueFunction;
@ -347,13 +347,7 @@ public class QueryMapper { @@ -347,13 +347,7 @@ public class QueryMapper {
Object mappedValue;
Class<?> typeHint;
if (criteria.getValue() instanceof SettableValue) {
SettableValue settableValue = (SettableValue) criteria.getValue();
mappedValue = convertValue(settableValue.getValue(), propertyField.getTypeHint());
typeHint = getTypeHint(mappedValue, actualType.getType(), settableValue);
} else if (criteria.getValue() instanceof Parameter) {
if (criteria.getValue() instanceof Parameter) {
Parameter parameter = (Parameter) criteria.getValue();
@ -384,21 +378,6 @@ public class QueryMapper { @@ -384,21 +378,6 @@ public class QueryMapper {
return Escaper.DEFAULT;
}
/**
* Potentially convert the {@link SettableValue}.
*
* @param value
* @return
*/
public SettableValue getBindValue(SettableValue value) {
if (value.isEmpty()) {
return SettableValue.empty(converter.getTargetType(value.getType()));
}
return SettableValue.from(convertValue(value.getValue(), ClassTypeInformation.OBJECT));
}
/**
* Potentially convert the {@link Parameter}.
*
@ -587,19 +566,6 @@ public class QueryMapper { @@ -587,19 +566,6 @@ public class QueryMapper {
return propertyType;
}
Class<?> getTypeHint(@Nullable Object mappedValue, Class<?> propertyType, SettableValue settableValue) {
if (mappedValue == null || propertyType.equals(Object.class)) {
return settableValue.getType();
}
if (mappedValue.getClass().equals(settableValue.getValue().getClass())) {
return settableValue.getType();
}
return propertyType;
}
Class<?> getTypeHint(@Nullable Object mappedValue, Class<?> propertyType, Parameter parameter) {
if (mappedValue == null || propertyType.equals(Object.class)) {

99
src/main/java/org/springframework/data/r2dbc/query/Update.java

@ -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);
}
}

40
src/main/java/org/springframework/data/r2dbc/query/UpdateMapper.java

@ -20,14 +20,10 @@ import java.util.List; @@ -20,14 +20,10 @@ import java.util.List;
import java.util.Map;
import org.springframework.data.r2dbc.convert.R2dbcConverter;
import org.springframework.r2dbc.core.Parameter;
import org.springframework.r2dbc.core.binding.BindMarkers;
import org.springframework.r2dbc.core.binding.Bindings;
import org.springframework.r2dbc.core.binding.MutableBindings;
import org.springframework.data.r2dbc.dialect.R2dbcDialect;
import org.springframework.data.r2dbc.mapping.SettableValue;
import org.springframework.data.relational.core.dialect.Escaper;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.query.Update;
import org.springframework.data.relational.core.query.ValueFunction;
import org.springframework.data.relational.core.sql.AssignValue;
import org.springframework.data.relational.core.sql.Assignment;
@ -38,7 +34,11 @@ import org.springframework.data.relational.core.sql.SqlIdentifier; @@ -38,7 +34,11 @@ import org.springframework.data.relational.core.sql.SqlIdentifier;
import org.springframework.data.relational.core.sql.Table;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
import org.springframework.r2dbc.core.Parameter;
import org.springframework.r2dbc.core.binding.BindMarker;
import org.springframework.r2dbc.core.binding.BindMarkers;
import org.springframework.r2dbc.core.binding.Bindings;
import org.springframework.r2dbc.core.binding.MutableBindings;
import org.springframework.util.Assert;
/**
@ -58,24 +58,6 @@ public class UpdateMapper extends QueryMapper { @@ -58,24 +58,6 @@ public class UpdateMapper extends QueryMapper {
super(dialect, converter);
}
/**
* Map a {@link Update} object to {@link BoundAssignments} and consider value/{@code NULL} {@link Bindings}.
*
* @param markers bind markers object, must not be {@literal null}.
* @param update update definition to map, must not be {@literal null}.
* @param table must not be {@literal null}.
* @param entity related {@link RelationalPersistentEntity}, can be {@literal null}.
* @return the mapped {@link BoundAssignments}.
* @deprecated since 1.1, use
* {@link #getMappedObject(BindMarkers, org.springframework.data.relational.core.query.Update, Table, RelationalPersistentEntity)}
* instead.
*/
@Deprecated
public BoundAssignments getMappedObject(BindMarkers markers, Update update, Table table,
@Nullable RelationalPersistentEntity<?> entity) {
return getMappedObject(markers, update.getAssignments(), table, entity);
}
/**
* Map a {@link org.springframework.data.relational.core.query.Update} object to {@link BoundAssignments} and consider
* value/{@code NULL} {@link Bindings}.
@ -87,8 +69,7 @@ public class UpdateMapper extends QueryMapper { @@ -87,8 +69,7 @@ public class UpdateMapper extends QueryMapper {
* @return the mapped {@link BoundAssignments}.
* @since 1.1
*/
public BoundAssignments getMappedObject(BindMarkers markers,
org.springframework.data.relational.core.query.Update update, Table table,
public BoundAssignments getMappedObject(BindMarkers markers, Update update, Table table,
@Nullable RelationalPersistentEntity<?> entity) {
return getMappedObject(markers, update.getAssignments(), table, entity);
}
@ -130,14 +111,7 @@ public class UpdateMapper extends QueryMapper { @@ -130,14 +111,7 @@ public class UpdateMapper extends QueryMapper {
Object mappedValue;
Class<?> typeHint;
if (value instanceof SettableValue) {
SettableValue settableValue = (SettableValue) value;
mappedValue = convertValue(settableValue.getValue(), propertyField.getTypeHint());
typeHint = getTypeHint(mappedValue, actualType.getType(), settableValue);
} else if (value instanceof Parameter) {
if (value instanceof Parameter) {
Parameter parameter = (Parameter) value;

15
src/main/java/org/springframework/data/r2dbc/repository/config/EnableR2dbcRepositories.java

@ -116,22 +116,9 @@ public @interface EnableR2dbcRepositories { @@ -116,22 +116,9 @@ public @interface EnableR2dbcRepositories {
*/
Class<?> repositoryBaseClass() default DefaultRepositoryBaseClass.class;
/**
* Configures the name of the {@link org.springframework.data.r2dbc.core.DatabaseClient} bean to be used with the
* repositories detected.
*
* @return
* @see #entityOperationsRef()
* @deprecated since 1.2, in favor of {@link #entityOperationsRef()}.
*/
@Deprecated
String databaseClientRef() default "";
/**
* Configures the name of the {@link org.springframework.data.r2dbc.core.R2dbcEntityOperations} bean to be used with
* the repositories detected. Used as alternative to {@link #databaseClientRef()} to configure an access strategy when
* using repositories with different database systems/dialects. If this attribute is set, then
* {@link #databaseClientRef()} is ignored.
* the repositories detected.
*
* @return
* @since 1.1.3

9
src/main/java/org/springframework/data/r2dbc/repository/config/R2dbcRepositoryConfigurationExtension.java

@ -29,7 +29,6 @@ import org.springframework.data.repository.config.RepositoryConfigurationExtensi @@ -29,7 +29,6 @@ import org.springframework.data.repository.config.RepositoryConfigurationExtensi
import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport;
import org.springframework.data.repository.config.XmlRepositoryConfigurationSource;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.util.StringUtils;
/**
* Reactive {@link RepositoryConfigurationExtension} for R2DBC.
@ -98,13 +97,7 @@ public class R2dbcRepositoryConfigurationExtension extends RepositoryConfigurati @@ -98,13 +97,7 @@ public class R2dbcRepositoryConfigurationExtension extends RepositoryConfigurati
AnnotationAttributes attributes = config.getAttributes();
String databaseClientRef = attributes.getString("databaseClientRef");
if (StringUtils.hasText(databaseClientRef)) {
builder.addPropertyReference("databaseClient", attributes.getString("databaseClientRef"));
builder.addPropertyReference("dataAccessStrategy", "reactiveDataAccessStrategy");
} else {
builder.addPropertyReference("entityOperations", attributes.getString("entityOperationsRef"));
}
builder.addPropertyReference("entityOperations", attributes.getString("entityOperationsRef"));
}
/*

2
src/main/java/org/springframework/data/r2dbc/repository/query/ExpressionEvaluatingParameterBinder.java

@ -128,8 +128,6 @@ class ExpressionEvaluatingParameterBinder { @@ -128,8 +128,6 @@ class ExpressionEvaluatingParameterBinder {
return dataAccessStrategy.getBindValue(parameter);
}
private org.springframework.r2dbc.core.Parameter getBindValue(org.springframework.r2dbc.core.Parameter bindValue) {
return dataAccessStrategy.getBindValue(bindValue);
}

10
src/main/java/org/springframework/data/r2dbc/repository/query/StringBasedR2dbcQuery.java

@ -28,7 +28,6 @@ import org.springframework.data.r2dbc.convert.R2dbcConverter; @@ -28,7 +28,6 @@ import org.springframework.data.r2dbc.convert.R2dbcConverter;
import org.springframework.data.r2dbc.core.R2dbcEntityOperations;
import org.springframework.data.r2dbc.core.ReactiveDataAccessStrategy;
import org.springframework.data.r2dbc.dialect.BindTargetBinder;
import org.springframework.data.r2dbc.mapping.SettableValue;
import org.springframework.data.r2dbc.repository.Query;
import org.springframework.data.relational.repository.query.RelationalParameterAccessor;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
@ -202,12 +201,12 @@ public class StringBasedR2dbcQuery extends AbstractR2dbcQuery { @@ -202,12 +201,12 @@ public class StringBasedR2dbcQuery extends AbstractR2dbcQuery {
if (recordedBindings.byName.containsKey(name)) {
remainderByName.remove(name);
return SettableValue.fromParameter(recordedBindings.byName.get(name));
return recordedBindings.byName.get(name);
}
if (recordedBindings.byIndex.containsKey(index)) {
remainderByIndex.remove(index);
return SettableValue.fromParameter(recordedBindings.byIndex.get(index));
return recordedBindings.byIndex.get(index);
}
return null;
@ -253,11 +252,6 @@ public class StringBasedR2dbcQuery extends AbstractR2dbcQuery { @@ -253,11 +252,6 @@ public class StringBasedR2dbcQuery extends AbstractR2dbcQuery {
@NotNull
private Parameter toParameter(Object value) {
if (value instanceof SettableValue) {
return ((SettableValue) value).toParameter();
}
return value instanceof Parameter ? (Parameter) value : Parameter.from(value);
}

103
src/main/java/org/springframework/data/r2dbc/repository/support/BindSpecAdapter.java

@ -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;
}
}

23
src/main/java/org/springframework/data/r2dbc/repository/support/SimpleR2dbcRepository.java

@ -105,29 +105,6 @@ public class SimpleR2dbcRepository<T, ID> implements R2dbcRepository<T, ID> { @@ -105,29 +105,6 @@ public class SimpleR2dbcRepository<T, ID> implements R2dbcRepository<T, ID> {
this.exampleMapper = new RelationalExampleMapper(converter.getMappingContext());
}
/**
* Create a new {@link SimpleR2dbcRepository}.
*
* @param entity
* @param databaseClient
* @param converter
* @param accessStrategy
* @deprecated since 1.2.
*/
@Deprecated
public SimpleR2dbcRepository(RelationalEntityInformation<T, ID> entity,
org.springframework.data.r2dbc.core.DatabaseClient databaseClient, R2dbcConverter converter,
ReactiveDataAccessStrategy accessStrategy) {
this.entity = entity;
this.entityOperations = new R2dbcEntityTemplate(databaseClient, accessStrategy);
this.idProperty = Lazy.of(() -> converter //
.getMappingContext() //
.getRequiredPersistentEntity(this.entity.getJavaType()) //
.getRequiredIdProperty());
this.exampleMapper = new RelationalExampleMapper(converter.getMappingContext());
}
// -------------------------------------------------------------------------
// Methods from ReactiveCrudRepository
// -------------------------------------------------------------------------

125
src/main/java/org/springframework/data/r2dbc/support/AbstractFallbackR2dbcExceptionTranslator.java

@ -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();
}
}

95
src/main/java/org/springframework/data/r2dbc/support/R2dbcExceptionSubclassTranslator.java

@ -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;
}
}

61
src/main/java/org/springframework/data/r2dbc/support/R2dbcExceptionTranslator.java

@ -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);
}

277
src/main/java/org/springframework/data/r2dbc/support/SqlErrorCodeR2dbcExceptionTranslator.java

@ -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 + "]");
}
}
}

148
src/main/java/org/springframework/data/r2dbc/support/SqlStateR2dbcExceptionTranslator.java

@ -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;
}
}

49
src/main/kotlin/org/springframework/data/r2dbc/core/CriteriaStepExtensions.kt

@ -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)

169
src/main/kotlin/org/springframework/data/r2dbc/core/DatabaseClientExtensions.kt

@ -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)

62
src/main/kotlin/org/springframework/data/r2dbc/core/RowsFetchSpecExtensions.kt

@ -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()

26
src/main/kotlin/org/springframework/data/r2dbc/core/UpdatedRowsFetchSpecExtensions.kt

@ -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()

60
src/test/java/org/springframework/data/r2dbc/connectionfactory/DelegatingConnectionFactoryUnitTests.java

@ -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);
}
}
}

121
src/test/java/org/springframework/data/r2dbc/connectionfactory/SingleConnectionConnectionFactoryUnitTests.java

@ -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();
}
}

163
src/test/java/org/springframework/data/r2dbc/connectionfactory/TransactionAwareConnectionFactoryProxyUnitTests.java

@ -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();
}
}

134
src/test/java/org/springframework/data/r2dbc/connectionfactory/init/AbstractDatabaseInitializationTests.java

@ -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();
}
}
}

112
src/test/java/org/springframework/data/r2dbc/connectionfactory/init/CompositeDatabasePopulatorTests.java

@ -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);
}
}

70
src/test/java/org/springframework/data/r2dbc/connectionfactory/init/ConnectionFactoryInitializerUnitTests.java

@ -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();
}
}

57
src/test/java/org/springframework/data/r2dbc/connectionfactory/init/H2DatabasePopulatorIntegrationTests.java

@ -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");
}
}

110
src/test/java/org/springframework/data/r2dbc/connectionfactory/init/ResourceDatabasePopulatorUnitTests.java

@ -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);
}
}

205
src/test/java/org/springframework/data/r2dbc/connectionfactory/init/ScriptUtilsUnitTests.java

@ -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();
}
}

190
src/test/java/org/springframework/data/r2dbc/connectionfactory/lookup/AbstractRoutingConnectionFactoryUnitTests.java

@ -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));
}
}
}

81
src/test/java/org/springframework/data/r2dbc/connectionfactory/lookup/BeanFactoryConnectionFactoryLookupUnitTests.java

@ -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);
}
}

42
src/test/java/org/springframework/data/r2dbc/connectionfactory/lookup/DummyConnectionFactory.java

@ -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();
}
}

102
src/test/java/org/springframework/data/r2dbc/connectionfactory/lookup/MapConnectionFactoryLookupUnitTests.java

@ -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);
}
}

527
src/test/java/org/springframework/data/r2dbc/core/AbstractDatabaseClientIntegrationTests.java

@ -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…
Cancel
Save