#468 - Correctly map result of exists queries.

We now map results of exists queries to a boolean flag to ensure proper decoding. Previously, results were attempted to be mapped onto a primitive type which failed as there's no converter registered for Row to Boolean.
This commit is contained in:
Mark Paluch
2020-09-21 15:17:45 +02:00
parent f0b30f0ca0
commit d3823947aa
5 changed files with 101 additions and 7 deletions

View File

@@ -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<Object> 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}
*

View File

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

View File

@@ -41,7 +41,7 @@ import org.springframework.util.ClassUtils;
*/
interface R2dbcQueryExecution {
Publisher<?> execute(FetchSpec<?> query, Class<?> type, SqlIdentifier tableName);
Publisher<?> execute(FetchSpec<Object> 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<Object> execute(FetchSpec<Object> query, Class<?> type, SqlIdentifier tableName) {
return (Publisher<Object>) this.converter.convert(this.delegate.execute(query, type, tableName));
}
}

View File

@@ -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<R2dbcSpELExpressionEvaluator> getSpelEvaluator(RelationalParameterAccessor accessor) {
return evaluationContextProvider

View File

@@ -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<Integer> deleteAllAndReturnCount();
Mono<Integer> countByNameContains(String namePart);
Mono<Boolean> existsByName(String name);
}
@Getter