Skip to content

Commit 748d6c3

Browse files
ada-wafflesmp911de
authored andcommitted
#421 - Handle modifying query methods returning kotlin.Unit.
Added check for kotlin.Unit to AbstractR2dbcQuery#getExecutionToWrap. This case is essentially equivalent to a return type of Void, but the singleton Unit instance needs to be returned instead of discarding the result entirely. It was also necessary to add a check to R2dbcQueryMethod#getEntityInformation, as otherwise a PersistentEntity is created for Unit, which leads to a new instance being created via reflection down the pipeline (which is probably not a thing that should happen). Original pull request: #422.
1 parent 4f04c87 commit 748d6c3

File tree

5 files changed

+92
-3
lines changed

5 files changed

+92
-3
lines changed

src/main/asciidoc/reference/r2dbc-repositories.adoc

+1-1
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ Mono<Integer> setFixedFirstnameFor(String firstname, String lastname);
267267

268268
The result of a modifying query can be:
269269

270-
* `Void` to discard update count and await completion.
270+
* `Void` (or Kotlin `Unit`) to discard update count and await completion.
271271
* `Integer` or another numeric type emitting the affected rows count.
272272
* `Boolean` to emit whether at least one row was updated.
273273

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

+8-1
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@
1515
*/
1616
package org.springframework.data.r2dbc.repository.query;
1717

18+
import kotlin.Unit;
19+
1820
import reactor.core.publisher.Flux;
1921
import reactor.core.publisher.Mono;
2022

2123
import org.reactivestreams.Publisher;
22-
24+
import org.springframework.core.KotlinDetector;
2325
import org.springframework.data.mapping.model.EntityInstantiators;
2426
import org.springframework.data.r2dbc.convert.R2dbcConverter;
2527
import org.springframework.data.r2dbc.core.DatabaseClient;
@@ -41,6 +43,7 @@
4143
* Base class for reactive {@link RepositoryQuery} implementations for R2DBC.
4244
*
4345
* @author Mark Paluch
46+
* @author Stephen Cohen
4447
*/
4548
public abstract class AbstractR2dbcQuery implements RepositoryQuery {
4649

@@ -141,6 +144,10 @@ private R2dbcQueryExecution getExecutionToWrap(ReturnedType returnedType) {
141144
return (q, t, c) -> q.rowsUpdated().then();
142145
}
143146

147+
if (KotlinDetector.isKotlinPresent() && Unit.class.isAssignableFrom(returnedType.getReturnedType())) {
148+
return (q, t, c) -> q.rowsUpdated().thenReturn(Unit.INSTANCE);
149+
}
150+
144151
return (q, t, c) -> q.rowsUpdated();
145152
}
146153

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

+6-1
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,12 @@
1717

1818
import static org.springframework.data.repository.util.ClassUtils.*;
1919

20+
import kotlin.Unit;
21+
2022
import java.lang.reflect.Method;
2123
import java.util.Optional;
2224

25+
import org.springframework.core.KotlinDetector;
2326
import org.springframework.core.annotation.AnnotatedElementUtils;
2427
import org.springframework.dao.InvalidDataAccessApiUsageException;
2528
import org.springframework.data.domain.Page;
@@ -51,6 +54,7 @@
5154
* Reactive specific implementation of {@link QueryMethod}.
5255
*
5356
* @author Mark Paluch
57+
* @author Stephen Cohen
5458
*/
5559
public class R2dbcQueryMethod extends QueryMethod {
5660

@@ -164,7 +168,8 @@ public RelationalEntityMetadata<?> getEntityInformation() {
164168
Class<?> returnedObjectType = getReturnedObjectType();
165169
Class<?> domainClass = getDomainClass();
166170

167-
if (ClassUtils.isPrimitiveOrWrapper(returnedObjectType)) {
171+
if (ClassUtils.isPrimitiveOrWrapper(returnedObjectType)
172+
|| KotlinDetector.isKotlinPresent() && Unit.class.isAssignableFrom(returnedObjectType)) {
168173

169174
this.metadata = new SimpleRelationalEntityMetadata<>((Class<Object>) domainClass,
170175
mappingContext.getRequiredPersistentEntity(domainClass));

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

+64
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
import static org.assertj.core.api.Assertions.*;
1919

20+
import kotlin.Unit;
21+
2022
import io.r2dbc.spi.ConnectionFactory;
2123
import lombok.AllArgsConstructor;
2224
import lombok.Getter;
@@ -297,6 +299,55 @@ public void derivedQueryWithCountProjection() {
297299
.verifyComplete();
298300
}
299301

302+
@Test // gh-421
303+
public void shouldDeleteAllAndReturnCount() {
304+
305+
shouldInsertNewItems();
306+
307+
repository.deleteAllAndReturnCount() //
308+
.as(StepVerifier::create) //
309+
.expectNext(2) //
310+
.verifyComplete();
311+
312+
repository.findAll() //
313+
.as(StepVerifier::create) //
314+
.verifyComplete();
315+
}
316+
317+
@Test // gh-421
318+
public void shouldDeleteAndReturnSuccess() {
319+
320+
shouldInsertNewItems();
321+
322+
repository.deleteByManualAndReturnSuccess(12) //
323+
.as(StepVerifier::create) //
324+
.expectNext(true) //
325+
.verifyComplete();
326+
327+
repository.findAll() //
328+
.map(LegoSet::getManual) //
329+
.as(StepVerifier::create) //
330+
.expectNext(13) //
331+
.verifyComplete();
332+
}
333+
334+
@Test // gh-421
335+
public void shouldDeleteAndReturnKotlinUnit() {
336+
337+
shouldInsertNewItems();
338+
339+
repository.deleteByManualAndReturnKotlinUnit(12) //
340+
.as(StepVerifier::create) //
341+
.expectNext(Unit.INSTANCE) //
342+
.verifyComplete();
343+
344+
repository.findAll() //
345+
.map(LegoSet::getManual) //
346+
.as(StepVerifier::create) //
347+
.expectNext(13) //
348+
.verifyComplete();
349+
}
350+
300351
private Condition<? super Object> numberOf(int expected) {
301352
return new Condition<>(it -> {
302353
return it instanceof Number && ((Number) it).intValue() == expected;
@@ -322,9 +373,22 @@ interface LegoSetRepository extends ReactiveSortingRepository<LegoSet, Integer>
322373

323374
Mono<Void> deleteAllBy();
324375

376+
@Modifying
325377
@Query("DELETE from legoset where manual = :manual")
326378
Mono<Void> deleteAllByManual(int manual);
327379

380+
@Modifying
381+
@Query("DELETE from legoset")
382+
Mono<Integer> deleteAllAndReturnCount();
383+
384+
@Modifying
385+
@Query("DELETE from legoset where manual = :manual")
386+
Mono<Boolean> deleteByManualAndReturnSuccess(int manual);
387+
388+
@Modifying
389+
@Query("DELETE from legoset where manual = :manual")
390+
Mono<Unit> deleteByManualAndReturnKotlinUnit(int manual);
391+
328392
Mono<Integer> countByNameContains(String namePart);
329393
}
330394

src/test/java/org/springframework/data/r2dbc/repository/query/R2dbcQueryMethodUnitTests.java

+13
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
import static org.assertj.core.api.Assertions.*;
1919

20+
import kotlin.Unit;
21+
2022
import reactor.core.publisher.Mono;
2123

2224
import java.lang.annotation.Retention;
@@ -45,6 +47,7 @@
4547
* Unit test for {@link R2dbcQueryMethod}.
4648
*
4749
* @author Mark Paluch
50+
* @author Stephen Cohen
4851
*/
4952
public class R2dbcQueryMethodUnitTests {
5053

@@ -128,6 +131,14 @@ public void fallsBackToRepositoryDomainTypeIfMethodDoesNotReturnADomainType() th
128131
assertThat(method.getEntityInformation().getJavaType()).isAssignableFrom(Contact.class);
129132
}
130133

134+
@Test // gh-421
135+
public void fallsBackToRepositoryDomainTypeIfMethodReturnsKotlinUnit() throws Exception {
136+
137+
R2dbcQueryMethod method = queryMethod(PersonRepository.class, "deleteByFirstname", String.class);
138+
139+
assertThat(method.getEntityInformation().getJavaType()).isAssignableFrom(Contact.class);
140+
}
141+
131142
private R2dbcQueryMethod queryMethod(Class<?> repository, String name, Class<?>... parameters) throws Exception {
132143

133144
Method method = repository.getMethod(name, parameters);
@@ -144,6 +155,8 @@ interface PersonRepository extends Repository<Contact, Long> {
144155
Mono<Slice<Contact>> findMonoSliceByLastname(String lastname, Pageable pageRequest);
145156

146157
void deleteByUserName(String userName);
158+
159+
Unit deleteByFirstname(String firstname);
147160
}
148161

149162
interface SampleRepository extends Repository<Contact, Long> {

0 commit comments

Comments
 (0)