Align with R2DBC 0.9 API changes
Adopt to R2DBC Parameter type, deprecate our own one in favor of the R2DBC type. Add support for extensible transaction definitions and support to consume Readable and result segments. Return Long instead of Integer in DatabaseClient update in preparation for R2DBC 1.0 changes.
This commit is contained in:
parent
fd34533a3e
commit
a3781a45d6
|
|
@ -346,7 +346,7 @@ configure([rootProject] + javaProjects) { project ->
|
||||||
// "https://junit.org/junit5/docs/5.8.2/api/",
|
// "https://junit.org/junit5/docs/5.8.2/api/",
|
||||||
"https://www.reactive-streams.org/reactive-streams-1.0.3-javadoc/",
|
"https://www.reactive-streams.org/reactive-streams-1.0.3-javadoc/",
|
||||||
"https://javadoc.io/static/io.rsocket/rsocket-core/1.1.1/",
|
"https://javadoc.io/static/io.rsocket/rsocket-core/1.1.1/",
|
||||||
"https://r2dbc.io/spec/0.8.5.RELEASE/api/",
|
"https://r2dbc.io/spec/0.9.1.RELEASE/api/",
|
||||||
// The external Javadoc link for JSR 305 must come last to ensure that types from
|
// The external Javadoc link for JSR 305 must come last to ensure that types from
|
||||||
// JSR 250 (such as @PostConstruct) are still supported. This is due to the fact
|
// JSR 250 (such as @PostConstruct) are still supported. This is due to the fact
|
||||||
// that JSR 250 and JSR 305 both define types in javax.annotation, which results
|
// that JSR 250 and JSR 305 both define types in javax.annotation, which results
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,10 @@ import java.time.Duration;
|
||||||
import io.r2dbc.spi.Connection;
|
import io.r2dbc.spi.Connection;
|
||||||
import io.r2dbc.spi.ConnectionFactory;
|
import io.r2dbc.spi.ConnectionFactory;
|
||||||
import io.r2dbc.spi.IsolationLevel;
|
import io.r2dbc.spi.IsolationLevel;
|
||||||
|
import io.r2dbc.spi.Option;
|
||||||
import io.r2dbc.spi.R2dbcException;
|
import io.r2dbc.spi.R2dbcException;
|
||||||
import io.r2dbc.spi.Result;
|
import io.r2dbc.spi.Result;
|
||||||
|
import org.reactivestreams.Publisher;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
|
@ -47,7 +49,7 @@ import org.springframework.util.Assert;
|
||||||
* <p><b>Note: The {@code ConnectionFactory} that this transaction manager
|
* <p><b>Note: The {@code ConnectionFactory} that this transaction manager
|
||||||
* operates on needs to return independent {@code Connection}s.</b>
|
* operates on needs to return independent {@code Connection}s.</b>
|
||||||
* The {@code Connection}s may come from a pool (the typical case), but the
|
* The {@code Connection}s may come from a pool (the typical case), but the
|
||||||
* {@code ConnectionFactory} must not return scoped scoped {@code Connection}s
|
* {@code ConnectionFactory} must not return scoped {@code Connection}s
|
||||||
* or the like. This transaction manager will associate {@code Connection}
|
* or the like. This transaction manager will associate {@code Connection}
|
||||||
* with context-bound transactions itself, according to the specified propagation
|
* with context-bound transactions itself, according to the specified propagation
|
||||||
* behavior. It assumes that a separate, independent {@code Connection} can
|
* behavior. It assumes that a separate, independent {@code Connection} can
|
||||||
|
|
@ -72,6 +74,11 @@ import org.springframework.util.Assert;
|
||||||
* synchronizations (if synchronization is generally active), assuming resources
|
* synchronizations (if synchronization is generally active), assuming resources
|
||||||
* operating on the underlying R2DBC {@code Connection}.
|
* operating on the underlying R2DBC {@code Connection}.
|
||||||
*
|
*
|
||||||
|
* <p>Spring's {@code TransactionDefinition} attributes are carried forward to R2DBC drivers
|
||||||
|
* using extensible R2DBC {@link io.r2dbc.spi.TransactionDefinition}. Subclasses may
|
||||||
|
* override {@link #createTransactionDefinition(TransactionDefinition)} to customize
|
||||||
|
* transaction definitions for vendor-specific attributes.
|
||||||
|
*
|
||||||
* @author Mark Paluch
|
* @author Mark Paluch
|
||||||
* @since 5.3
|
* @since 5.3
|
||||||
* @see ConnectionFactoryUtils#getConnection(ConnectionFactory)
|
* @see ConnectionFactoryUtils#getConnection(ConnectionFactory)
|
||||||
|
|
@ -203,7 +210,8 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
|
||||||
}
|
}
|
||||||
|
|
||||||
return connectionMono.flatMap(con -> {
|
return connectionMono.flatMap(con -> {
|
||||||
return prepareTransactionalConnection(con, definition, transaction).then(Mono.from(con.beginTransaction()))
|
return prepareTransactionalConnection(con, definition, transaction)
|
||||||
|
.then(Mono.from(doBegin(definition, con)))
|
||||||
.doOnSuccess(v -> {
|
.doOnSuccess(v -> {
|
||||||
txObject.getConnectionHolder().setTransactionActive(true);
|
txObject.getConnectionHolder().setTransactionActive(true);
|
||||||
Duration timeout = determineTimeout(definition);
|
Duration timeout = determineTimeout(definition);
|
||||||
|
|
@ -230,6 +238,31 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
|
||||||
}).then();
|
}).then();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Publisher<Void> doBegin(TransactionDefinition definition, Connection con) {
|
||||||
|
io.r2dbc.spi.TransactionDefinition transactionDefinition = createTransactionDefinition(definition);
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Starting R2DBC transaction on Connection [" + con + "] using [" + transactionDefinition + "]");
|
||||||
|
}
|
||||||
|
return con.beginTransaction(transactionDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the transaction definition from our {@code TransactionDefinition}.
|
||||||
|
* Can be overridden to wrap the R2DBC {@code TransactionDefinition} to adjust or
|
||||||
|
* enhance transaction attributes.
|
||||||
|
* @param definition the transaction definition
|
||||||
|
* @return the actual transaction definition to use
|
||||||
|
* @since 6.0
|
||||||
|
* @see io.r2dbc.spi.TransactionDefinition
|
||||||
|
*/
|
||||||
|
protected io.r2dbc.spi.TransactionDefinition createTransactionDefinition(TransactionDefinition definition) {
|
||||||
|
// Apply specific isolation level, if any.
|
||||||
|
IsolationLevel isolationLevelToUse = resolveIsolationLevel(definition.getIsolationLevel());
|
||||||
|
return new ExtendedTransactionDefinition(definition.getName(), definition.isReadOnly(),
|
||||||
|
definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ? isolationLevelToUse : null,
|
||||||
|
determineTimeout(definition));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine the actual timeout to use for the given definition.
|
* Determine the actual timeout to use for the given definition.
|
||||||
* Will fall back to this manager's default timeout if the
|
* Will fall back to this manager's default timeout if the
|
||||||
|
|
@ -375,21 +408,6 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
|
||||||
.then();
|
.then();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply specific isolation level, if any.
|
|
||||||
IsolationLevel isolationLevelToUse = resolveIsolationLevel(definition.getIsolationLevel());
|
|
||||||
if (isolationLevelToUse != null && definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
|
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug("Changing isolation level of R2DBC Connection [" + con + "] to " + isolationLevelToUse.asSql());
|
|
||||||
}
|
|
||||||
IsolationLevel currentIsolation = con.getTransactionIsolationLevel();
|
|
||||||
if (!currentIsolation.asSql().equalsIgnoreCase(isolationLevelToUse.asSql())) {
|
|
||||||
|
|
||||||
txObject.setPreviousIsolationLevel(currentIsolation);
|
|
||||||
prepare = prepare.then(Mono.from(con.setTransactionIsolationLevel(isolationLevelToUse)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Switch to manual commit if necessary. This is very expensive in some R2DBC drivers,
|
// Switch to manual commit if necessary. This is very expensive in some R2DBC drivers,
|
||||||
// so we don't want to do it unnecessarily (for example if we've explicitly
|
// so we don't want to do it unnecessarily (for example if we've explicitly
|
||||||
// configured the connection pool to set it already).
|
// configured the connection pool to set it already).
|
||||||
|
|
@ -436,6 +454,62 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extended R2DBC transaction definition object providing transaction attributes
|
||||||
|
* to R2DBC drivers when starting a transaction.
|
||||||
|
*/
|
||||||
|
private record ExtendedTransactionDefinition(@Nullable String transactionName,
|
||||||
|
boolean readOnly,
|
||||||
|
@Nullable IsolationLevel isolationLevel,
|
||||||
|
Duration lockWaitTimeout) implements io.r2dbc.spi.TransactionDefinition {
|
||||||
|
|
||||||
|
private ExtendedTransactionDefinition(@Nullable String transactionName, boolean readOnly,
|
||||||
|
@Nullable IsolationLevel isolationLevel, Duration lockWaitTimeout) {
|
||||||
|
this.transactionName = transactionName;
|
||||||
|
this.readOnly = readOnly;
|
||||||
|
this.isolationLevel = isolationLevel;
|
||||||
|
this.lockWaitTimeout = lockWaitTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T getAttribute(Option<T> option) {
|
||||||
|
return (T) doGetValue(option);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Object doGetValue(Option<?> option) {
|
||||||
|
if (io.r2dbc.spi.TransactionDefinition.ISOLATION_LEVEL.equals(option)) {
|
||||||
|
return this.isolationLevel;
|
||||||
|
}
|
||||||
|
if (io.r2dbc.spi.TransactionDefinition.NAME.equals(option)) {
|
||||||
|
return this.transactionName;
|
||||||
|
}
|
||||||
|
if (io.r2dbc.spi.TransactionDefinition.READ_ONLY.equals(option)) {
|
||||||
|
return this.readOnly;
|
||||||
|
}
|
||||||
|
if (io.r2dbc.spi.TransactionDefinition.LOCK_WAIT_TIMEOUT.equals(option)
|
||||||
|
&& !this.lockWaitTimeout.isZero()) {
|
||||||
|
return this.lockWaitTimeout;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(getClass().getSimpleName());
|
||||||
|
sb.append(" [transactionName='").append(this.transactionName).append('\'');
|
||||||
|
sb.append(", readOnly=").append(this.readOnly);
|
||||||
|
sb.append(", isolationLevel=").append(this.isolationLevel);
|
||||||
|
sb.append(", lockWaitTimeout=").append(this.lockWaitTimeout);
|
||||||
|
sb.append(']');
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ConnectionFactory transaction object, representing a ConnectionHolder.
|
* ConnectionFactory transaction object, representing a ConnectionHolder.
|
||||||
* Used as transaction object by R2dbcTransactionManager.
|
* Used as transaction object by R2dbcTransactionManager.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2020 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
package org.springframework.r2dbc.core;
|
package org.springframework.r2dbc.core;
|
||||||
|
|
||||||
import org.springframework.lang.Nullable;
|
import io.r2dbc.spi.Parameter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface that defines common functionality for objects
|
* Interface that defines common functionality for objects
|
||||||
|
|
@ -44,24 +44,13 @@ interface BindParameterSource {
|
||||||
boolean hasValue(String paramName);
|
boolean hasValue(String paramName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the parameter value for the requested named parameter.
|
* Return the parameter for the requested named parameter.
|
||||||
* @param paramName the name of the parameter
|
* @param paramName the name of the parameter
|
||||||
* @return the value of the specified parameter (can be {@code null})
|
* @return the specified parameter
|
||||||
* @throws IllegalArgumentException if there is no value
|
* @throws IllegalArgumentException if there is no value
|
||||||
* for the requested parameter
|
* for the requested parameter
|
||||||
*/
|
*/
|
||||||
@Nullable
|
Parameter getValue(String paramName) throws IllegalArgumentException;
|
||||||
Object getValue(String paramName) throws IllegalArgumentException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine the type for the specified named parameter.
|
|
||||||
* @param paramName the name of the parameter
|
|
||||||
* @return the type of the specified parameter, or
|
|
||||||
* {@link Object#getClass()} if not known.
|
|
||||||
*/
|
|
||||||
default Class<?> getType(String paramName) {
|
|
||||||
return Object.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the parameter names of the underlying parameter source.
|
* Return the parameter names of the underlying parameter source.
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
package org.springframework.r2dbc.core;
|
package org.springframework.r2dbc.core;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
|
|
@ -55,12 +55,12 @@ public class ColumnMapRowMapper implements BiFunction<Row, RowMetadata, Map<Stri
|
||||||
@SuppressWarnings("deprecation") // getColumnNames() is deprecated as of R2DBC 0.9
|
@SuppressWarnings("deprecation") // getColumnNames() is deprecated as of R2DBC 0.9
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> apply(Row row, RowMetadata rowMetadata) {
|
public Map<String, Object> apply(Row row, RowMetadata rowMetadata) {
|
||||||
Collection<String> columns = rowMetadata.getColumnNames();
|
List<? extends ColumnMetadata> columns = rowMetadata.getColumnMetadatas();
|
||||||
int columnCount = columns.size();
|
int columnCount = columns.size();
|
||||||
Map<String, Object> mapOfColValues = createColumnMap(columnCount);
|
Map<String, Object> mapOfColValues = createColumnMap(columnCount);
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (String column : columns) {
|
for (ColumnMetadata column : columns) {
|
||||||
String key = getColumnKey(column);
|
String key = getColumnKey(column.getName());
|
||||||
Object obj = getColumnValue(row, index++);
|
Object obj = getColumnValue(row, index++);
|
||||||
mapOfColValues.put(key, obj);
|
mapOfColValues.put(key, obj);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2020 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -20,12 +20,17 @@ import java.util.Map;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import io.r2dbc.spi.ConnectionFactory;
|
import io.r2dbc.spi.ConnectionFactory;
|
||||||
|
import io.r2dbc.spi.Readable;
|
||||||
|
import io.r2dbc.spi.Result;
|
||||||
import io.r2dbc.spi.Row;
|
import io.r2dbc.spi.Row;
|
||||||
import io.r2dbc.spi.RowMetadata;
|
import io.r2dbc.spi.RowMetadata;
|
||||||
import io.r2dbc.spi.Statement;
|
import io.r2dbc.spi.Statement;
|
||||||
|
import org.reactivestreams.Publisher;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import org.springframework.r2dbc.core.binding.BindMarkersFactory;
|
import org.springframework.r2dbc.core.binding.BindMarkersFactory;
|
||||||
|
|
@ -157,9 +162,9 @@ public interface DatabaseClient extends ConnectionAccessor {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bind a non-{@code null} value to a parameter identified by its
|
* Bind a non-{@code null} value to a parameter identified by its
|
||||||
* {@code index}. {@code value} can be either a scalar value or {@link Parameter}.
|
* {@code index}. {@code value} can be either a scalar value or {@link io.r2dbc.spi.Parameter}.
|
||||||
* @param index zero based index to bind the parameter to
|
* @param index zero based index to bind the parameter to
|
||||||
* @param value either a scalar value or {@link Parameter}
|
* @param value either a scalar value or {@link io.r2dbc.spi.Parameter}
|
||||||
*/
|
*/
|
||||||
GenericExecuteSpec bind(int index, Object value);
|
GenericExecuteSpec bind(int index, Object value);
|
||||||
|
|
||||||
|
|
@ -213,14 +218,12 @@ public interface DatabaseClient extends ConnectionAccessor {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure a result mapping {@link Function function} and enter the execution stage.
|
* Configure a result mapping {@link Function function} and enter the execution stage.
|
||||||
* @param mappingFunction a function that maps from {@link Row} to the result type
|
* @param mappingFunction a function that maps from {@link Readable} to the result type
|
||||||
* @param <R> the result type
|
* @param <R> the result type
|
||||||
* @return a {@link FetchSpec} for configuration what to fetch
|
* @return a {@link FetchSpec} for configuration what to fetch
|
||||||
|
* @since 6.0
|
||||||
*/
|
*/
|
||||||
default <R> RowsFetchSpec<R> map(Function<Row, R> mappingFunction) {
|
<R> RowsFetchSpec<R> map(Function<? super Readable, R> mappingFunction);
|
||||||
Assert.notNull(mappingFunction, "Mapping function must not be null");
|
|
||||||
return map((row, rowMetadata) -> mappingFunction.apply(row));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure a result mapping {@link BiFunction function} and enter the execution stage.
|
* Configure a result mapping {@link BiFunction function} and enter the execution stage.
|
||||||
|
|
@ -231,6 +234,17 @@ public interface DatabaseClient extends ConnectionAccessor {
|
||||||
*/
|
*/
|
||||||
<R> RowsFetchSpec<R> map(BiFunction<Row, RowMetadata, R> mappingFunction);
|
<R> RowsFetchSpec<R> map(BiFunction<Row, RowMetadata, R> mappingFunction);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform the SQL call and apply {@link BiFunction function} to the {@link Result}.
|
||||||
|
* @param mappingFunction a function that maps from {@link Result} into a result publisher
|
||||||
|
* @param <R> the result type
|
||||||
|
* @return a {@link Flux} emitting mapped elements
|
||||||
|
* @since 6.0
|
||||||
|
* @see Result#filter(Predicate)
|
||||||
|
* @see Result#flatMap(Function)
|
||||||
|
*/
|
||||||
|
<R> Flux<R> flatMap(Function<Result, Publisher<R>> mappingFunction);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform the SQL call and retrieve the result by entering the execution stage.
|
* Perform the SQL call and retrieve the result by entering the execution stage.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2020 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -32,7 +32,10 @@ import java.util.stream.Collectors;
|
||||||
|
|
||||||
import io.r2dbc.spi.Connection;
|
import io.r2dbc.spi.Connection;
|
||||||
import io.r2dbc.spi.ConnectionFactory;
|
import io.r2dbc.spi.ConnectionFactory;
|
||||||
|
import io.r2dbc.spi.Parameter;
|
||||||
|
import io.r2dbc.spi.Parameters;
|
||||||
import io.r2dbc.spi.R2dbcException;
|
import io.r2dbc.spi.R2dbcException;
|
||||||
|
import io.r2dbc.spi.Readable;
|
||||||
import io.r2dbc.spi.Result;
|
import io.r2dbc.spi.Result;
|
||||||
import io.r2dbc.spi.Row;
|
import io.r2dbc.spi.Row;
|
||||||
import io.r2dbc.spi.RowMetadata;
|
import io.r2dbc.spi.RowMetadata;
|
||||||
|
|
@ -188,11 +191,12 @@ class DefaultDatabaseClient implements DatabaseClient {
|
||||||
new CloseSuppressingInvocationHandler(con));
|
new CloseSuppressingInvocationHandler(con));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Mono<Integer> sumRowsUpdated(
|
private static Mono<Long> sumRowsUpdated(
|
||||||
Function<Connection, Flux<Result>> resultFunction, Connection it) {
|
Function<Connection, Flux<Result>> resultFunction, Connection it) {
|
||||||
return resultFunction.apply(it)
|
return resultFunction.apply(it)
|
||||||
.flatMap(Result::getRowsUpdated)
|
.flatMap(Result::getRowsUpdated)
|
||||||
.collect(Collectors.summingInt(Integer::intValue));
|
.cast(Number.class)
|
||||||
|
.collect(Collectors.summingLong(Number::longValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -243,17 +247,21 @@ class DefaultDatabaseClient implements DatabaseClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public DefaultGenericExecuteSpec bind(int index, Object value) {
|
public DefaultGenericExecuteSpec bind(int index, Object value) {
|
||||||
assertNotPreparedOperation();
|
assertNotPreparedOperation();
|
||||||
Assert.notNull(value, () -> String.format(
|
Assert.notNull(value, () -> String.format(
|
||||||
"Value at index %d must not be null. Use bindNull(…) instead.", index));
|
"Value at index %d must not be null. Use bindNull(…) instead.", index));
|
||||||
|
|
||||||
Map<Integer, Parameter> byIndex = new LinkedHashMap<>(this.byIndex);
|
Map<Integer, Parameter> byIndex = new LinkedHashMap<>(this.byIndex);
|
||||||
if (value instanceof Parameter) {
|
if (value instanceof Parameter p) {
|
||||||
byIndex.put(index, (Parameter) value);
|
byIndex.put(index, p);
|
||||||
|
}
|
||||||
|
else if (value instanceof org.springframework.r2dbc.core.Parameter p) {
|
||||||
|
byIndex.put(index, p.hasValue() ? Parameters.in(p.getValue()) : Parameters.in(p.getType()));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
byIndex.put(index, Parameter.fromOrEmpty(value, value.getClass()));
|
byIndex.put(index, Parameters.in(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new DefaultGenericExecuteSpec(byIndex, this.byName, this.sqlSupplier, this.filterFunction);
|
return new DefaultGenericExecuteSpec(byIndex, this.byName, this.sqlSupplier, this.filterFunction);
|
||||||
|
|
@ -264,12 +272,13 @@ class DefaultDatabaseClient implements DatabaseClient {
|
||||||
assertNotPreparedOperation();
|
assertNotPreparedOperation();
|
||||||
|
|
||||||
Map<Integer, Parameter> byIndex = new LinkedHashMap<>(this.byIndex);
|
Map<Integer, Parameter> byIndex = new LinkedHashMap<>(this.byIndex);
|
||||||
byIndex.put(index, Parameter.empty(type));
|
byIndex.put(index, Parameters.in(type));
|
||||||
|
|
||||||
return new DefaultGenericExecuteSpec(byIndex, this.byName, this.sqlSupplier, this.filterFunction);
|
return new DefaultGenericExecuteSpec(byIndex, this.byName, this.sqlSupplier, this.filterFunction);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public DefaultGenericExecuteSpec bind(String name, Object value) {
|
public DefaultGenericExecuteSpec bind(String name, Object value) {
|
||||||
assertNotPreparedOperation();
|
assertNotPreparedOperation();
|
||||||
|
|
||||||
|
|
@ -278,11 +287,14 @@ class DefaultDatabaseClient implements DatabaseClient {
|
||||||
"Value for parameter %s must not be null. Use bindNull(…) instead.", name));
|
"Value for parameter %s must not be null. Use bindNull(…) instead.", name));
|
||||||
|
|
||||||
Map<String, Parameter> byName = new LinkedHashMap<>(this.byName);
|
Map<String, Parameter> byName = new LinkedHashMap<>(this.byName);
|
||||||
if (value instanceof Parameter) {
|
if (value instanceof Parameter p) {
|
||||||
byName.put(name, (Parameter) value);
|
byName.put(name, p);
|
||||||
|
}
|
||||||
|
else if (value instanceof org.springframework.r2dbc.core.Parameter p) {
|
||||||
|
byName.put(name, p.hasValue() ? Parameters.in(p.getValue()) : Parameters.in(p.getType()));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
byName.put(name, Parameter.fromOrEmpty(value, value.getClass()));
|
byName.put(name, Parameters.in(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new DefaultGenericExecuteSpec(this.byIndex, byName, this.sqlSupplier, this.filterFunction);
|
return new DefaultGenericExecuteSpec(this.byIndex, byName, this.sqlSupplier, this.filterFunction);
|
||||||
|
|
@ -294,7 +306,7 @@ class DefaultDatabaseClient implements DatabaseClient {
|
||||||
Assert.hasText(name, "Parameter name must not be null or empty!");
|
Assert.hasText(name, "Parameter name must not be null or empty!");
|
||||||
|
|
||||||
Map<String, Parameter> byName = new LinkedHashMap<>(this.byName);
|
Map<String, Parameter> byName = new LinkedHashMap<>(this.byName);
|
||||||
byName.put(name, Parameter.empty(type));
|
byName.put(name, Parameters.in(type));
|
||||||
|
|
||||||
return new DefaultGenericExecuteSpec(this.byIndex, byName, this.sqlSupplier, this.filterFunction);
|
return new DefaultGenericExecuteSpec(this.byIndex, byName, this.sqlSupplier, this.filterFunction);
|
||||||
}
|
}
|
||||||
|
|
@ -306,15 +318,27 @@ class DefaultDatabaseClient implements DatabaseClient {
|
||||||
this.byIndex, this.byName, this.sqlSupplier, this.filterFunction.andThen(filter));
|
this.byIndex, this.byName, this.sqlSupplier, this.filterFunction.andThen(filter));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R> FetchSpec<R> map(Function<? super Readable, R> mappingFunction) {
|
||||||
|
Assert.notNull(mappingFunction, "Mapping function must not be null");
|
||||||
|
return execute(this.sqlSupplier, result -> result.map(mappingFunction));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <R> FetchSpec<R> map(BiFunction<Row, RowMetadata, R> mappingFunction) {
|
public <R> FetchSpec<R> map(BiFunction<Row, RowMetadata, R> mappingFunction) {
|
||||||
Assert.notNull(mappingFunction, "Mapping function must not be null");
|
Assert.notNull(mappingFunction, "Mapping function must not be null");
|
||||||
return execute(this.sqlSupplier, mappingFunction);
|
return execute(this.sqlSupplier, result -> result.map(mappingFunction));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R> Flux<R> flatMap(Function<Result, Publisher<R>> mappingFunction) {
|
||||||
|
Assert.notNull(mappingFunction, "Mapping function must not be null");
|
||||||
|
return flatMap(this.sqlSupplier, mappingFunction);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FetchSpec<Map<String, Object>> fetch() {
|
public FetchSpec<Map<String, Object>> fetch() {
|
||||||
return execute(this.sqlSupplier, ColumnMapRowMapper.INSTANCE);
|
return execute(this.sqlSupplier, result -> result.map(ColumnMapRowMapper.INSTANCE));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -322,7 +346,7 @@ class DefaultDatabaseClient implements DatabaseClient {
|
||||||
return fetch().rowsUpdated().then();
|
return fetch().rowsUpdated().then();
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> FetchSpec<T> execute(Supplier<String> sqlSupplier, BiFunction<Row, RowMetadata, T> mappingFunction) {
|
private ResultFunction getResultFunction(Supplier<String> sqlSupplier) {
|
||||||
String sql = getRequiredSql(sqlSupplier);
|
String sql = getRequiredSql(sqlSupplier);
|
||||||
Function<Connection, Statement> statementFunction = connection -> {
|
Function<Connection, Statement> statementFunction = connection -> {
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
|
|
@ -376,11 +400,25 @@ class DefaultDatabaseClient implements DatabaseClient {
|
||||||
.cast(Result.class).checkpoint("SQL \"" + sql + "\" [DatabaseClient]");
|
.cast(Result.class).checkpoint("SQL \"" + sql + "\" [DatabaseClient]");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return new ResultFunction(resultFunction, sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> FetchSpec<T> execute(Supplier<String> sqlSupplier, Function<Result, Publisher<T>> resultAdapter) {
|
||||||
|
ResultFunction resultHandler = getResultFunction(sqlSupplier);
|
||||||
|
|
||||||
return new DefaultFetchSpec<>(
|
return new DefaultFetchSpec<>(
|
||||||
DefaultDatabaseClient.this, sql,
|
DefaultDatabaseClient.this, resultHandler.sql(),
|
||||||
new ConnectionFunction<>(sql, resultFunction),
|
new ConnectionFunction<>(resultHandler.sql(), resultHandler.function()),
|
||||||
new ConnectionFunction<>(sql, connection -> sumRowsUpdated(resultFunction, connection)),
|
new ConnectionFunction<>(resultHandler.sql(), connection -> sumRowsUpdated(resultHandler.function(), connection)),
|
||||||
mappingFunction);
|
resultAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> Flux<T> flatMap(Supplier<String> sqlSupplier, Function<Result, Publisher<T>> mappingFunction) {
|
||||||
|
ResultFunction resultHandler = getResultFunction(sqlSupplier);
|
||||||
|
ConnectionFunction<Flux<T>> connectionFunction = new ConnectionFunction<>(resultHandler.sql(), cx -> resultHandler.function()
|
||||||
|
.apply(cx)
|
||||||
|
.flatMap(mappingFunction));
|
||||||
|
return inConnectionMany(connectionFunction);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MapBindParameterSource retrieveParameters(String sql, List<String> parameterNames,
|
private MapBindParameterSource retrieveParameters(String sql, List<String> parameterNames,
|
||||||
|
|
@ -424,27 +462,11 @@ class DefaultDatabaseClient implements DatabaseClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void bindByName(Statement statement, Map<String, Parameter> byName) {
|
private void bindByName(Statement statement, Map<String, Parameter> byName) {
|
||||||
byName.forEach((name, parameter) -> {
|
byName.forEach(statement::bind);
|
||||||
Object value = parameter.getValue();
|
|
||||||
if (value != null) {
|
|
||||||
statement.bind(name, value);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
statement.bindNull(name, parameter.getType());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void bindByIndex(Statement statement, Map<Integer, Parameter> byIndex) {
|
private void bindByIndex(Statement statement, Map<Integer, Parameter> byIndex) {
|
||||||
byIndex.forEach((i, parameter) -> {
|
byIndex.forEach(statement::bind);
|
||||||
Object value = parameter.getValue();
|
|
||||||
if (value != null) {
|
|
||||||
statement.bind(i, value);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
statement.bindNull(i, parameter.getType());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getRequiredSql(Supplier<String> sqlSupplier) {
|
private String getRequiredSql(Supplier<String> sqlSupplier) {
|
||||||
|
|
@ -452,6 +474,9 @@ class DefaultDatabaseClient implements DatabaseClient {
|
||||||
Assert.state(StringUtils.hasText(sql), "SQL returned by SQL supplier must not be empty!");
|
Assert.state(StringUtils.hasText(sql), "SQL returned by SQL supplier must not be empty!");
|
||||||
return sql;
|
return sql;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
record ResultFunction(Function<Connection, Flux<Result>> function, String sql){}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,11 @@
|
||||||
|
|
||||||
package org.springframework.r2dbc.core;
|
package org.springframework.r2dbc.core;
|
||||||
|
|
||||||
import java.util.function.BiFunction;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import io.r2dbc.spi.Connection;
|
import io.r2dbc.spi.Connection;
|
||||||
import io.r2dbc.spi.Result;
|
import io.r2dbc.spi.Result;
|
||||||
import io.r2dbc.spi.Row;
|
import org.reactivestreams.Publisher;
|
||||||
import io.r2dbc.spi.RowMetadata;
|
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
|
@ -43,21 +41,21 @@ class DefaultFetchSpec<T> implements FetchSpec<T> {
|
||||||
|
|
||||||
private final Function<Connection, Flux<Result>> resultFunction;
|
private final Function<Connection, Flux<Result>> resultFunction;
|
||||||
|
|
||||||
private final Function<Connection, Mono<Integer>> updatedRowsFunction;
|
private final Function<Connection, Mono<Long>> updatedRowsFunction;
|
||||||
|
|
||||||
private final BiFunction<Row, RowMetadata, T> mappingFunction;
|
private final Function<Result, Publisher<T>> resultAdapter;
|
||||||
|
|
||||||
|
|
||||||
DefaultFetchSpec(ConnectionAccessor connectionAccessor, String sql,
|
DefaultFetchSpec(ConnectionAccessor connectionAccessor, String sql,
|
||||||
Function<Connection, Flux<Result>> resultFunction,
|
Function<Connection, Flux<Result>> resultFunction,
|
||||||
Function<Connection, Mono<Integer>> updatedRowsFunction,
|
Function<Connection, Mono<Long>> updatedRowsFunction,
|
||||||
BiFunction<Row, RowMetadata, T> mappingFunction) {
|
Function<Result, Publisher<T>> resultAdapter) {
|
||||||
|
|
||||||
this.sql = sql;
|
this.sql = sql;
|
||||||
this.connectionAccessor = connectionAccessor;
|
this.connectionAccessor = connectionAccessor;
|
||||||
this.resultFunction = resultFunction;
|
this.resultFunction = resultFunction;
|
||||||
this.updatedRowsFunction = updatedRowsFunction;
|
this.updatedRowsFunction = updatedRowsFunction;
|
||||||
this.mappingFunction = mappingFunction;
|
this.resultAdapter = resultAdapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -86,11 +84,11 @@ class DefaultFetchSpec<T> implements FetchSpec<T> {
|
||||||
public Flux<T> all() {
|
public Flux<T> all() {
|
||||||
return this.connectionAccessor.inConnectionMany(new ConnectionFunction<>(this.sql,
|
return this.connectionAccessor.inConnectionMany(new ConnectionFunction<>(this.sql,
|
||||||
connection -> this.resultFunction.apply(connection)
|
connection -> this.resultFunction.apply(connection)
|
||||||
.flatMap(result -> result.map(this.mappingFunction))));
|
.flatMap(this.resultAdapter)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Integer> rowsUpdated() {
|
public Mono<Long> rowsUpdated() {
|
||||||
return this.connectionAccessor.inConnection(this.updatedRowsFunction);
|
return this.connectionAccessor.inConnection(this.updatedRowsFunction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2020 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -19,6 +19,9 @@ package org.springframework.r2dbc.core;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import io.r2dbc.spi.Parameter;
|
||||||
|
import io.r2dbc.spi.Parameters;
|
||||||
|
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -61,7 +64,7 @@ class MapBindParameterSource implements BindParameterSource {
|
||||||
MapBindParameterSource addValue(String paramName, Object value) {
|
MapBindParameterSource addValue(String paramName, Object value) {
|
||||||
Assert.notNull(paramName, "Parameter name must not be null");
|
Assert.notNull(paramName, "Parameter name must not be null");
|
||||||
Assert.notNull(value, "Value must not be null");
|
Assert.notNull(value, "Value must not be null");
|
||||||
this.values.put(paramName, Parameter.fromOrEmpty(value, value.getClass()));
|
this.values.put(paramName, Parameters.in(value));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,21 +75,11 @@ class MapBindParameterSource implements BindParameterSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<?> getType(String paramName) {
|
public Parameter getValue(String paramName) throws IllegalArgumentException {
|
||||||
Assert.notNull(paramName, "Parameter name must not be null");
|
|
||||||
Parameter parameter = this.values.get(paramName);
|
|
||||||
if (parameter != null) {
|
|
||||||
return parameter.getType();
|
|
||||||
}
|
|
||||||
return Object.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getValue(String paramName) throws IllegalArgumentException {
|
|
||||||
if (!hasValue(paramName)) {
|
if (!hasValue(paramName)) {
|
||||||
throw new IllegalArgumentException("No value registered for key '" + paramName + "'");
|
throw new IllegalArgumentException("No value registered for key '" + paramName + "'");
|
||||||
}
|
}
|
||||||
return this.values.get(paramName).getValue();
|
return this.values.get(paramName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2021 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -26,6 +26,8 @@ import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import io.r2dbc.spi.Parameter;
|
||||||
|
|
||||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.r2dbc.core.binding.BindMarker;
|
import org.springframework.r2dbc.core.binding.BindMarker;
|
||||||
|
|
@ -291,10 +293,10 @@ abstract class NamedParameterUtils {
|
||||||
actualSql.append(originalSql, lastIndex, startIndex);
|
actualSql.append(originalSql, lastIndex, startIndex);
|
||||||
NamedParameters.NamedParameter marker = markerHolder.getOrCreate(paramName);
|
NamedParameters.NamedParameter marker = markerHolder.getOrCreate(paramName);
|
||||||
if (paramSource.hasValue(paramName)) {
|
if (paramSource.hasValue(paramName)) {
|
||||||
Object value = paramSource.getValue(paramName);
|
Parameter parameter = paramSource.getValue(paramName);
|
||||||
if (value instanceof Collection) {
|
if (parameter.getValue() instanceof Collection<?> c) {
|
||||||
|
|
||||||
Iterator<?> entryIter = ((Collection<?>) value).iterator();
|
Iterator<?> entryIter = c.iterator();
|
||||||
int k = 0;
|
int k = 0;
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
while (entryIter.hasNext()) {
|
while (entryIter.hasNext()) {
|
||||||
|
|
@ -508,14 +510,14 @@ abstract class NamedParameterUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void bind(BindTarget target, String identifier, Object value) {
|
public void bind(BindTarget target, String identifier, Parameter parameter) {
|
||||||
List<BindMarker> bindMarkers = getBindMarkers(identifier);
|
List<BindMarker> bindMarkers = getBindMarkers(identifier);
|
||||||
if (bindMarkers == null) {
|
if (bindMarkers == null) {
|
||||||
target.bind(identifier, value);
|
target.bind(identifier, parameter);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (value instanceof Collection) {
|
if (parameter.getValue() instanceof Collection) {
|
||||||
Collection<Object> collection = (Collection<Object>) value;
|
Collection<Object> collection = (Collection<Object>) parameter.getValue();
|
||||||
Iterator<Object> iterator = collection.iterator();
|
Iterator<Object> iterator = collection.iterator();
|
||||||
Iterator<BindMarker> markers = bindMarkers.iterator();
|
Iterator<BindMarker> markers = bindMarkers.iterator();
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
|
|
@ -532,7 +534,7 @@ abstract class NamedParameterUtils {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (BindMarker bindMarker : bindMarkers) {
|
for (BindMarker bindMarker : bindMarkers) {
|
||||||
bindMarker.bind(target, value);
|
bindMarker.bind(target, parameter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -544,14 +546,14 @@ abstract class NamedParameterUtils {
|
||||||
markers.next().bind(target, valueToBind);
|
markers.next().bind(target, valueToBind);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void bindNull(BindTarget target, String identifier, Class<?> valueType) {
|
public void bindNull(BindTarget target, String identifier, Parameter parameter) {
|
||||||
List<BindMarker> bindMarkers = getBindMarkers(identifier);
|
List<BindMarker> bindMarkers = getBindMarkers(identifier);
|
||||||
if (bindMarkers == null) {
|
if (bindMarkers == null) {
|
||||||
target.bindNull(identifier, valueType);
|
target.bind(identifier, parameter);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (BindMarker bindMarker : bindMarkers) {
|
for (BindMarker bindMarker : bindMarkers) {
|
||||||
bindMarker.bindNull(target, valueType);
|
bindMarker.bind(target, parameter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -576,12 +578,12 @@ abstract class NamedParameterUtils {
|
||||||
@Override
|
@Override
|
||||||
public void bindTo(BindTarget target) {
|
public void bindTo(BindTarget target) {
|
||||||
for (String namedParameter : this.parameterSource.getParameterNames()) {
|
for (String namedParameter : this.parameterSource.getParameterNames()) {
|
||||||
Object value = this.parameterSource.getValue(namedParameter);
|
Parameter parameter = this.parameterSource.getValue(namedParameter);
|
||||||
if (value == null) {
|
if (parameter.getValue() == null) {
|
||||||
bindNull(target, namedParameter, this.parameterSource.getType(namedParameter));
|
bindNull(target, namedParameter, parameter);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
bind(target, namedParameter, value);
|
bind(target, namedParameter, parameter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2020 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -29,7 +29,9 @@ import org.springframework.util.ObjectUtils;
|
||||||
* @author Mark Paluch
|
* @author Mark Paluch
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @since 5.3
|
* @since 5.3
|
||||||
|
* @deprecated since 6.0, use {@code io.r2dbc.spi.Parameter} instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "6.0")
|
||||||
public final class Parameter {
|
public final class Parameter {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,6 @@ public interface UpdatedRowsFetchSpec {
|
||||||
* Get the number of updated rows.
|
* Get the number of updated rows.
|
||||||
* @return a Mono emitting the number of updated rows
|
* @return a Mono emitting the number of updated rows
|
||||||
*/
|
*/
|
||||||
Mono<Integer> rowsUpdated();
|
Mono<Long> rowsUpdated();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package org.springframework.r2dbc.core
|
package org.springframework.r2dbc.core
|
||||||
|
|
||||||
|
import io.r2dbc.spi.Parameters
|
||||||
import kotlinx.coroutines.reactor.awaitSingleOrNull
|
import kotlinx.coroutines.reactor.awaitSingleOrNull
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -35,7 +36,7 @@ suspend fun DatabaseClient.GenericExecuteSpec.await() {
|
||||||
* @author Ibanga Enoobong Ime
|
* @author Ibanga Enoobong Ime
|
||||||
*/
|
*/
|
||||||
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
|
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
|
||||||
inline fun <reified T : Any> DatabaseClient.GenericExecuteSpec.bind(index: Int, value: T?) = bind(index, Parameter.fromOrEmpty(value, T::class.java))
|
inline fun <reified T : Any> DatabaseClient.GenericExecuteSpec.bind(index: Int, value: T?) = bind(index, if (value != null) Parameters.`in`(value) else Parameters.`in`(T::class.java))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extension for [DatabaseClient.GenericExecuteSpec.bind] providing a variant leveraging reified type parameters
|
* Extension for [DatabaseClient.GenericExecuteSpec.bind] providing a variant leveraging reified type parameters
|
||||||
|
|
@ -44,4 +45,4 @@ inline fun <reified T : Any> DatabaseClient.GenericExecuteSpec.bind(index: Int,
|
||||||
* @author Ibanga Enoobong Ime
|
* @author Ibanga Enoobong Ime
|
||||||
*/
|
*/
|
||||||
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
|
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
|
||||||
inline fun <reified T : Any> DatabaseClient.GenericExecuteSpec.bind(name: String, value: T?) = bind(name, Parameter.fromOrEmpty(value, T::class.java))
|
inline fun <reified T : Any> DatabaseClient.GenericExecuteSpec.bind(name: String, value: T?) = bind(name, if (value != null) Parameters.`in`(value) else Parameters.`in`(T::class.java))
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2020 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -23,5 +23,5 @@ import kotlinx.coroutines.reactive.awaitSingle
|
||||||
*
|
*
|
||||||
* @author Fred Montariol
|
* @author Fred Montariol
|
||||||
*/
|
*/
|
||||||
suspend fun UpdatedRowsFetchSpec.awaitRowsUpdated(): Int =
|
suspend fun UpdatedRowsFetchSpec.awaitRowsUpdated(): Long =
|
||||||
rowsUpdated().awaitSingle()
|
rowsUpdated().awaitSingle()
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2020 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package org.springframework.r2dbc.connection;
|
package org.springframework.r2dbc.connection;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import io.r2dbc.spi.Connection;
|
import io.r2dbc.spi.Connection;
|
||||||
|
|
@ -25,6 +26,7 @@ import io.r2dbc.spi.R2dbcBadGrammarException;
|
||||||
import io.r2dbc.spi.Statement;
|
import io.r2dbc.spi.Statement;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
import reactor.test.StepVerifier;
|
import reactor.test.StepVerifier;
|
||||||
|
|
||||||
|
|
@ -65,7 +67,7 @@ class R2dbcTransactionManagerUnitTests {
|
||||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
void before() {
|
void before() {
|
||||||
when(connectionFactoryMock.create()).thenReturn((Mono) Mono.just(connectionMock));
|
when(connectionFactoryMock.create()).thenReturn((Mono) Mono.just(connectionMock));
|
||||||
when(connectionMock.beginTransaction()).thenReturn(Mono.empty());
|
when(connectionMock.beginTransaction(any(io.r2dbc.spi.TransactionDefinition.class))).thenReturn(Mono.empty());
|
||||||
when(connectionMock.close()).thenReturn(Mono.empty());
|
when(connectionMock.close()).thenReturn(Mono.empty());
|
||||||
tm = new R2dbcTransactionManager(connectionFactoryMock);
|
tm = new R2dbcTransactionManager(connectionFactoryMock);
|
||||||
}
|
}
|
||||||
|
|
@ -91,7 +93,7 @@ class R2dbcTransactionManagerUnitTests {
|
||||||
|
|
||||||
assertThat(commits).hasValue(1);
|
assertThat(commits).hasValue(1);
|
||||||
verify(connectionMock).isAutoCommit();
|
verify(connectionMock).isAutoCommit();
|
||||||
verify(connectionMock).beginTransaction();
|
verify(connectionMock).beginTransaction(any(io.r2dbc.spi.TransactionDefinition.class));
|
||||||
verify(connectionMock).commitTransaction();
|
verify(connectionMock).commitTransaction();
|
||||||
verify(connectionMock).close();
|
verify(connectionMock).close();
|
||||||
verifyNoMoreInteractions(connectionMock);
|
verifyNoMoreInteractions(connectionMock);
|
||||||
|
|
@ -125,13 +127,13 @@ class R2dbcTransactionManagerUnitTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void appliesIsolationLevel() {
|
void appliesTransactionDefinition() {
|
||||||
when(connectionMock.commitTransaction()).thenReturn(Mono.empty());
|
when(connectionMock.commitTransaction()).thenReturn(Mono.empty());
|
||||||
when(connectionMock.getTransactionIsolationLevel()).thenReturn(
|
|
||||||
IsolationLevel.READ_COMMITTED);
|
|
||||||
when(connectionMock.setTransactionIsolationLevel(any())).thenReturn(Mono.empty());
|
|
||||||
|
|
||||||
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
|
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
|
||||||
|
definition.setName("my-transaction");
|
||||||
|
definition.setTimeout(10);
|
||||||
|
definition.setReadOnly(true);
|
||||||
definition.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
|
definition.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
|
||||||
|
|
||||||
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
|
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
|
||||||
|
|
@ -142,12 +144,17 @@ class R2dbcTransactionManagerUnitTests {
|
||||||
.expectNextCount(1)
|
.expectNextCount(1)
|
||||||
.verifyComplete();
|
.verifyComplete();
|
||||||
|
|
||||||
verify(connectionMock).beginTransaction();
|
ArgumentCaptor<io.r2dbc.spi.TransactionDefinition> txCaptor = ArgumentCaptor.forClass(io.r2dbc.spi.TransactionDefinition.class);
|
||||||
verify(connectionMock).setTransactionIsolationLevel(
|
verify(connectionMock).beginTransaction(txCaptor.capture());
|
||||||
IsolationLevel.READ_COMMITTED);
|
verify(connectionMock, never()).setTransactionIsolationLevel(any());
|
||||||
verify(connectionMock).setTransactionIsolationLevel(IsolationLevel.SERIALIZABLE);
|
|
||||||
verify(connectionMock).commitTransaction();
|
verify(connectionMock).commitTransaction();
|
||||||
verify(connectionMock).close();
|
verify(connectionMock).close();
|
||||||
|
|
||||||
|
io.r2dbc.spi.TransactionDefinition def = txCaptor.getValue();
|
||||||
|
assertThat(def.getAttribute(io.r2dbc.spi.TransactionDefinition.NAME)).isEqualTo("my-transaction");
|
||||||
|
assertThat(def.getAttribute(io.r2dbc.spi.TransactionDefinition.LOCK_WAIT_TIMEOUT)).isEqualTo(Duration.ofSeconds(10));
|
||||||
|
assertThat(def.getAttribute(io.r2dbc.spi.TransactionDefinition.READ_ONLY)).isEqualTo(true);
|
||||||
|
assertThat(def.getAttribute(io.r2dbc.spi.TransactionDefinition.ISOLATION_LEVEL)).isEqualTo(IsolationLevel.SERIALIZABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -167,7 +174,7 @@ class R2dbcTransactionManagerUnitTests {
|
||||||
.expectNextCount(1)
|
.expectNextCount(1)
|
||||||
.verifyComplete();
|
.verifyComplete();
|
||||||
|
|
||||||
verify(connectionMock).beginTransaction();
|
verify(connectionMock).beginTransaction(any(io.r2dbc.spi.TransactionDefinition.class));
|
||||||
verify(connectionMock, never()).setTransactionIsolationLevel(any());
|
verify(connectionMock, never()).setTransactionIsolationLevel(any());
|
||||||
verify(connectionMock).commitTransaction();
|
verify(connectionMock).commitTransaction();
|
||||||
}
|
}
|
||||||
|
|
@ -187,7 +194,7 @@ class R2dbcTransactionManagerUnitTests {
|
||||||
.expectNextCount(1)
|
.expectNextCount(1)
|
||||||
.verifyComplete();
|
.verifyComplete();
|
||||||
|
|
||||||
verify(connectionMock).beginTransaction();
|
verify(connectionMock).beginTransaction(any(io.r2dbc.spi.TransactionDefinition.class));
|
||||||
verify(connectionMock, never()).setAutoCommit(anyBoolean());
|
verify(connectionMock, never()).setAutoCommit(anyBoolean());
|
||||||
verify(connectionMock).commitTransaction();
|
verify(connectionMock).commitTransaction();
|
||||||
}
|
}
|
||||||
|
|
@ -208,7 +215,7 @@ class R2dbcTransactionManagerUnitTests {
|
||||||
.expectNextCount(1)
|
.expectNextCount(1)
|
||||||
.verifyComplete();
|
.verifyComplete();
|
||||||
|
|
||||||
verify(connectionMock).beginTransaction();
|
verify(connectionMock).beginTransaction(any(io.r2dbc.spi.TransactionDefinition.class));
|
||||||
verify(connectionMock).setAutoCommit(false);
|
verify(connectionMock).setAutoCommit(false);
|
||||||
verify(connectionMock).setAutoCommit(true);
|
verify(connectionMock).setAutoCommit(true);
|
||||||
verify(connectionMock).commitTransaction();
|
verify(connectionMock).commitTransaction();
|
||||||
|
|
@ -236,7 +243,7 @@ class R2dbcTransactionManagerUnitTests {
|
||||||
.verifyComplete();
|
.verifyComplete();
|
||||||
|
|
||||||
verify(connectionMock).isAutoCommit();
|
verify(connectionMock).isAutoCommit();
|
||||||
verify(connectionMock).beginTransaction();
|
verify(connectionMock).beginTransaction(any(io.r2dbc.spi.TransactionDefinition.class));
|
||||||
verify(connectionMock).createStatement("SET TRANSACTION READ ONLY");
|
verify(connectionMock).createStatement("SET TRANSACTION READ ONLY");
|
||||||
verify(connectionMock).commitTransaction();
|
verify(connectionMock).commitTransaction();
|
||||||
verify(connectionMock).close();
|
verify(connectionMock).close();
|
||||||
|
|
@ -258,7 +265,7 @@ class R2dbcTransactionManagerUnitTests {
|
||||||
.verifyError(IllegalTransactionStateException.class);
|
.verifyError(IllegalTransactionStateException.class);
|
||||||
|
|
||||||
verify(connectionMock).isAutoCommit();
|
verify(connectionMock).isAutoCommit();
|
||||||
verify(connectionMock).beginTransaction();
|
verify(connectionMock).beginTransaction(any(io.r2dbc.spi.TransactionDefinition.class));
|
||||||
verify(connectionMock).createStatement("foo");
|
verify(connectionMock).createStatement("foo");
|
||||||
verify(connectionMock).commitTransaction();
|
verify(connectionMock).commitTransaction();
|
||||||
verify(connectionMock).close();
|
verify(connectionMock).close();
|
||||||
|
|
@ -288,7 +295,7 @@ class R2dbcTransactionManagerUnitTests {
|
||||||
assertThat(commits).hasValue(0);
|
assertThat(commits).hasValue(0);
|
||||||
assertThat(rollbacks).hasValue(1);
|
assertThat(rollbacks).hasValue(1);
|
||||||
verify(connectionMock).isAutoCommit();
|
verify(connectionMock).isAutoCommit();
|
||||||
verify(connectionMock).beginTransaction();
|
verify(connectionMock).beginTransaction(any(io.r2dbc.spi.TransactionDefinition.class));
|
||||||
verify(connectionMock).rollbackTransaction();
|
verify(connectionMock).rollbackTransaction();
|
||||||
verify(connectionMock).close();
|
verify(connectionMock).close();
|
||||||
verifyNoMoreInteractions(connectionMock);
|
verifyNoMoreInteractions(connectionMock);
|
||||||
|
|
@ -311,7 +318,7 @@ class R2dbcTransactionManagerUnitTests {
|
||||||
.verifyError(IllegalTransactionStateException.class);
|
.verifyError(IllegalTransactionStateException.class);
|
||||||
|
|
||||||
verify(connectionMock).isAutoCommit();
|
verify(connectionMock).isAutoCommit();
|
||||||
verify(connectionMock).beginTransaction();
|
verify(connectionMock).beginTransaction(any(io.r2dbc.spi.TransactionDefinition.class));
|
||||||
verify(connectionMock).createStatement("foo");
|
verify(connectionMock).createStatement("foo");
|
||||||
verify(connectionMock, never()).commitTransaction();
|
verify(connectionMock, never()).commitTransaction();
|
||||||
verify(connectionMock).rollbackTransaction();
|
verify(connectionMock).rollbackTransaction();
|
||||||
|
|
@ -341,7 +348,7 @@ class R2dbcTransactionManagerUnitTests {
|
||||||
.verifyComplete();
|
.verifyComplete();
|
||||||
|
|
||||||
verify(connectionMock).isAutoCommit();
|
verify(connectionMock).isAutoCommit();
|
||||||
verify(connectionMock).beginTransaction();
|
verify(connectionMock).beginTransaction(any(io.r2dbc.spi.TransactionDefinition.class));
|
||||||
verify(connectionMock).rollbackTransaction();
|
verify(connectionMock).rollbackTransaction();
|
||||||
verify(connectionMock).close();
|
verify(connectionMock).close();
|
||||||
verifyNoMoreInteractions(connectionMock);
|
verifyNoMoreInteractions(connectionMock);
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import io.r2dbc.spi.ConnectionFactory;
|
||||||
import io.r2dbc.spi.Wrapped;
|
import io.r2dbc.spi.Wrapped;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.ArgumentMatchers;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
import reactor.test.StepVerifier;
|
import reactor.test.StepVerifier;
|
||||||
|
|
||||||
|
|
@ -130,7 +131,7 @@ class TransactionAwareConnectionFactoryProxyUnitTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldEmitBoundConnection() {
|
void shouldEmitBoundConnection() {
|
||||||
when(connectionMock1.beginTransaction()).thenReturn(Mono.empty());
|
when(connectionMock1.beginTransaction(ArgumentMatchers.any())).thenReturn(Mono.empty());
|
||||||
when(connectionMock1.commitTransaction()).thenReturn(Mono.empty());
|
when(connectionMock1.commitTransaction()).thenReturn(Mono.empty());
|
||||||
when(connectionMock1.close()).thenReturn(Mono.empty());
|
when(connectionMock1.close()).thenReturn(Mono.empty());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2020 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -81,7 +81,7 @@ public abstract class AbstractDatabaseClientIntegrationTests {
|
||||||
.bindNull("manual", Integer.class)
|
.bindNull("manual", Integer.class)
|
||||||
.fetch().rowsUpdated()
|
.fetch().rowsUpdated()
|
||||||
.as(StepVerifier::create)
|
.as(StepVerifier::create)
|
||||||
.expectNext(1)
|
.expectNext(1L)
|
||||||
.verifyComplete();
|
.verifyComplete();
|
||||||
|
|
||||||
databaseClient.sql("SELECT id FROM legoset")
|
databaseClient.sql("SELECT id FROM legoset")
|
||||||
|
|
@ -123,7 +123,7 @@ public abstract class AbstractDatabaseClientIntegrationTests {
|
||||||
.bindNull("manual", Integer.class)
|
.bindNull("manual", Integer.class)
|
||||||
.fetch().rowsUpdated()
|
.fetch().rowsUpdated()
|
||||||
.as(StepVerifier::create)
|
.as(StepVerifier::create)
|
||||||
.expectNext(1)
|
.expectNext(1L)
|
||||||
.verifyComplete();
|
.verifyComplete();
|
||||||
|
|
||||||
databaseClient.sql("SELECT id FROM legoset")
|
databaseClient.sql("SELECT id FROM legoset")
|
||||||
|
|
|
||||||
|
|
@ -109,15 +109,15 @@ public abstract class AbstractTransactionalDatabaseClientIntegrationTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void executeInsertInTransaction() {
|
public void executeInsertInTransaction() {
|
||||||
Flux<Integer> integerFlux = databaseClient
|
Flux<Long> longFlux = databaseClient
|
||||||
.sql(getInsertIntoLegosetStatement())
|
.sql(getInsertIntoLegosetStatement())
|
||||||
.bind(0, 42055)
|
.bind(0, 42055)
|
||||||
.bind(1, "SCHAUFELRADBAGGER")
|
.bind(1, "SCHAUFELRADBAGGER")
|
||||||
.bindNull(2, Integer.class)
|
.bindNull(2, Integer.class)
|
||||||
.fetch().rowsUpdated().flux().as(rxtx::transactional);
|
.fetch().rowsUpdated().flux().as(rxtx::transactional);
|
||||||
|
|
||||||
integerFlux.as(StepVerifier::create)
|
longFlux.as(StepVerifier::create)
|
||||||
.expectNext(1)
|
.expectNext(1L)
|
||||||
.verifyComplete();
|
.verifyComplete();
|
||||||
|
|
||||||
databaseClient
|
databaseClient
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import java.util.Arrays;
|
||||||
|
|
||||||
import io.r2dbc.spi.Connection;
|
import io.r2dbc.spi.Connection;
|
||||||
import io.r2dbc.spi.ConnectionFactory;
|
import io.r2dbc.spi.ConnectionFactory;
|
||||||
|
import io.r2dbc.spi.Parameters;
|
||||||
import io.r2dbc.spi.Result;
|
import io.r2dbc.spi.Result;
|
||||||
import io.r2dbc.spi.Statement;
|
import io.r2dbc.spi.Statement;
|
||||||
import io.r2dbc.spi.test.MockColumnMetadata;
|
import io.r2dbc.spi.test.MockColumnMetadata;
|
||||||
|
|
@ -137,12 +138,12 @@ class DefaultDatabaseClientUnitTests {
|
||||||
databaseClient.sql("SELECT * FROM table WHERE key = $1").bindNull(0,
|
databaseClient.sql("SELECT * FROM table WHERE key = $1").bindNull(0,
|
||||||
String.class).then().as(StepVerifier::create).verifyComplete();
|
String.class).then().as(StepVerifier::create).verifyComplete();
|
||||||
|
|
||||||
verify(statement).bindNull(0, String.class);
|
verify(statement).bind(0, Parameters.in(String.class));
|
||||||
|
|
||||||
databaseClient.sql("SELECT * FROM table WHERE key = $1").bindNull("$1",
|
databaseClient.sql("SELECT * FROM table WHERE key = $1").bindNull("$1",
|
||||||
String.class).then().as(StepVerifier::create).verifyComplete();
|
String.class).then().as(StepVerifier::create).verifyComplete();
|
||||||
|
|
||||||
verify(statement).bindNull("$1", String.class);
|
verify(statement).bind("$1", Parameters.in(String.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -155,13 +156,13 @@ class DefaultDatabaseClientUnitTests {
|
||||||
Parameter.empty(String.class)).then().as(
|
Parameter.empty(String.class)).then().as(
|
||||||
StepVerifier::create).verifyComplete();
|
StepVerifier::create).verifyComplete();
|
||||||
|
|
||||||
verify(statement).bindNull(0, String.class);
|
verify(statement).bind(0, Parameters.in(String.class));
|
||||||
|
|
||||||
databaseClient.sql("SELECT * FROM table WHERE key = $1").bind("$1",
|
databaseClient.sql("SELECT * FROM table WHERE key = $1").bind("$1",
|
||||||
Parameter.empty(String.class)).then().as(
|
Parameter.empty(String.class)).then().as(
|
||||||
StepVerifier::create).verifyComplete();
|
StepVerifier::create).verifyComplete();
|
||||||
|
|
||||||
verify(statement).bindNull("$1", String.class);
|
verify(statement).bind("$1", Parameters.in(String.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -173,7 +174,7 @@ class DefaultDatabaseClientUnitTests {
|
||||||
databaseClient.sql("SELECT * FROM table WHERE key = :key").bindNull("key",
|
databaseClient.sql("SELECT * FROM table WHERE key = :key").bindNull("key",
|
||||||
String.class).then().as(StepVerifier::create).verifyComplete();
|
String.class).then().as(StepVerifier::create).verifyComplete();
|
||||||
|
|
||||||
verify(statement).bindNull(0, String.class);
|
verify(statement).bind(0, Parameters.in(String.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -204,12 +205,12 @@ class DefaultDatabaseClientUnitTests {
|
||||||
databaseClient.sql("SELECT * FROM table WHERE key = $1").bind(0,
|
databaseClient.sql("SELECT * FROM table WHERE key = $1").bind(0,
|
||||||
Parameter.from("foo")).then().as(StepVerifier::create).verifyComplete();
|
Parameter.from("foo")).then().as(StepVerifier::create).verifyComplete();
|
||||||
|
|
||||||
verify(statement).bind(0, "foo");
|
verify(statement).bind(0, Parameters.in("foo"));
|
||||||
|
|
||||||
databaseClient.sql("SELECT * FROM table WHERE key = $1").bind("$1",
|
databaseClient.sql("SELECT * FROM table WHERE key = $1").bind("$1",
|
||||||
"foo").then().as(StepVerifier::create).verifyComplete();
|
"foo").then().as(StepVerifier::create).verifyComplete();
|
||||||
|
|
||||||
verify(statement).bind("$1", "foo");
|
verify(statement).bind("$1", Parameters.in("foo"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -221,7 +222,7 @@ class DefaultDatabaseClientUnitTests {
|
||||||
databaseClient.sql("SELECT * FROM table WHERE key = :key").bind("key",
|
databaseClient.sql("SELECT * FROM table WHERE key = :key").bind("key",
|
||||||
"foo").then().as(StepVerifier::create).verifyComplete();
|
"foo").then().as(StepVerifier::create).verifyComplete();
|
||||||
|
|
||||||
verify(statement).bind(0, "foo");
|
verify(statement).bind(0, Parameters.in("foo"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2021 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -22,6 +22,7 @@ import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import io.r2dbc.spi.Parameters;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import org.springframework.r2dbc.core.binding.BindMarkersFactory;
|
import org.springframework.r2dbc.core.binding.BindMarkersFactory;
|
||||||
|
|
@ -294,7 +295,7 @@ public class NamedParameterUtilsUnitTests {
|
||||||
|
|
||||||
PreparedOperation<String> operation = NamedParameterUtils.substituteNamedParameters(
|
PreparedOperation<String> operation = NamedParameterUtils.substituteNamedParameters(
|
||||||
sql, factory, new MapBindParameterSource(
|
sql, factory, new MapBindParameterSource(
|
||||||
Collections.singletonMap("id", Parameter.from("foo"))));
|
Collections.singletonMap("id", Parameters.in("foo"))));
|
||||||
|
|
||||||
assertThat(operation.toQuery()).isEqualTo(
|
assertThat(operation.toQuery()).isEqualTo(
|
||||||
"SELECT * FROM person where name = $0 or lastname = $0");
|
"SELECT * FROM person where name = $0 or lastname = $0");
|
||||||
|
|
@ -307,7 +308,7 @@ public class NamedParameterUtilsUnitTests {
|
||||||
@Override
|
@Override
|
||||||
public void bind(int index, Object value) {
|
public void bind(int index, Object value) {
|
||||||
assertThat(index).isEqualTo(0);
|
assertThat(index).isEqualTo(0);
|
||||||
assertThat(value).isEqualTo("foo");
|
assertThat(value).isEqualTo(Parameters.in("foo"));
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void bindNull(String identifier, Class<?> type) {
|
public void bindNull(String identifier, Class<?> type) {
|
||||||
|
|
@ -330,7 +331,7 @@ public class NamedParameterUtilsUnitTests {
|
||||||
|
|
||||||
PreparedOperation<String> operation = NamedParameterUtils.substituteNamedParameters(
|
PreparedOperation<String> operation = NamedParameterUtils.substituteNamedParameters(
|
||||||
sql, factory, new MapBindParameterSource(Collections.singletonMap("ids",
|
sql, factory, new MapBindParameterSource(Collections.singletonMap("ids",
|
||||||
Parameter.from(Arrays.asList("foo", "bar", "baz")))));
|
Parameters.in(Arrays.asList("foo", "bar", "baz")))));
|
||||||
|
|
||||||
assertThat(operation.toQuery()).isEqualTo(
|
assertThat(operation.toQuery()).isEqualTo(
|
||||||
"SELECT * FROM person where name IN ($0, $1, $2) or lastname IN ($0, $1, $2)");
|
"SELECT * FROM person where name IN ($0, $1, $2) or lastname IN ($0, $1, $2)");
|
||||||
|
|
@ -369,7 +370,7 @@ public class NamedParameterUtilsUnitTests {
|
||||||
|
|
||||||
PreparedOperation<String> operation = NamedParameterUtils.substituteNamedParameters(
|
PreparedOperation<String> operation = NamedParameterUtils.substituteNamedParameters(
|
||||||
sql, factory, new MapBindParameterSource(
|
sql, factory, new MapBindParameterSource(
|
||||||
Collections.singletonMap("id", Parameter.from("foo"))));
|
Collections.singletonMap("id", Parameters.in("foo"))));
|
||||||
|
|
||||||
assertThat(operation.toQuery()).isEqualTo(
|
assertThat(operation.toQuery()).isEqualTo(
|
||||||
"SELECT * FROM person where name = ? or lastname = ?");
|
"SELECT * FROM person where name = ? or lastname = ?");
|
||||||
|
|
@ -395,7 +396,7 @@ public class NamedParameterUtilsUnitTests {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
assertThat(bindValues).hasSize(2).containsEntry(0, "foo").containsEntry(1, "foo");
|
assertThat(bindValues).hasSize(2).containsEntry(0, Parameters.in("foo")).containsEntry(1, Parameters.in("foo"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -406,7 +407,7 @@ public class NamedParameterUtilsUnitTests {
|
||||||
|
|
||||||
PreparedOperation<String> operation = NamedParameterUtils.substituteNamedParameters(
|
PreparedOperation<String> operation = NamedParameterUtils.substituteNamedParameters(
|
||||||
sql, factory, new MapBindParameterSource(
|
sql, factory, new MapBindParameterSource(
|
||||||
Collections.singletonMap("id", Parameter.empty(String.class))));
|
Collections.singletonMap("id", Parameters.in(String.class))));
|
||||||
|
|
||||||
assertThat(operation.toQuery()).isEqualTo(
|
assertThat(operation.toQuery()).isEqualTo(
|
||||||
"SELECT * FROM person where name = $0 or lastname = $0");
|
"SELECT * FROM person where name = $0 or lastname = $0");
|
||||||
|
|
@ -418,7 +419,8 @@ public class NamedParameterUtilsUnitTests {
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void bind(int index, Object value) {
|
public void bind(int index, Object value) {
|
||||||
throw new UnsupportedOperationException();
|
assertThat(index).isEqualTo(0);
|
||||||
|
assertThat(value).isEqualTo(Parameters.in(String.class));
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void bindNull(String identifier, Class<?> type) {
|
public void bindNull(String identifier, Class<?> type) {
|
||||||
|
|
@ -426,8 +428,7 @@ public class NamedParameterUtilsUnitTests {
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void bindNull(int index, Class<?> type) {
|
public void bindNull(int index, Class<?> type) {
|
||||||
assertThat(index).isEqualTo(0);
|
throw new UnsupportedOperationException();
|
||||||
assertThat(type).isEqualTo(String.class);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2020 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -19,6 +19,7 @@ package org.springframework.r2dbc.core
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import io.mockk.verify
|
import io.mockk.verify
|
||||||
|
import io.r2dbc.spi.Parameters
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import reactor.core.publisher.Mono
|
import reactor.core.publisher.Mono
|
||||||
|
|
@ -42,7 +43,7 @@ class DatabaseClientExtensionsTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
verify {
|
verify {
|
||||||
spec.bind(0, Parameter.fromOrEmpty("foo", String::class.java))
|
spec.bind(0, Parameters.`in`("foo"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,7 +57,7 @@ class DatabaseClientExtensionsTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
verify {
|
verify {
|
||||||
spec.bind(0, Parameter.empty(String::class.java))
|
spec.bind(0, Parameters.`in`(String::class.java))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,7 +71,7 @@ class DatabaseClientExtensionsTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
verify {
|
verify {
|
||||||
spec.bind("field", Parameter.fromOrEmpty("foo", String::class.java))
|
spec.bind("field", Parameters.`in`("foo"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -84,7 +85,7 @@ class DatabaseClientExtensionsTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
verify {
|
verify {
|
||||||
spec.bind("field", Parameter.empty(String::class.java))
|
spec.bind("field", Parameters.`in`(String::class.java))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue