diff --git a/src/main/java/org/springframework/data/r2dbc/repository/query/AbstractR2dbcQuery.java b/src/main/java/org/springframework/data/r2dbc/repository/query/AbstractR2dbcQuery.java index 10aaeb851..e574a353d 100644 --- a/src/main/java/org/springframework/data/r2dbc/repository/query/AbstractR2dbcQuery.java +++ b/src/main/java/org/springframework/data/r2dbc/repository/query/AbstractR2dbcQuery.java @@ -89,17 +89,21 @@ public abstract class AbstractR2dbcQuery implements RepositoryQuery { return createQuery(parameterAccessor).flatMapMany(it -> executeQuery(parameterAccessor, it)); } + @SuppressWarnings({ "unchecked", "rawtypes" }) private Publisher executeQuery(RelationalParameterAccessor parameterAccessor, BindableQuery it) { ResultProcessor processor = method.getResultProcessor().withDynamicProjection(parameterAccessor); DatabaseClient.GenericExecuteSpec boundQuery = it.bind(databaseClient.sql(it)); - FetchSpec fetchSpec; - if (requiresMapping()) { - EntityRowMapper rowMapper = new EntityRowMapper<>(resolveResultType(processor), converter); + FetchSpec fetchSpec; + + if (isExistsQuery()) { + fetchSpec = (FetchSpec) boundQuery.map(row -> true); + } else if (requiresMapping()) { + EntityRowMapper rowMapper = new EntityRowMapper<>(resolveResultType(processor), converter); fetchSpec = new FetchSpecAdapter<>(boundQuery.map(rowMapper)); } else { - fetchSpec = boundQuery.fetch(); + fetchSpec = (FetchSpec) boundQuery.fetch(); } SqlIdentifier tableName = method.getEntityInformation().getTableName(); @@ -143,6 +147,14 @@ public abstract class AbstractR2dbcQuery implements RepositoryQuery { return (q, t, c) -> q.rowsUpdated(); } + if (isCountQuery()) { + return (q, t, c) -> q.first().defaultIfEmpty(0L); + } + + if (isExistsQuery()) { + return (q, t, c) -> q.first().defaultIfEmpty(false); + } + if (method.isCollectionQuery()) { return (q, t, c) -> q.all(); } @@ -158,6 +170,22 @@ public abstract class AbstractR2dbcQuery implements RepositoryQuery { */ protected abstract boolean isModifyingQuery(); + /** + * Returns whether the query should get a count projection applied. + * + * @return + * @since 1.2 + */ + protected abstract boolean isCountQuery(); + + /** + * Returns whether the query should get an exists projection applied. + * + * @return + * @since 1.2 + */ + protected abstract boolean isExistsQuery(); + /** * Creates a {@link BindableQuery} instance using the given {@link ParameterAccessor} * diff --git a/src/main/java/org/springframework/data/r2dbc/repository/query/PartTreeR2dbcQuery.java b/src/main/java/org/springframework/data/r2dbc/repository/query/PartTreeR2dbcQuery.java index 6319ef2c1..4762893b3 100644 --- a/src/main/java/org/springframework/data/r2dbc/repository/query/PartTreeR2dbcQuery.java +++ b/src/main/java/org/springframework/data/r2dbc/repository/query/PartTreeR2dbcQuery.java @@ -82,6 +82,24 @@ public class PartTreeR2dbcQuery extends AbstractR2dbcQuery { return this.tree.isDelete(); } + /* + * (non-Javadoc) + * @see org.springframework.data.r2dbc.repository.query.AbstractR2dbcQuery#isCountQuery() + */ + @Override + protected boolean isCountQuery() { + return this.tree.isCountProjection(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.r2dbc.repository.query.AbstractR2dbcQuery#isExistsQuery() + */ + @Override + protected boolean isExistsQuery() { + return this.tree.isExistsProjection(); + } + /* * (non-Javadoc) * @see org.springframework.data.r2dbc.repository.query.AbstractR2dbcQuery#createQuery(org.springframework.data.relational.repository.query.RelationalParameterAccessor) diff --git a/src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcQueryExecution.java b/src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcQueryExecution.java index 5067dcdb5..536755987 100644 --- a/src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcQueryExecution.java +++ b/src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcQueryExecution.java @@ -41,7 +41,7 @@ import org.springframework.util.ClassUtils; */ interface R2dbcQueryExecution { - Publisher execute(FetchSpec query, Class type, SqlIdentifier tableName); + Publisher execute(FetchSpec query, Class type, SqlIdentifier tableName); /** * An {@link R2dbcQueryExecution} that wraps the results of the given delegate with the given result processing. @@ -60,8 +60,8 @@ interface R2dbcQueryExecution { * @see org.springframework.data.r2dbc.repository.query.R2dbcQueryExecution#execute(org.springframework.data.r2dbc.function.FetchSpec, java.lang.Class, java.lang.String) */ @Override - public Publisher execute(FetchSpec query, Class type, SqlIdentifier tableName) { - return (Publisher) this.converter.convert(this.delegate.execute(query, type, tableName)); + public Publisher execute(FetchSpec query, Class type, SqlIdentifier tableName) { + return (Publisher) this.converter.convert(this.delegate.execute(query, type, tableName)); } } diff --git a/src/main/java/org/springframework/data/r2dbc/repository/query/StringBasedR2dbcQuery.java b/src/main/java/org/springframework/data/r2dbc/repository/query/StringBasedR2dbcQuery.java index 3dec9636a..2cd55e4b3 100644 --- a/src/main/java/org/springframework/data/r2dbc/repository/query/StringBasedR2dbcQuery.java +++ b/src/main/java/org/springframework/data/r2dbc/repository/query/StringBasedR2dbcQuery.java @@ -112,6 +112,24 @@ public class StringBasedR2dbcQuery extends AbstractR2dbcQuery { return getQueryMethod().isModifyingQuery(); } + /* + * (non-Javadoc) + * @see org.springframework.data.r2dbc.repository.query.AbstractR2dbcQuery#isCountQuery() + */ + @Override + protected boolean isCountQuery() { + return false; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.r2dbc.repository.query.AbstractR2dbcQuery#isExistsQuery() + */ + @Override + protected boolean isExistsQuery() { + return false; + } + /* * (non-Javadoc) * @see org.springframework.data.r2dbc.repository.query.AbstractR2dbcQuery#createQuery(org.springframework.data.relational.repository.query.RelationalParameterAccessor) @@ -133,6 +151,7 @@ public class StringBasedR2dbcQuery extends AbstractR2dbcQuery { }); } + private Mono getSpelEvaluator(RelationalParameterAccessor accessor) { return evaluationContextProvider diff --git a/src/test/java/org/springframework/data/r2dbc/repository/AbstractR2dbcRepositoryIntegrationTests.java b/src/test/java/org/springframework/data/r2dbc/repository/AbstractR2dbcRepositoryIntegrationTests.java index 31aa5b3b4..cf9eec871 100644 --- a/src/test/java/org/springframework/data/r2dbc/repository/AbstractR2dbcRepositoryIntegrationTests.java +++ b/src/test/java/org/springframework/data/r2dbc/repository/AbstractR2dbcRepositoryIntegrationTests.java @@ -296,6 +296,33 @@ public abstract class AbstractR2dbcRepositoryIntegrationTests extends R2dbcInteg .verifyComplete(); } + @Test // gh-363 + void derivedQueryWithCount() { + + shouldInsertNewItems(); + + repository.countByNameContains("SCH") // + .as(StepVerifier::create) // + .assertNext(i -> assertThat(i).isEqualTo(2)) // + .verifyComplete(); + } + + @Test // gh-468 + void derivedQueryWithExists() { + + shouldInsertNewItems(); + + repository.existsByName("ABS") // + .as(StepVerifier::create) // + .expectNext(Boolean.FALSE) // + .verifyComplete(); + + repository.existsByName("SCHAUFELRADBAGGER") // + .as(StepVerifier::create) // + .expectNext(true) // + .verifyComplete(); + } + @Test // gh-421 void shouldDeleteAllAndReturnCount() { @@ -345,6 +372,8 @@ public abstract class AbstractR2dbcRepositoryIntegrationTests extends R2dbcInteg Mono deleteAllAndReturnCount(); Mono countByNameContains(String namePart); + + Mono existsByName(String name); } @Getter