Skip to content

Commit f2224a9

Browse files
schaudermp911de
authored andcommitted
Ignore collection like attributes for query by example.
Collection valued attributes now get ignored. Before RelationalExampleMapper tried to generate predicates for these, resulting in invalid SQL. Closes #1969
1 parent 1c9c014 commit f2224a9

File tree

3 files changed

+56
-99
lines changed

3 files changed

+56
-99
lines changed

spring-data-relational/src/main/java/org/springframework/data/relational/repository/query/RelationalExampleMapper.java

+5
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
*
3939
* @since 2.2
4040
* @author Greg Turnquist
41+
* @author Jens Schauder
4142
*/
4243
public class RelationalExampleMapper {
4344

@@ -78,6 +79,10 @@ private <T> Query getMappedExample(Example<T> example, RelationalPersistentEntit
7879

7980
entity.doWithProperties((PropertyHandler<RelationalPersistentProperty>) property -> {
8081

82+
if (property.isCollectionLike() || property.isMap()) {
83+
return;
84+
}
85+
8186
if (matcherAccessor.isIgnoredPath(property.getName())) {
8287
return;
8388
}

spring-data-relational/src/test/java/org/springframework/data/relational/repository/query/RelationalExampleMapperTests.java

+50-99
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,23 @@
1616

1717
package org.springframework.data.relational.repository.query;
1818

19+
import static org.assertj.core.api.Assertions.*;
20+
import static org.springframework.data.domain.ExampleMatcher.*;
21+
import static org.springframework.data.domain.ExampleMatcher.GenericPropertyMatchers.*;
22+
import static org.springframework.data.domain.ExampleMatcher.StringMatcher.*;
23+
24+
import java.util.List;
25+
import java.util.Map;
26+
import java.util.Objects;
27+
1928
import org.junit.jupiter.api.BeforeEach;
2029
import org.junit.jupiter.api.Test;
2130
import org.springframework.data.annotation.Id;
2231
import org.springframework.data.domain.Example;
2332
import org.springframework.data.domain.ExampleMatcher;
2433
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
2534
import org.springframework.data.relational.core.query.Query;
26-
27-
import java.util.Objects;
28-
29-
import static org.assertj.core.api.Assertions.*;
30-
import static org.springframework.data.domain.ExampleMatcher.GenericPropertyMatchers.*;
31-
import static org.springframework.data.domain.ExampleMatcher.StringMatcher.*;
32-
import static org.springframework.data.domain.ExampleMatcher.*;
35+
import org.springframework.lang.Nullable;
3336

3437
/**
3538
* Verify that the {@link RelationalExampleMapper} properly turns {@link Example}s into {@link Query}'s.
@@ -48,8 +51,7 @@ public void before() {
4851
@Test // GH-929
4952
void queryByExampleWithId() {
5053

51-
Person person = new Person();
52-
person.setId("id1");
54+
Person person = new Person("id1", null, null, null, null, null);
5355

5456
Example<Person> example = Example.of(person);
5557

@@ -63,8 +65,7 @@ void queryByExampleWithId() {
6365
@Test // GH-929
6466
void queryByExampleWithFirstname() {
6567

66-
Person person = new Person();
67-
person.setFirstname("Frodo");
68+
Person person = new Person(null, "Frodo", null, null, null, null);
6869

6970
Example<Person> example = Example.of(person);
7071

@@ -78,9 +79,7 @@ void queryByExampleWithFirstname() {
7879
@Test // GH-929
7980
void queryByExampleWithFirstnameAndLastname() {
8081

81-
Person person = new Person();
82-
person.setFirstname("Frodo");
83-
person.setLastname("Baggins");
82+
Person person = new Person(null, "Frodo", "Baggins", null, null, null);
8483

8584
Example<Person> example = Example.of(person);
8685

@@ -94,8 +93,7 @@ void queryByExampleWithFirstnameAndLastname() {
9493
@Test // GH-929
9594
void queryByExampleWithNullMatchingLastName() {
9695

97-
Person person = new Person();
98-
person.setLastname("Baggins");
96+
Person person = new Person(null, null, "Baggins", null, null, null);
9997

10098
ExampleMatcher matcher = matching().withIncludeNullValues();
10199
Example<Person> example = Example.of(person, matcher);
@@ -110,9 +108,7 @@ void queryByExampleWithNullMatchingLastName() {
110108
@Test // GH-929
111109
void queryByExampleWithNullMatchingFirstnameAndLastname() {
112110

113-
Person person = new Person();
114-
person.setFirstname("Bilbo");
115-
person.setLastname("Baggins");
111+
Person person = new Person(null, "Bilbo", "Baggins", null, null, null);
116112

117113
ExampleMatcher matcher = matching().withIncludeNullValues();
118114
Example<Person> example = Example.of(person, matcher);
@@ -127,9 +123,7 @@ void queryByExampleWithNullMatchingFirstnameAndLastname() {
127123
@Test // GH-929
128124
void queryByExampleWithFirstnameAndLastnameIgnoringFirstname() {
129125

130-
Person person = new Person();
131-
person.setFirstname("Frodo");
132-
person.setLastname("Baggins");
126+
Person person = new Person(null, "Bilbo", "Baggins", null, null, null);
133127

134128
ExampleMatcher matcher = matching().withIgnorePaths("firstname");
135129
Example<Person> example = Example.of(person, matcher);
@@ -144,9 +138,7 @@ void queryByExampleWithFirstnameAndLastnameIgnoringFirstname() {
144138
@Test // GH-929
145139
void queryByExampleWithFirstnameAndLastnameWithNullMatchingIgnoringFirstName() {
146140

147-
Person person = new Person();
148-
person.setFirstname("Frodo");
149-
person.setLastname("Baggins");
141+
Person person = new Person(null, "Bilbo", "Baggins", null, null, null);
150142

151143
ExampleMatcher matcher = matching().withIncludeNullValues().withIgnorePaths("firstname");
152144
Example<Person> example = Example.of(person, matcher);
@@ -161,8 +153,7 @@ void queryByExampleWithFirstnameAndLastnameWithNullMatchingIgnoringFirstName() {
161153
@Test // GH-929
162154
void queryByExampleWithFirstnameWithStringMatchingAtTheBeginning() {
163155

164-
Person person = new Person();
165-
person.setFirstname("Fro");
156+
Person person = new Person(null, "Fro", null, null, null, null);
166157

167158
ExampleMatcher matcher = matching().withStringMatcher(STARTING);
168159
Example<Person> example = Example.of(person, matcher);
@@ -177,8 +168,7 @@ void queryByExampleWithFirstnameWithStringMatchingAtTheBeginning() {
177168
@Test // GH-929
178169
void queryByExampleWithFirstnameWithStringMatchingOnTheEnding() {
179170

180-
Person person = new Person();
181-
person.setFirstname("do");
171+
Person person = new Person(null, "do", null, null, null, null);
182172

183173
ExampleMatcher matcher = matching().withStringMatcher(ENDING);
184174
Example<Person> example = Example.of(person, matcher);
@@ -193,8 +183,7 @@ void queryByExampleWithFirstnameWithStringMatchingOnTheEnding() {
193183
@Test // GH-929
194184
void queryByExampleWithFirstnameWithStringMatchingContaining() {
195185

196-
Person person = new Person();
197-
person.setFirstname("do");
186+
Person person = new Person(null, "do", null, null, null, null);
198187

199188
ExampleMatcher matcher = matching().withStringMatcher(CONTAINING);
200189
Example<Person> example = Example.of(person, matcher);
@@ -209,8 +198,7 @@ void queryByExampleWithFirstnameWithStringMatchingContaining() {
209198
@Test // GH-929
210199
void queryByExampleWithFirstnameWithStringMatchingRegEx() {
211200

212-
Person person = new Person();
213-
person.setFirstname("do");
201+
Person person = new Person(null, "do", null, null, null, null);
214202

215203
ExampleMatcher matcher = matching().withStringMatcher(ExampleMatcher.StringMatcher.REGEX);
216204
Example<Person> example = Example.of(person, matcher);
@@ -222,8 +210,7 @@ void queryByExampleWithFirstnameWithStringMatchingRegEx() {
222210
@Test // GH-929
223211
void queryByExampleWithFirstnameWithFieldSpecificStringMatcherEndsWith() {
224212

225-
Person person = new Person();
226-
person.setFirstname("do");
213+
Person person = new Person(null, "do", null, null, null, null);
227214

228215
ExampleMatcher matcher = matching().withMatcher("firstname", endsWith());
229216
Example<Person> example = Example.of(person, matcher);
@@ -238,8 +225,7 @@ void queryByExampleWithFirstnameWithFieldSpecificStringMatcherEndsWith() {
238225
@Test // GH-929
239226
void queryByExampleWithFirstnameWithFieldSpecificStringMatcherStartsWith() {
240227

241-
Person person = new Person();
242-
person.setFirstname("Fro");
228+
Person person = new Person(null, "Fro", null, null, null, null);
243229

244230
ExampleMatcher matcher = matching().withMatcher("firstname", startsWith());
245231
Example<Person> example = Example.of(person, matcher);
@@ -254,8 +240,7 @@ void queryByExampleWithFirstnameWithFieldSpecificStringMatcherStartsWith() {
254240
@Test // GH-929
255241
void queryByExampleWithFirstnameWithFieldSpecificStringMatcherContains() {
256242

257-
Person person = new Person();
258-
person.setFirstname("do");
243+
Person person = new Person(null, "do", null, null, null, null);
259244

260245
ExampleMatcher matcher = matching().withMatcher("firstname", contains());
261246
Example<Person> example = Example.of(person, matcher);
@@ -270,8 +255,7 @@ void queryByExampleWithFirstnameWithFieldSpecificStringMatcherContains() {
270255
@Test // GH-929
271256
void queryByExampleWithFirstnameWithStringMatchingAtTheBeginningIncludingNull() {
272257

273-
Person person = new Person();
274-
person.setFirstname("Fro");
258+
Person person = new Person(null, "Fro", null, null, null, null);
275259

276260
ExampleMatcher matcher = matching().withStringMatcher(STARTING).withIncludeNullValues();
277261
Example<Person> example = Example.of(person, matcher);
@@ -286,8 +270,7 @@ void queryByExampleWithFirstnameWithStringMatchingAtTheBeginningIncludingNull()
286270
@Test // GH-929
287271
void queryByExampleWithFirstnameWithStringMatchingOnTheEndingIncludingNull() {
288272

289-
Person person = new Person();
290-
person.setFirstname("do");
273+
Person person = new Person(null, "do", null, null, null, null);
291274

292275
ExampleMatcher matcher = matching().withStringMatcher(ENDING).withIncludeNullValues();
293276
Example<Person> example = Example.of(person, matcher);
@@ -302,8 +285,7 @@ void queryByExampleWithFirstnameWithStringMatchingOnTheEndingIncludingNull() {
302285
@Test // GH-929
303286
void queryByExampleWithFirstnameIgnoreCaseFieldLevel() {
304287

305-
Person person = new Person();
306-
person.setFirstname("fro");
288+
Person person = new Person(null, "fro", null, null, null, null);
307289

308290
ExampleMatcher matcher = matching().withMatcher("firstname", startsWith().ignoreCase());
309291
Example<Person> example = Example.of(person, matcher);
@@ -320,8 +302,7 @@ void queryByExampleWithFirstnameIgnoreCaseFieldLevel() {
320302
@Test // GH-929
321303
void queryByExampleWithFirstnameWithStringMatchingContainingIncludingNull() {
322304

323-
Person person = new Person();
324-
person.setFirstname("do");
305+
Person person = new Person(null, "do", null, null, null, null);
325306

326307
ExampleMatcher matcher = matching().withStringMatcher(CONTAINING).withIncludeNullValues();
327308
Example<Person> example = Example.of(person, matcher);
@@ -336,8 +317,7 @@ void queryByExampleWithFirstnameWithStringMatchingContainingIncludingNull() {
336317
@Test // GH-929
337318
void queryByExampleWithFirstnameIgnoreCase() {
338319

339-
Person person = new Person();
340-
person.setFirstname("Frodo");
320+
Person person = new Person(null, "Frodo", null, null, null, null);
341321

342322
ExampleMatcher matcher = matching().withIgnoreCase(true);
343323
Example<Person> example = Example.of(person, matcher);
@@ -354,9 +334,7 @@ void queryByExampleWithFirstnameIgnoreCase() {
354334
@Test // GH-929
355335
void queryByExampleWithFirstnameOrLastname() {
356336

357-
Person person = new Person();
358-
person.setFirstname("Frodo");
359-
person.setLastname("Baggins");
337+
Person person = new Person(null, "Frodo", "Baggins", null, null, null);
360338

361339
ExampleMatcher matcher = matchingAny();
362340
Example<Person> example = Example.of(person, matcher);
@@ -371,9 +349,7 @@ void queryByExampleWithFirstnameOrLastname() {
371349
@Test // GH-929
372350
void queryByExampleEvenHandlesInvisibleFields() {
373351

374-
Person person = new Person();
375-
person.setFirstname("Frodo");
376-
person.setSecret("I have the ring!");
352+
Person person = new Person(null, "Frodo", null, "I have the ring!", null, null);
377353

378354
Example<Person> example = Example.of(person);
379355

@@ -388,10 +364,7 @@ void queryByExampleEvenHandlesInvisibleFields() {
388364
@Test // GH-929
389365
void queryByExampleSupportsPropertyTransforms() {
390366

391-
Person person = new Person();
392-
person.setFirstname("Frodo");
393-
person.setLastname("Baggins");
394-
person.setSecret("I have the ring!");
367+
Person person = new Person(null, "Frodo", "Baggins", "I have the ring!", null, null);
395368

396369
ExampleMatcher matcher = matching() //
397370
.withTransformer("firstname", o -> {
@@ -418,55 +391,33 @@ void queryByExampleSupportsPropertyTransforms() {
418391
"(secret = 'I have the ring!')");
419392
}
420393

421-
static class Person {
394+
@Test // GH-1969
395+
void collectionLikeAttributesGetIgnored() {
422396

423-
@Id
424-
String id;
425-
String firstname;
426-
String lastname;
427-
String secret;
397+
Example<Person> example = Example.of(new Person(null, "Frodo", null, null, List.of(new Possession("Ring")), null));
428398

429-
public Person(String id, String firstname, String lastname, String secret) {
430-
this.id = id;
431-
this.firstname = firstname;
432-
this.lastname = lastname;
433-
this.secret = secret;
434-
}
435-
436-
public Person() {
437-
}
399+
Query query = exampleMapper.getMappedExample(example);
438400

439-
// Override default visibility of getting the secret.
440-
private String getSecret() {
441-
return this.secret;
442-
}
401+
assertThat(query.getCriteria().orElseThrow().toString()).doesNotContainIgnoringCase("possession");
402+
}
443403

444-
public String getId() {
445-
return this.id;
446-
}
404+
@Test // GH-1969
405+
void mapAttributesGetIgnored() {
447406

448-
public String getFirstname() {
449-
return this.firstname;
450-
}
407+
Example<Person> example = Example.of(new Person(null, "Frodo", null, null, null, Map.of("Home", new Address("Bag End"))));
451408

452-
public String getLastname() {
453-
return this.lastname;
454-
}
409+
Query query = exampleMapper.getMappedExample(example);
455410

456-
public void setId(String id) {
457-
this.id = id;
458-
}
411+
assertThat(query.getCriteria().orElseThrow().toString()).doesNotContainIgnoringCase("address");
412+
}
459413

460-
public void setFirstname(String firstname) {
461-
this.firstname = firstname;
462-
}
414+
record Person(@Id @Nullable String id, @Nullable String firstname, @Nullable String lastname, @Nullable String secret,
415+
@Nullable List<Possession> possessions,@Nullable Map<String,Address> addresses) {
416+
}
463417

464-
public void setLastname(String lastname) {
465-
this.lastname = lastname;
466-
}
418+
record Possession(String name) {
419+
}
467420

468-
public void setSecret(String secret) {
469-
this.secret = secret;
470-
}
421+
record Address(String description) {
471422
}
472423
}

src/main/antora/modules/ROOT/pages/query-by-example.adoc

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
:support-qbe-collection: false
12
include::{commons}@data-commons::query-by-example.adoc[]
23

34
Here's an example:

0 commit comments

Comments
 (0)