Skip to content

Commit 91b4660

Browse files
committed
spring-projects#282 - Add support of '(NOT) IN', 'TRUE/FALSE' and 'NEGATING_SIMPLE_PROPERTY' conditions
1 parent 7095113 commit 91b4660

File tree

2 files changed

+124
-17
lines changed

2 files changed

+124
-17
lines changed

Diff for: src/main/java/org/springframework/data/r2dbc/repository/query/ConditionFactory.java

+30-7
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,16 @@ public Condition createCondition(Part part) {
9393
case IS_NOT_NULL: {
9494
return Conditions.isNull(createPropertyPathExpression(part.getProperty())).not();
9595
}
96+
case IN: {
97+
Expression pathExpression = createPropertyPathExpression(part.getProperty());
98+
BindMarker bindMarker = createBindMarker(parameterMetadataProvider.next(part));
99+
return Conditions.in(pathExpression, bindMarker);
100+
}
101+
case NOT_IN: {
102+
Expression pathExpression = createPropertyPathExpression(part.getProperty());
103+
BindMarker bindMarker = createBindMarker(parameterMetadataProvider.next(part));
104+
return Conditions.in(pathExpression, bindMarker).not();
105+
}
96106
case STARTING_WITH:
97107
case ENDING_WITH:
98108
case CONTAINING:
@@ -106,6 +116,16 @@ public Condition createCondition(Part part) {
106116
BindMarker bindMarker = createBindMarker(parameterMetadataProvider.next(part));
107117
return NotLike.create(pathExpression, bindMarker);
108118
}
119+
case TRUE: {
120+
Expression pathExpression = createPropertyPathExpression(part.getProperty());
121+
// TODO: include factory method for '= TRUE' condition into spring-data-relational
122+
return Conditions.isEqual(pathExpression, SQL.literalOf((Object) "TRUE"));
123+
}
124+
case FALSE: {
125+
Expression pathExpression = createPropertyPathExpression(part.getProperty());
126+
// TODO: include factory method for '= FALSE' condition into spring-data-relational
127+
return Conditions.isEqual(pathExpression, SQL.literalOf((Object) "FALSE"));
128+
}
109129
case SIMPLE_PROPERTY: {
110130
Expression pathExpression = createPropertyPathExpression(part.getProperty());
111131
ParameterMetadata parameterMetadata = parameterMetadataProvider.next(part);
@@ -114,19 +134,22 @@ public Condition createCondition(Part part) {
114134
}
115135
return Conditions.isEqual(pathExpression, createBindMarker(parameterMetadata));
116136
}
137+
case NEGATING_SIMPLE_PROPERTY: {
138+
Expression pathExpression = createPropertyPathExpression(part.getProperty());
139+
ParameterMetadata parameterMetadata = parameterMetadataProvider.next(part);
140+
return Conditions.isEqual(pathExpression, createBindMarker(parameterMetadata)).not();
141+
}
117142
}
118143
throw new UnsupportedOperationException("Creating conditions for type " + type + " is unsupported");
119144
}
120145

121146
@NotNull
122147
private Expression createPropertyPathExpression(PropertyPath propertyPath) {
123148
@SuppressWarnings("unchecked")
124-
RelationalPersistentEntity<?> persistentEntity
125-
= mappingContext.getRequiredPersistentEntity(propertyPath.getOwningType());
126-
RelationalPersistentProperty persistentProperty
127-
= persistentEntity.getRequiredPersistentProperty(propertyPath.getSegment());
128-
Table table = SQL.table(persistentEntity.getTableName());
129-
return SQL.column(persistentProperty.getColumnName(), table);
149+
RelationalPersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(propertyPath.getOwningType());
150+
RelationalPersistentProperty property = entity.getRequiredPersistentProperty(propertyPath.getSegment());
151+
Table table = SQL.table(entity.getTableName());
152+
return SQL.column(property.getColumnName(), table);
130153
}
131154

132155
@NotNull
@@ -153,7 +176,7 @@ private NotLike(Expression leftColumnOrExpression, Expression rightColumnOrExpre
153176
/**
154177
* Creates new instance of this class with the given {@link Expression}s.
155178
*
156-
* @param leftColumnOrExpression the left {@link Expression}
179+
* @param leftColumnOrExpression the left {@link Expression}
157180
* @param rightColumnOrExpression the right {@link Expression}
158181
* @return {@link NotLike} condition
159182
*/

Diff for: src/test/java/org/springframework/data/r2dbc/repository/query/PartTreeR2dbcQueryIntegrationTests.java

+94-10
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
import reactor.core.publisher.Mono;
4242

4343
import java.lang.reflect.Method;
44+
import java.util.Collection;
45+
import java.util.Collections;
4446
import java.util.Date;
4547

4648
import static org.assertj.core.api.Assertions.assertThat;
@@ -56,7 +58,8 @@ public class PartTreeR2dbcQueryIntegrationTests {
5658
+ TABLE + ".first_name, "
5759
+ TABLE + ".last_name, "
5860
+ TABLE + ".date_of_birth, "
59-
+ TABLE + ".age";
61+
+ TABLE + ".age, "
62+
+ TABLE + ".active";
6063

6164
@Mock
6265
private ConnectionFactory connectionFactory;
@@ -242,7 +245,7 @@ public void createsQueryToFindAllEntitiesByStringAttributeLike() throws Exceptio
242245
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByFirstNameLike", String.class);
243246
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
244247
dataAccessStrategy);
245-
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] {"%John%"});
248+
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[]{"%John%"});
246249
BindableQuery bindableQuery = r2dbcQuery.createQuery(accessor);
247250
String expectedSql = "SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".first_name LIKE ?";
248251
assertThat(bindableQuery.get()).isEqualTo(expectedSql);
@@ -253,7 +256,7 @@ public void createsQueryToFindAllEntitiesByStringAttributeNotLike() throws Excep
253256
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByFirstNameNotLike", String.class);
254257
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
255258
dataAccessStrategy);
256-
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] {"%John%"});
259+
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[]{"%John%"});
257260
BindableQuery bindableQuery = r2dbcQuery.createQuery(accessor);
258261
String expectedSql = "SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".first_name NOT LIKE ?";
259262
assertThat(bindableQuery.get()).isEqualTo(expectedSql);
@@ -264,7 +267,7 @@ public void createsQueryToFindAllEntitiesByStringAttributeStartingWith() throws
264267
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByFirstNameStartingWith", String.class);
265268
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
266269
dataAccessStrategy);
267-
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] {"Jo"});
270+
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[]{"Jo"});
268271
BindableQuery bindableQuery = r2dbcQuery.createQuery(accessor);
269272
String expectedSql = "SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".first_name LIKE ?";
270273
assertThat(bindableQuery.get()).isEqualTo(expectedSql);
@@ -276,7 +279,7 @@ public void appendsLikeOperatorParameterWithPercentSymbolForStartingWithQuery()
276279
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByFirstNameStartingWith", String.class);
277280
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
278281
dataAccessStrategy);
279-
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] {"Jo"});
282+
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[]{"Jo"});
280283
BindableQuery bindableQuery = r2dbcQuery.createQuery(accessor);
281284
DatabaseClient.BindSpec bindSpecMock = mock(DatabaseClient.BindSpec.class);
282285
bindableQuery.bind(bindSpecMock);
@@ -288,7 +291,7 @@ public void createsQueryToFindAllEntitiesByStringAttributeEndingWith() throws Ex
288291
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByFirstNameEndingWith", String.class);
289292
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
290293
dataAccessStrategy);
291-
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] {"hn"});
294+
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[]{"hn"});
292295
BindableQuery bindableQuery = r2dbcQuery.createQuery(accessor);
293296
String expectedSql = "SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".first_name LIKE ?";
294297
assertThat(bindableQuery.get()).isEqualTo(expectedSql);
@@ -300,7 +303,7 @@ public void prependsLikeOperatorParameterWithPercentSymbolForStartingWithQuery()
300303
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByFirstNameEndingWith", String.class);
301304
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
302305
dataAccessStrategy);
303-
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] {"hn"});
306+
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[]{"hn"});
304307
BindableQuery bindableQuery = r2dbcQuery.createQuery(accessor);
305308
DatabaseClient.BindSpec bindSpecMock = mock(DatabaseClient.BindSpec.class);
306309
bindableQuery.bind(bindSpecMock);
@@ -312,7 +315,7 @@ public void createsQueryToFindAllEntitiesByStringAttributeContaining() throws Ex
312315
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByFirstNameContaining", String.class);
313316
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
314317
dataAccessStrategy);
315-
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] {"oh"});
318+
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[]{"oh"});
316319
BindableQuery bindableQuery = r2dbcQuery.createQuery(accessor);
317320
String expectedSql = "SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".first_name LIKE ?";
318321
assertThat(bindableQuery.get()).isEqualTo(expectedSql);
@@ -324,7 +327,7 @@ public void wrapsLikeOperatorParameterWithPercentSymbolsForContainingQuery() thr
324327
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByFirstNameContaining", String.class);
325328
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
326329
dataAccessStrategy);
327-
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] {"hn"});
330+
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[]{"hn"});
328331
BindableQuery bindableQuery = r2dbcQuery.createQuery(accessor);
329332
DatabaseClient.BindSpec bindSpecMock = mock(DatabaseClient.BindSpec.class);
330333
bindableQuery.bind(bindSpecMock);
@@ -337,13 +340,81 @@ public void createsQueryToFindAllEntitiesByIntegerAttributeWithDescendingOrderin
337340
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByAgeOrderByLastNameDesc", Integer.class);
338341
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
339342
dataAccessStrategy);
340-
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] {"oh"});
343+
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[]{"oh"});
341344
BindableQuery bindableQuery = r2dbcQuery.createQuery(accessor);
342345
String expectedSql = "SELECT " + ALL_FIELDS + " FROM " + TABLE
343346
+ " WHERE " + TABLE + ".age = ? ORDER BY last_name DESC";
344347
assertThat(bindableQuery.get()).isEqualTo(expectedSql);
345348
}
346349

350+
@Test
351+
public void createsQueryToFindAllEntitiesByStringAttributeNot() throws Exception {
352+
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByLastNameNot", String.class);
353+
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
354+
dataAccessStrategy);
355+
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[]{"Doe"});
356+
BindableQuery bindableQuery = r2dbcQuery.createQuery(accessor);
357+
String expectedSql = "SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".last_name != ?";
358+
assertThat(bindableQuery.get()).isEqualTo(expectedSql);
359+
}
360+
361+
@Test
362+
public void createsQueryToFindAllEntitiesByIntegerAttributeIn() throws Exception {
363+
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByAgeIn", Collection.class);
364+
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
365+
dataAccessStrategy);
366+
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod,
367+
new Object[]{Collections.singleton(25)});
368+
BindableQuery bindableQuery = r2dbcQuery.createQuery(accessor);
369+
String expectedSql = "SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".age IN (?)";
370+
assertThat(bindableQuery.get()).isEqualTo(expectedSql);
371+
}
372+
373+
@Test
374+
public void createsQueryToFindAllEntitiesByIntegerAttributeNotIn() throws Exception {
375+
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByAgeNotIn", Collection.class);
376+
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
377+
dataAccessStrategy);
378+
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod,
379+
new Object[]{Collections.singleton(25)});
380+
BindableQuery bindableQuery = r2dbcQuery.createQuery(accessor);
381+
String expectedSql = "SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".age NOT IN (?)";
382+
assertThat(bindableQuery.get()).isEqualTo(expectedSql);
383+
}
384+
385+
@Test
386+
public void createsQueryToFindAllEntitiesByBooleanAttributeTrue() throws Exception {
387+
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByActiveTrue");
388+
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
389+
dataAccessStrategy);
390+
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[0]);
391+
BindableQuery bindableQuery = r2dbcQuery.createQuery(accessor);
392+
String expectedSql = "SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".active = TRUE";
393+
assertThat(bindableQuery.get()).isEqualTo(expectedSql);
394+
}
395+
396+
@Test
397+
public void createsQueryToFindAllEntitiesByBooleanAttributeFalse() throws Exception {
398+
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByActiveFalse");
399+
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
400+
dataAccessStrategy);
401+
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[0]);
402+
BindableQuery bindableQuery = r2dbcQuery.createQuery(accessor);
403+
String expectedSql = "SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".active = FALSE";
404+
assertThat(bindableQuery.get()).isEqualTo(expectedSql);
405+
}
406+
407+
@Test
408+
public void createsQueryToFindAllEntitiesByStringAttributeIgnoringCase() throws Exception {
409+
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByFirstNameIgnoreCase", String.class);
410+
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
411+
dataAccessStrategy);
412+
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[]{"John"});
413+
BindableQuery bindableQuery = r2dbcQuery.createQuery(accessor);
414+
String expectedSql = "SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE UPPER(" + TABLE + ".first_name) = UPPER(?)";
415+
assertThat(bindableQuery.get()).isEqualTo(expectedSql);
416+
}
417+
347418
private R2dbcQueryMethod getQueryMethod(String methodName, Class<?>... parameterTypes) throws Exception {
348419
Method method = UserRepository.class.getMethod(methodName, parameterTypes);
349420
return new R2dbcQueryMethod(method, new DefaultRepositoryMetadata(UserRepository.class),
@@ -392,6 +463,18 @@ private interface UserRepository extends Repository<User, Long> {
392463
Flux<User> findAllByFirstNameContaining(String containing);
393464

394465
Flux<User> findAllByAgeOrderByLastNameDesc(Integer age);
466+
467+
Flux<User> findAllByLastNameNot(String lastName);
468+
469+
Flux<User> findAllByAgeIn(Collection<Integer> ages);
470+
471+
Flux<User> findAllByAgeNotIn(Collection<Integer> ages);
472+
473+
Flux<User> findAllByActiveTrue();
474+
475+
Flux<User> findAllByActiveFalse();
476+
477+
Flux<User> findAllByFirstNameIgnoreCase(String firstName);
395478
}
396479

397480
@Table("users")
@@ -403,5 +486,6 @@ private static class User {
403486
private String lastName;
404487
private Date dateOfBirth;
405488
private Integer age;
489+
private Boolean active;
406490
}
407491
}

0 commit comments

Comments
 (0)