Skip to content

Commit d382394

Browse files
committed
#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.
1 parent f0b30f0 commit d382394

File tree

5 files changed

+101
-7
lines changed

5 files changed

+101
-7
lines changed

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

+32-4
Original file line numberDiff line numberDiff line change
@@ -89,17 +89,21 @@ public Object execute(Object[] parameters) {
8989
return createQuery(parameterAccessor).flatMapMany(it -> executeQuery(parameterAccessor, it));
9090
}
9191

92+
@SuppressWarnings({ "unchecked", "rawtypes" })
9293
private Publisher<?> executeQuery(RelationalParameterAccessor parameterAccessor, BindableQuery it) {
9394

9495
ResultProcessor processor = method.getResultProcessor().withDynamicProjection(parameterAccessor);
9596
DatabaseClient.GenericExecuteSpec boundQuery = it.bind(databaseClient.sql(it));
9697

97-
FetchSpec<?> fetchSpec;
98-
if (requiresMapping()) {
99-
EntityRowMapper<?> rowMapper = new EntityRowMapper<>(resolveResultType(processor), converter);
98+
FetchSpec<Object> fetchSpec;
99+
100+
if (isExistsQuery()) {
101+
fetchSpec = (FetchSpec) boundQuery.map(row -> true);
102+
} else if (requiresMapping()) {
103+
EntityRowMapper rowMapper = new EntityRowMapper<>(resolveResultType(processor), converter);
100104
fetchSpec = new FetchSpecAdapter<>(boundQuery.map(rowMapper));
101105
} else {
102-
fetchSpec = boundQuery.fetch();
106+
fetchSpec = (FetchSpec) boundQuery.fetch();
103107
}
104108

105109
SqlIdentifier tableName = method.getEntityInformation().getTableName();
@@ -143,6 +147,14 @@ private R2dbcQueryExecution getExecutionToWrap(ReturnedType returnedType) {
143147
return (q, t, c) -> q.rowsUpdated();
144148
}
145149

150+
if (isCountQuery()) {
151+
return (q, t, c) -> q.first().defaultIfEmpty(0L);
152+
}
153+
154+
if (isExistsQuery()) {
155+
return (q, t, c) -> q.first().defaultIfEmpty(false);
156+
}
157+
146158
if (method.isCollectionQuery()) {
147159
return (q, t, c) -> q.all();
148160
}
@@ -158,6 +170,22 @@ private R2dbcQueryExecution getExecutionToWrap(ReturnedType returnedType) {
158170
*/
159171
protected abstract boolean isModifyingQuery();
160172

173+
/**
174+
* Returns whether the query should get a count projection applied.
175+
*
176+
* @return
177+
* @since 1.2
178+
*/
179+
protected abstract boolean isCountQuery();
180+
181+
/**
182+
* Returns whether the query should get an exists projection applied.
183+
*
184+
* @return
185+
* @since 1.2
186+
*/
187+
protected abstract boolean isExistsQuery();
188+
161189
/**
162190
* Creates a {@link BindableQuery} instance using the given {@link ParameterAccessor}
163191
*

src/main/java/org/springframework/data/r2dbc/repository/query/PartTreeR2dbcQuery.java

+18
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,24 @@ protected boolean isModifyingQuery() {
8282
return this.tree.isDelete();
8383
}
8484

85+
/*
86+
* (non-Javadoc)
87+
* @see org.springframework.data.r2dbc.repository.query.AbstractR2dbcQuery#isCountQuery()
88+
*/
89+
@Override
90+
protected boolean isCountQuery() {
91+
return this.tree.isCountProjection();
92+
}
93+
94+
/*
95+
* (non-Javadoc)
96+
* @see org.springframework.data.r2dbc.repository.query.AbstractR2dbcQuery#isExistsQuery()
97+
*/
98+
@Override
99+
protected boolean isExistsQuery() {
100+
return this.tree.isExistsProjection();
101+
}
102+
85103
/*
86104
* (non-Javadoc)
87105
* @see org.springframework.data.r2dbc.repository.query.AbstractR2dbcQuery#createQuery(org.springframework.data.relational.repository.query.RelationalParameterAccessor)

src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcQueryExecution.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
*/
4242
interface R2dbcQueryExecution {
4343

44-
Publisher<?> execute(FetchSpec<?> query, Class<?> type, SqlIdentifier tableName);
44+
Publisher<?> execute(FetchSpec<Object> query, Class<?> type, SqlIdentifier tableName);
4545

4646
/**
4747
* An {@link R2dbcQueryExecution} that wraps the results of the given delegate with the given result processing.
@@ -60,8 +60,8 @@ final class ResultProcessingExecution implements R2dbcQueryExecution {
6060
* @see org.springframework.data.r2dbc.repository.query.R2dbcQueryExecution#execute(org.springframework.data.r2dbc.function.FetchSpec, java.lang.Class, java.lang.String)
6161
*/
6262
@Override
63-
public Publisher<?> execute(FetchSpec<?> query, Class<?> type, SqlIdentifier tableName) {
64-
return (Publisher<?>) this.converter.convert(this.delegate.execute(query, type, tableName));
63+
public Publisher<Object> execute(FetchSpec<Object> query, Class<?> type, SqlIdentifier tableName) {
64+
return (Publisher<Object>) this.converter.convert(this.delegate.execute(query, type, tableName));
6565
}
6666
}
6767

src/main/java/org/springframework/data/r2dbc/repository/query/StringBasedR2dbcQuery.java

+19
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,24 @@ protected boolean isModifyingQuery() {
112112
return getQueryMethod().isModifyingQuery();
113113
}
114114

115+
/*
116+
* (non-Javadoc)
117+
* @see org.springframework.data.r2dbc.repository.query.AbstractR2dbcQuery#isCountQuery()
118+
*/
119+
@Override
120+
protected boolean isCountQuery() {
121+
return false;
122+
}
123+
124+
/*
125+
* (non-Javadoc)
126+
* @see org.springframework.data.r2dbc.repository.query.AbstractR2dbcQuery#isExistsQuery()
127+
*/
128+
@Override
129+
protected boolean isExistsQuery() {
130+
return false;
131+
}
132+
115133
/*
116134
* (non-Javadoc)
117135
* @see org.springframework.data.r2dbc.repository.query.AbstractR2dbcQuery#createQuery(org.springframework.data.relational.repository.query.RelationalParameterAccessor)
@@ -133,6 +151,7 @@ public String get() {
133151
});
134152
}
135153

154+
136155
private Mono<R2dbcSpELExpressionEvaluator> getSpelEvaluator(RelationalParameterAccessor accessor) {
137156

138157
return evaluationContextProvider

src/test/java/org/springframework/data/r2dbc/repository/AbstractR2dbcRepositoryIntegrationTests.java

+29
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,33 @@ void derivedQueryWithCountProjection() {
296296
.verifyComplete();
297297
}
298298

299+
@Test // gh-363
300+
void derivedQueryWithCount() {
301+
302+
shouldInsertNewItems();
303+
304+
repository.countByNameContains("SCH") //
305+
.as(StepVerifier::create) //
306+
.assertNext(i -> assertThat(i).isEqualTo(2)) //
307+
.verifyComplete();
308+
}
309+
310+
@Test // gh-468
311+
void derivedQueryWithExists() {
312+
313+
shouldInsertNewItems();
314+
315+
repository.existsByName("ABS") //
316+
.as(StepVerifier::create) //
317+
.expectNext(Boolean.FALSE) //
318+
.verifyComplete();
319+
320+
repository.existsByName("SCHAUFELRADBAGGER") //
321+
.as(StepVerifier::create) //
322+
.expectNext(true) //
323+
.verifyComplete();
324+
}
325+
299326
@Test // gh-421
300327
void shouldDeleteAllAndReturnCount() {
301328

@@ -345,6 +372,8 @@ interface LegoSetRepository extends ReactiveCrudRepository<LegoSet, Integer> {
345372
Mono<Integer> deleteAllAndReturnCount();
346373

347374
Mono<Integer> countByNameContains(String namePart);
375+
376+
Mono<Boolean> existsByName(String name);
348377
}
349378

350379
@Getter

0 commit comments

Comments
 (0)