Browse Source

Adopt to Reactor 2022 changes.

Closes: #1285
pull/1298/head
Christoph Strobl 4 years ago
parent
commit
dd3a69c20d
No known key found for this signature in database
GPG Key ID: 8CC1AB53391458C8
  1. 10
      spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/AbstractR2dbcQuery.java
  2. 97
      spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcParameterAccessor.java
  3. 17
      spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/repository/query/PartTreeR2dbcQueryUnitTests.java

10
spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/AbstractR2dbcQuery.java

@ -18,14 +18,12 @@ package org.springframework.data.r2dbc.repository.query;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import org.reactivestreams.Publisher; import org.reactivestreams.Publisher;
import org.springframework.data.mapping.model.EntityInstantiators; import org.springframework.data.mapping.model.EntityInstantiators;
import org.springframework.data.r2dbc.convert.R2dbcConverter; import org.springframework.data.r2dbc.convert.R2dbcConverter;
import org.springframework.data.r2dbc.core.R2dbcEntityOperations; import org.springframework.data.r2dbc.core.R2dbcEntityOperations;
import org.springframework.data.r2dbc.repository.query.R2dbcQueryExecution.ResultProcessingConverter; import org.springframework.data.r2dbc.repository.query.R2dbcQueryExecution.ResultProcessingConverter;
import org.springframework.data.r2dbc.repository.query.R2dbcQueryExecution.ResultProcessingExecution; import org.springframework.data.r2dbc.repository.query.R2dbcQueryExecution.ResultProcessingExecution;
import org.springframework.data.relational.repository.query.RelationalParameterAccessor; import org.springframework.data.relational.repository.query.RelationalParameterAccessor;
import org.springframework.data.relational.repository.query.RelationalParametersParameterAccessor;
import org.springframework.data.repository.query.ParameterAccessor; import org.springframework.data.repository.query.ParameterAccessor;
import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ResultProcessor;
@ -41,6 +39,7 @@ import org.springframework.util.Assert;
* *
* @author Mark Paluch * @author Mark Paluch
* @author Stephen Cohen * @author Stephen Cohen
* @author Christoph Strobl
*/ */
public abstract class AbstractR2dbcQuery implements RepositoryQuery { public abstract class AbstractR2dbcQuery implements RepositoryQuery {
@ -83,13 +82,12 @@ public abstract class AbstractR2dbcQuery implements RepositoryQuery {
*/ */
public Object execute(Object[] parameters) { public Object execute(Object[] parameters) {
RelationalParameterAccessor parameterAccessor = new RelationalParametersParameterAccessor(method, parameters); Mono<R2dbcParameterAccessor> resolveParameters = new R2dbcParameterAccessor(method, parameters).resolveParameters();
return resolveParameters.flatMapMany(it -> createQuery(it).flatMapMany(foo -> executeQuery(it, foo)));
return createQuery(parameterAccessor).flatMapMany(it -> executeQuery(parameterAccessor, it));
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private Publisher<?> executeQuery(RelationalParameterAccessor parameterAccessor, PreparedOperation<?> operation) { private Publisher<?> executeQuery(R2dbcParameterAccessor parameterAccessor, PreparedOperation<?> operation) {
ResultProcessor processor = method.getResultProcessor().withDynamicProjection(parameterAccessor); ResultProcessor processor = method.getResultProcessor().withDynamicProjection(parameterAccessor);

97
spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcParameterAccessor.java

@ -15,12 +15,15 @@
*/ */
package org.springframework.data.r2dbc.repository.query; package org.springframework.data.r2dbc.repository.query;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import reactor.core.publisher.MonoProcessor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.data.relational.repository.query.RelationalParametersParameterAccessor; import org.springframework.data.relational.repository.query.RelationalParametersParameterAccessor;
import org.springframework.data.repository.util.ReactiveWrapperConverters; import org.springframework.data.repository.util.ReactiveWrapperConverters;
@ -31,11 +34,12 @@ import org.springframework.data.repository.util.ReactiveWrappers;
* to reactive parameter wrapper types upon creation. This class performs synchronization when accessing parameters. * to reactive parameter wrapper types upon creation. This class performs synchronization when accessing parameters.
* *
* @author Mark Paluch * @author Mark Paluch
* @author Christoph Strobl
*/ */
class R2dbcParameterAccessor extends RelationalParametersParameterAccessor { class R2dbcParameterAccessor extends RelationalParametersParameterAccessor {
private final Object[] values; private final Object[] values;
private final List<MonoProcessor<?>> subscriptions; private final R2dbcQueryMethod method;
/** /**
* Creates a new {@link R2dbcParameterAccessor}. * Creates a new {@link R2dbcParameterAccessor}.
@ -45,37 +49,7 @@ class R2dbcParameterAccessor extends RelationalParametersParameterAccessor {
super(method, values); super(method, values);
this.values = values; this.values = values;
this.subscriptions = new ArrayList<>(values.length); this.method = method;
for (int i = 0; i < values.length; i++) {
Object value = values[i];
if (value == null || !ReactiveWrappers.supports(value.getClass())) {
subscriptions.add(null);
continue;
}
if (ReactiveWrappers.isSingleValueType(value.getClass())) {
subscriptions.add(ReactiveWrapperConverters.toWrapper(value, Mono.class).toProcessor());
} else {
subscriptions.add(ReactiveWrapperConverters.toWrapper(value, Flux.class).collectList().toProcessor());
}
}
}
/* (non-Javadoc)
* @see org.springframework.data.repository.query.ParametersParameterAccessor#getValue(int)
*/
@SuppressWarnings("unchecked")
@Override
protected <T> T getValue(int index) {
if (subscriptions.get(index) != null) {
return (T) subscriptions.get(index).block();
}
return super.getValue(index);
} }
/* (non-Javadoc) /* (non-Javadoc)
@ -97,4 +71,61 @@ class R2dbcParameterAccessor extends RelationalParametersParameterAccessor {
public Object getBindableValue(int index) { public Object getBindableValue(int index) {
return getValue(getParameters().getBindableParameter(index).getIndex()); return getValue(getParameters().getBindableParameter(index).getIndex());
} }
/**
* Resolve parameters that were provided through reactive wrapper types. Flux is collected into a list, values from
* Mono's are used directly.
*
* @return
*/
@SuppressWarnings("unchecked")
public Mono<R2dbcParameterAccessor> resolveParameters() {
boolean hasReactiveWrapper = false;
for (Object value : values) {
if (value == null || !ReactiveWrappers.supports(value.getClass())) {
continue;
}
hasReactiveWrapper = true;
break;
}
if (!hasReactiveWrapper) {
return Mono.just(this);
}
Object[] resolved = new Object[values.length];
Map<Integer, Optional<?>> holder = new ConcurrentHashMap<>();
List<Publisher<?>> publishers = new ArrayList<>();
for (int i = 0; i < values.length; i++) {
Object value = resolved[i] = values[i];
if (value == null || !ReactiveWrappers.supports(value.getClass())) {
continue;
}
if (ReactiveWrappers.isSingleValueType(value.getClass())) {
int index = i;
publishers.add(ReactiveWrapperConverters.toWrapper(value, Mono.class) //
.map(Optional::of) //
.defaultIfEmpty(Optional.empty()) //
.doOnNext(it -> holder.put(index, (Optional<?>) it)));
} else {
int index = i;
publishers.add(ReactiveWrapperConverters.toWrapper(value, Flux.class) //
.collectList() //
.doOnNext(it -> holder.put(index, Optional.of(it))));
}
}
return Flux.merge(publishers).then().thenReturn(resolved).map(values -> {
holder.forEach((index, v) -> values[index] = v.orElse(null));
return new R2dbcParameterAccessor(method, values);
});
}
} }

17
spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/repository/query/PartTreeR2dbcQueryUnitTests.java

@ -734,6 +734,20 @@ class PartTreeR2dbcQueryUnitTests {
.where("users.first_name = $1 AND (users.age = $2) FOR SHARE OF users"); .where("users.first_name = $1 AND (users.age = $2) FOR SHARE OF users");
} }
@Test // GH-1285
void bindsParametersFromPublisher() throws Exception {
R2dbcQueryMethod queryMethod = getQueryMethod("findByFirstName", Mono.class);
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter, dataAccessStrategy);
R2dbcParameterAccessor accessor = new R2dbcParameterAccessor(queryMethod, new Object[] { Mono.just("John") });
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery, accessor.resolveParameters().block());
BindTarget bindTarget = mock(BindTarget.class);
preparedOperation.bindTo(bindTarget);
verify(bindTarget, times(1)).bind(0, "John");
}
private PreparedOperation<?> createQuery(R2dbcQueryMethod queryMethod, PartTreeR2dbcQuery r2dbcQuery, private PreparedOperation<?> createQuery(R2dbcQueryMethod queryMethod, PartTreeR2dbcQuery r2dbcQuery,
Object... parameters) { Object... parameters) {
return createQuery(r2dbcQuery, getAccessor(queryMethod, parameters)); return createQuery(r2dbcQuery, getAccessor(queryMethod, parameters));
@ -927,6 +941,9 @@ class PartTreeR2dbcQueryUnitTests {
Mono<Integer> deleteByFirstName(String firstName); Mono<Integer> deleteByFirstName(String firstName);
Mono<Long> countByFirstName(String firstName); Mono<Long> countByFirstName(String firstName);
Mono<User> findByFirstName(Mono<String> firstName);
} }
@Table("users") @Table("users")

Loading…
Cancel
Save