diff --git a/src/main/java/org/springframework/data/r2dbc/repository/support/ReactiveFluentQuerySupport.java b/src/main/java/org/springframework/data/r2dbc/repository/support/ReactiveFluentQuerySupport.java
new file mode 100644
index 00000000..2836cbfb
--- /dev/null
+++ b/src/main/java/org/springframework/data/r2dbc/repository/support/ReactiveFluentQuerySupport.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 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.repository.support;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.springframework.data.domain.Sort;
+import org.springframework.data.repository.query.FluentQuery;
+import org.springframework.util.Assert;
+
+/**
+ * Support class for {@link org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery} implementations.
+ *
+ * @author Mark Paluch
+ * @since 1.4
+ */
+abstract class ReactiveFluentQuerySupport
implements FluentQuery.ReactiveFluentQuery {
+
+ private final P predicate;
+ private final Sort sort;
+ private final Class resultType;
+ private final List fieldsToInclude;
+
+ ReactiveFluentQuerySupport(P predicate, Sort sort, Class resultType, List fieldsToInclude) {
+ this.predicate = predicate;
+ this.sort = sort;
+ this.resultType = resultType;
+ this.fieldsToInclude = fieldsToInclude;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery#sortBy(org.springframework.data.domain.Sort)
+ */
+ @Override
+ public ReactiveFluentQuery sortBy(Sort sort) {
+
+ Assert.notNull(sort, "Sort must not be null!");
+
+ return create(predicate, sort, resultType, fieldsToInclude);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery#as(java.lang.Class)
+ */
+ @Override
+ public ReactiveFluentQuery as(Class projection) {
+
+ Assert.notNull(projection, "Projection target type must not be null!");
+
+ return create(predicate, sort, projection, fieldsToInclude);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery#project(java.util.Collection)
+ */
+ @Override
+ public ReactiveFluentQuery project(Collection properties) {
+
+ Assert.notNull(properties, "Projection properties must not be null!");
+
+ return create(predicate, sort, resultType, new ArrayList<>(properties));
+ }
+
+ protected abstract ReactiveFluentQuerySupport create(P predicate, Sort sort, Class resultType,
+ List fieldsToInclude);
+
+ P getPredicate() {
+ return predicate;
+ }
+
+ Sort getSort() {
+ return sort;
+ }
+
+ Class getResultType() {
+ return resultType;
+ }
+
+ List getFieldsToInclude() {
+ return fieldsToInclude;
+ }
+}
diff --git a/src/main/java/org/springframework/data/r2dbc/repository/support/ReactivePageableExecutionUtils.java b/src/main/java/org/springframework/data/r2dbc/repository/support/ReactivePageableExecutionUtils.java
new file mode 100644
index 00000000..dce0d9a4
--- /dev/null
+++ b/src/main/java/org/springframework/data/r2dbc/repository/support/ReactivePageableExecutionUtils.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 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.repository.support;
+
+import reactor.core.publisher.Mono;
+
+import java.util.List;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.Pageable;
+import org.springframework.util.Assert;
+
+/**
+ * Support for query execution using {@link Pageable}. Using {@link ReactivePageableExecutionUtils} assumes that data
+ * queries are cheaper than {@code COUNT} queries and so some cases can take advantage of optimizations.
+ *
+ * @author Mark Paluch
+ * @since 1.4
+ */
+abstract class ReactivePageableExecutionUtils {
+
+ private ReactivePageableExecutionUtils() {}
+
+ /**
+ * Constructs a {@link Page} based on the given {@code content}, {@link Pageable} and {@link Mono} applying
+ * optimizations. The construction of {@link Page} omits a count query if the total can be determined based on the
+ * result size and {@link Pageable}.
+ *
+ * @param content must not be {@literal null}.
+ * @param pageable must not be {@literal null}.
+ * @param totalSupplier must not be {@literal null}.
+ * @return the {@link Page}.
+ */
+ public static Mono> getPage(List content, Pageable pageable, Mono totalSupplier) {
+
+ Assert.notNull(content, "Content must not be null!");
+ Assert.notNull(pageable, "Pageable must not be null!");
+ Assert.notNull(totalSupplier, "TotalSupplier must not be null!");
+
+ if (pageable.isUnpaged() || pageable.getOffset() == 0) {
+
+ if (pageable.isUnpaged() || pageable.getPageSize() > content.size()) {
+ return Mono.just(new PageImpl<>(content, pageable, content.size()));
+ }
+
+ return totalSupplier.map(total -> new PageImpl<>(content, pageable, total));
+ }
+
+ if (content.size() != 0 && pageable.getPageSize() > content.size()) {
+ return Mono.just(new PageImpl<>(content, pageable, pageable.getOffset() + content.size()));
+ }
+
+ return totalSupplier.map(total -> new PageImpl<>(content, pageable, total));
+ }
+}
diff --git a/src/main/java/org/springframework/data/r2dbc/repository/support/SimpleR2dbcRepository.java b/src/main/java/org/springframework/data/r2dbc/repository/support/SimpleR2dbcRepository.java
index 21551626..670277e5 100644
--- a/src/main/java/org/springframework/data/r2dbc/repository/support/SimpleR2dbcRepository.java
+++ b/src/main/java/org/springframework/data/r2dbc/repository/support/SimpleR2dbcRepository.java
@@ -18,21 +18,28 @@ package org.springframework.data.r2dbc.repository.support;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
+import java.util.Collections;
import java.util.List;
+import java.util.function.Function;
+import java.util.function.UnaryOperator;
import org.reactivestreams.Publisher;
import org.springframework.data.domain.Example;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.r2dbc.convert.R2dbcConverter;
import org.springframework.data.r2dbc.core.R2dbcEntityOperations;
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
import org.springframework.data.r2dbc.core.ReactiveDataAccessStrategy;
+import org.springframework.data.r2dbc.core.ReactiveSelectOperation;
import org.springframework.data.r2dbc.repository.R2dbcRepository;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.core.query.Criteria;
import org.springframework.data.relational.core.query.Query;
import org.springframework.data.relational.repository.query.RelationalEntityInformation;
import org.springframework.data.relational.repository.query.RelationalExampleMapper;
+import org.springframework.data.repository.query.FluentQuery;
import org.springframework.data.repository.reactive.ReactiveSortingRepository;
import org.springframework.data.util.Lazy;
import org.springframework.data.util.Streamable;
@@ -432,6 +439,16 @@ public class SimpleR2dbcRepository implements R2dbcRepository {
return this.entityOperations.exists(query, example.getProbeType());
}
+ @Override
+ public > P findBy(Example example,
+ Function, P> queryFunction) {
+
+ Assert.notNull(example, "Sample must not be null!");
+ Assert.notNull(queryFunction, "Query function must not be null!");
+
+ return queryFunction.apply(new ReactiveFluentQueryByExample<>(example, example.getProbeType()));
+ }
+
private RelationalPersistentProperty getIdProperty() {
return this.idProperty.get();
}
@@ -439,4 +456,113 @@ public class SimpleR2dbcRepository implements R2dbcRepository {
private Query getIdQuery(Object id) {
return Query.query(Criteria.where(getIdProperty().getName()).is(id));
}
+
+ /**
+ * {@link org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery} using {@link Example}.
+ *
+ * @author Mark Paluch
+ * @since 1.4
+ */
+ class ReactiveFluentQueryByExample extends ReactiveFluentQuerySupport, T> {
+
+ ReactiveFluentQueryByExample(Example example, Class resultType) {
+ this(example, Sort.unsorted(), resultType, Collections.emptyList());
+ }
+
+ ReactiveFluentQueryByExample(Example example, Sort sort, Class resultType, List fieldsToInclude) {
+ super(example, sort, resultType, fieldsToInclude);
+ }
+
+ @Override
+ protected ReactiveFluentQueryByExample create(Example predicate, Sort sort, Class resultType,
+ List fieldsToInclude) {
+ return new ReactiveFluentQueryByExample<>(predicate, sort, resultType, fieldsToInclude);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery#one()
+ */
+ @Override
+ public Mono one() {
+ return createQuery().one();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery#first()
+ */
+ @Override
+ public Mono first() {
+ return createQuery().first();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery#all()
+ */
+ @Override
+ public Flux all() {
+ return createQuery().all();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery#page(org.springframework.data.domain.Pageable)
+ */
+ @Override
+ public Mono> page(Pageable pageable) {
+
+ Assert.notNull(pageable, "Pageable must not be null!");
+
+ Mono> items = createQuery(q -> q.with(pageable)).all().collectList();
+
+ return items.flatMap(content -> ReactivePageableExecutionUtils.getPage(content, pageable, this.count()));
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery#count()
+ */
+ @Override
+ public Mono count() {
+ return createQuery().count();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery#exists()
+ */
+ @Override
+ public Mono exists() {
+ return createQuery().exists();
+ }
+
+ private ReactiveSelectOperation.TerminatingSelect createQuery() {
+ return createQuery(UnaryOperator.identity());
+ }
+
+ @SuppressWarnings("unchecked")
+ private ReactiveSelectOperation.TerminatingSelect createQuery(UnaryOperator queryCustomizer) {
+
+ Query query = exampleMapper.getMappedExample(getPredicate());
+
+ if (getSort().isSorted()) {
+ query = query.sort(getSort());
+ }
+
+ if (!getFieldsToInclude().isEmpty()) {
+ query = query.columns(getFieldsToInclude().toArray(new String[0]));
+ }
+
+ query = queryCustomizer.apply(query);
+
+ ReactiveSelectOperation.ReactiveSelect select = entityOperations.select(getPredicate().getProbeType());
+
+ if (getResultType() != getPredicate().getProbeType()) {
+ return select.as(getResultType()).matching(query);
+ }
+ return (ReactiveSelectOperation.TerminatingSelect) select.matching(query);
+ }
+ }
}
diff --git a/src/test/java/org/springframework/data/r2dbc/repository/support/AbstractSimpleR2dbcRepositoryIntegrationTests.java b/src/test/java/org/springframework/data/r2dbc/repository/support/AbstractSimpleR2dbcRepositoryIntegrationTests.java
index 511e1b30..4ce470cd 100644
--- a/src/test/java/org/springframework/data/r2dbc/repository/support/AbstractSimpleR2dbcRepositoryIntegrationTests.java
+++ b/src/test/java/org/springframework/data/r2dbc/repository/support/AbstractSimpleR2dbcRepositoryIntegrationTests.java
@@ -38,10 +38,12 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
+import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Version;
import org.springframework.data.domain.Example;
+import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.r2dbc.convert.MappingR2dbcConverter;
import org.springframework.data.r2dbc.core.ReactiveDataAccessStrategy;
@@ -51,6 +53,7 @@ import org.springframework.data.relational.core.mapping.RelationalPersistentEnti
import org.springframework.data.relational.core.mapping.Table;
import org.springframework.data.relational.repository.query.RelationalEntityInformation;
import org.springframework.data.relational.repository.support.MappingRelationalEntityInformation;
+import org.springframework.data.repository.query.FluentQuery;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.r2dbc.core.DatabaseClient;
@@ -771,6 +774,214 @@ public abstract class AbstractSimpleR2dbcRepositoryIntegrationTests extends R2db
.verifyComplete();
}
+ @Test // GH-663
+ void findByShouldReturnFirstResult() {
+
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('FORSCHUNGSSCHIFF', 13)");
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('SCHAUFELRADBAGGER', 12)");
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('VOLTRON', 15)");
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('RALLYEAUTO', 14)");
+
+ LegoSet probe = new LegoSet();
+ probe.setName("FORSCHUNGSSCHIFF");
+
+ repository.findBy(Example.of(probe, matching().withIgnorePaths("id")), FluentQuery.ReactiveFluentQuery::first) //
+ .as(StepVerifier::create) //
+ .assertNext(actual -> {
+
+ assertThat(actual.getManual()).isEqualTo(13);
+ }).verifyComplete();
+ }
+
+ @Test // GH-663
+ void findByShouldReturnOneResult() {
+
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('FORSCHUNGSSCHIFF', 13)");
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('SCHAUFELRADBAGGER', 13)");
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('VOLTRON', 13)");
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('RALLYEAUTO', 14)");
+
+ LegoSet probe = new LegoSet();
+ probe.setName("FORSCHUNGSSCHIFF");
+
+ repository.findBy(Example.of(probe, matching().withIgnorePaths("id")), FluentQuery.ReactiveFluentQuery::one) //
+ .as(StepVerifier::create) //
+ .assertNext(actual -> {
+
+ assertThat(actual.getManual()).isEqualTo(13);
+ }).verifyComplete();
+
+ probe = new LegoSet();
+ probe.setManual(13);
+
+ repository.findBy(Example.of(probe, matching().withIgnorePaths("id")), FluentQuery.ReactiveFluentQuery::one) //
+ .as(StepVerifier::create) //
+ .verifyError(IncorrectResultSizeDataAccessException.class);
+ }
+
+ @Test // GH-663
+ void findByShouldReturnAll() {
+
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('FORSCHUNGSSCHIFF', 13)");
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('SCHAUFELRADBAGGER', 13)");
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('VOLTRON', 13)");
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('RALLYEAUTO', 14)");
+
+ LegoSet probe = new LegoSet();
+ probe.setManual(13);
+
+ repository.findBy(Example.of(probe, matching().withIgnorePaths("id")), FluentQuery.ReactiveFluentQuery::all) //
+ .as(StepVerifier::create) //
+ .expectNextCount(3) //
+ .verifyComplete();
+ }
+
+ @Test // GH-663
+ void findByShouldApplySortAll() {
+
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('FORSCHUNGSSCHIFF', 13)");
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('SCHAUFELRADBAGGER', 13)");
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('VOLTRON', 13)");
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('RALLYEAUTO', 14)");
+
+ LegoSet probe = new LegoSet();
+ probe.setManual(13);
+
+ repository.findBy(Example.of(probe, matching().withIgnorePaths("id")), it -> it.sortBy(Sort.by("name")).all())
+ .map(LegoSet::getName) //
+ .as(StepVerifier::create) //
+ .expectNext("FORSCHUNGSSCHIFF", "SCHAUFELRADBAGGER", "VOLTRON") //
+ .verifyComplete();
+
+ repository
+ .findBy(Example.of(probe, matching().withIgnorePaths("id")),
+ it -> it.sortBy(Sort.by(Sort.Direction.DESC, "name")).all())
+ .map(LegoSet::getName) //
+ .as(StepVerifier::create) //
+ .expectNext("VOLTRON", "SCHAUFELRADBAGGER", "FORSCHUNGSSCHIFF") //
+ .verifyComplete();
+ }
+
+ @Test // GH-663
+ void findByShouldApplyProjection() {
+
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('FORSCHUNGSSCHIFF', 13)");
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('SCHAUFELRADBAGGER', 13)");
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('VOLTRON', 13)");
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('RALLYEAUTO', 14)");
+
+ LegoSet probe = new LegoSet();
+ probe.setName("FORSCHUNGSSCHIFF");
+
+ repository.findBy(Example.of(probe, matching().withIgnorePaths("id")), it -> it.project("name").first()) //
+ .as(StepVerifier::create) //
+ .assertNext(it -> {
+
+ assertThat(it.getName()).isNotNull();
+ assertThat(it.getManual()).isNull();
+ }).verifyComplete();
+ }
+
+ @Test // GH-663
+ void findByShouldApplyProjectionAs() {
+
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('FORSCHUNGSSCHIFF', 13)");
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('SCHAUFELRADBAGGER', 13)");
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('VOLTRON', 13)");
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('RALLYEAUTO', 14)");
+
+ LegoSet probe = new LegoSet();
+ probe.setName("FORSCHUNGSSCHIFF");
+
+ repository.findBy(Example.of(probe, matching().withIgnorePaths("id")), it -> it.as(LegoSetProjection.class).first()) //
+ .as(StepVerifier::create) //
+ .assertNext(it -> {
+
+ assertThat(it.getName()).isEqualTo("FORSCHUNGSSCHIFF");
+ }).verifyComplete();
+ }
+
+ @Test // GH-663
+ void findByShouldApplyPagination() {
+
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('FORSCHUNGSSCHIFF', 13)");
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('SCHAUFELRADBAGGER', 13)");
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('VOLTRON', 13)");
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('RALLYEAUTO', 14)");
+
+ LegoSet probe = new LegoSet();
+ probe.setManual(13);
+
+ repository
+ .findBy(Example.of(probe, matching().withIgnorePaths("id")),
+ it -> it.page(PageRequest.of(0, 1, Sort.by("name")))) //
+ .as(StepVerifier::create) //
+ .assertNext(it -> {
+
+ assertThat(it.getTotalElements()).isEqualTo(3);
+ assertThat(it.getContent()).extracting(LegoSet::getName).containsOnly("FORSCHUNGSSCHIFF");
+ }).verifyComplete();
+
+ repository
+ .findBy(Example.of(probe, matching().withIgnorePaths("id")),
+ it -> it.page(PageRequest.of(1, 1, Sort.by("name")))) //
+ .as(StepVerifier::create) //
+ .assertNext(it -> {
+
+ assertThat(it.getTotalElements()).isEqualTo(3);
+ assertThat(it.getContent()).extracting(LegoSet::getName).containsOnly("SCHAUFELRADBAGGER");
+ }).verifyComplete();
+ }
+
+ @Test // GH-663
+ void findByShouldCount() {
+
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('FORSCHUNGSSCHIFF', 13)");
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('SCHAUFELRADBAGGER', 13)");
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('VOLTRON', 13)");
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('RALLYEAUTO', 14)");
+
+ LegoSet probe = new LegoSet();
+ probe.setManual(13);
+
+ repository.findBy(Example.of(probe, matching().withIgnorePaths("id")), FluentQuery.ReactiveFluentQuery::count) //
+ .as(StepVerifier::create) //
+ .expectNext(3L) //
+ .verifyComplete();
+
+ probe = new LegoSet();
+ probe.setManual(0);
+
+ repository.findBy(Example.of(probe, matching().withIgnorePaths("id")), FluentQuery.ReactiveFluentQuery::count) //
+ .as(StepVerifier::create) //
+ .expectNext(0L) //
+ .verifyComplete();
+ }
+
+ @Test // GH-663
+ void findByShouldReportExists() {
+
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('SCHAUFELRADBAGGER', 13)");
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('VOLTRON', 13)");
+ jdbc.execute("INSERT INTO legoset (name, manual) VALUES('RALLYEAUTO', 14)");
+
+ LegoSet probe = new LegoSet();
+ probe.setManual(13);
+
+ repository.findBy(Example.of(probe, matching().withIgnorePaths("id")), FluentQuery.ReactiveFluentQuery::exists) //
+ .as(StepVerifier::create) //
+ .expectNext(true) //
+ .verifyComplete();
+
+ probe = new LegoSet();
+ probe.setManual(0);
+
+ repository.findBy(Example.of(probe, matching().withIgnorePaths("id")), FluentQuery.ReactiveFluentQuery::exists) //
+ .as(StepVerifier::create) //
+ .expectNext(false) //
+ .verifyComplete();
+ }
+
@Data
@Table("legoset")
@AllArgsConstructor
@@ -782,6 +993,10 @@ public abstract class AbstractSimpleR2dbcRepositoryIntegrationTests extends R2db
Integer manual;
}
+ interface LegoSetProjection {
+ String getName();
+ }
+
@Data
@Table("legoset")
@AllArgsConstructor