#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:
@@ -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}
|
||||
*
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user