Browse Source

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
pull/1955/head
Jens Schauder 12 months ago committed by Mark Paluch
parent
commit
ece981f9f1
No known key found for this signature in database
GPG Key ID: 55BC6374BAA9D973
  1. 5
      spring-data-relational/src/main/java/org/springframework/data/relational/repository/query/RelationalExampleMapper.java
  2. 149
      spring-data-relational/src/test/java/org/springframework/data/relational/repository/query/RelationalExampleMapperTests.java
  3. 1
      src/main/antora/modules/ROOT/pages/query-by-example.adoc

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

@ -38,6 +38,7 @@ import org.springframework.util.Assert; @@ -38,6 +38,7 @@ import org.springframework.util.Assert;
*
* @since 2.2
* @author Greg Turnquist
* @author Jens Schauder
*/
public class RelationalExampleMapper {
@ -78,6 +79,10 @@ public class RelationalExampleMapper { @@ -78,6 +79,10 @@ public class RelationalExampleMapper {
entity.doWithProperties((PropertyHandler<RelationalPersistentProperty>) property -> {
if (property.isCollectionLike() || property.isMap()) {
return;
}
if (matcherAccessor.isIgnoredPath(property.getName())) {
return;
}

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

@ -16,6 +16,15 @@ @@ -16,6 +16,15 @@
package org.springframework.data.relational.repository.query;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.domain.ExampleMatcher.*;
import static org.springframework.data.domain.ExampleMatcher.GenericPropertyMatchers.*;
import static org.springframework.data.domain.ExampleMatcher.StringMatcher.*;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.data.annotation.Id;
@ -23,13 +32,7 @@ import org.springframework.data.domain.Example; @@ -23,13 +32,7 @@ import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.relational.core.query.Query;
import java.util.Objects;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.domain.ExampleMatcher.GenericPropertyMatchers.*;
import static org.springframework.data.domain.ExampleMatcher.StringMatcher.*;
import static org.springframework.data.domain.ExampleMatcher.*;
import org.springframework.lang.Nullable;
/**
* Verify that the {@link RelationalExampleMapper} properly turns {@link Example}s into {@link Query}'s.
@ -48,8 +51,7 @@ public class RelationalExampleMapperTests { @@ -48,8 +51,7 @@ public class RelationalExampleMapperTests {
@Test // GH-929
void queryByExampleWithId() {
Person person = new Person();
person.setId("id1");
Person person = new Person("id1", null, null, null, null, null);
Example<Person> example = Example.of(person);
@ -63,8 +65,7 @@ public class RelationalExampleMapperTests { @@ -63,8 +65,7 @@ public class RelationalExampleMapperTests {
@Test // GH-929
void queryByExampleWithFirstname() {
Person person = new Person();
person.setFirstname("Frodo");
Person person = new Person(null, "Frodo", null, null, null, null);
Example<Person> example = Example.of(person);
@ -78,9 +79,7 @@ public class RelationalExampleMapperTests { @@ -78,9 +79,7 @@ public class RelationalExampleMapperTests {
@Test // GH-929
void queryByExampleWithFirstnameAndLastname() {
Person person = new Person();
person.setFirstname("Frodo");
person.setLastname("Baggins");
Person person = new Person(null, "Frodo", "Baggins", null, null, null);
Example<Person> example = Example.of(person);
@ -94,8 +93,7 @@ public class RelationalExampleMapperTests { @@ -94,8 +93,7 @@ public class RelationalExampleMapperTests {
@Test // GH-929
void queryByExampleWithNullMatchingLastName() {
Person person = new Person();
person.setLastname("Baggins");
Person person = new Person(null, null, "Baggins", null, null, null);
ExampleMatcher matcher = matching().withIncludeNullValues();
Example<Person> example = Example.of(person, matcher);
@ -110,9 +108,7 @@ public class RelationalExampleMapperTests { @@ -110,9 +108,7 @@ public class RelationalExampleMapperTests {
@Test // GH-929
void queryByExampleWithNullMatchingFirstnameAndLastname() {
Person person = new Person();
person.setFirstname("Bilbo");
person.setLastname("Baggins");
Person person = new Person(null, "Bilbo", "Baggins", null, null, null);
ExampleMatcher matcher = matching().withIncludeNullValues();
Example<Person> example = Example.of(person, matcher);
@ -127,9 +123,7 @@ public class RelationalExampleMapperTests { @@ -127,9 +123,7 @@ public class RelationalExampleMapperTests {
@Test // GH-929
void queryByExampleWithFirstnameAndLastnameIgnoringFirstname() {
Person person = new Person();
person.setFirstname("Frodo");
person.setLastname("Baggins");
Person person = new Person(null, "Bilbo", "Baggins", null, null, null);
ExampleMatcher matcher = matching().withIgnorePaths("firstname");
Example<Person> example = Example.of(person, matcher);
@ -144,9 +138,7 @@ public class RelationalExampleMapperTests { @@ -144,9 +138,7 @@ public class RelationalExampleMapperTests {
@Test // GH-929
void queryByExampleWithFirstnameAndLastnameWithNullMatchingIgnoringFirstName() {
Person person = new Person();
person.setFirstname("Frodo");
person.setLastname("Baggins");
Person person = new Person(null, "Bilbo", "Baggins", null, null, null);
ExampleMatcher matcher = matching().withIncludeNullValues().withIgnorePaths("firstname");
Example<Person> example = Example.of(person, matcher);
@ -161,8 +153,7 @@ public class RelationalExampleMapperTests { @@ -161,8 +153,7 @@ public class RelationalExampleMapperTests {
@Test // GH-929
void queryByExampleWithFirstnameWithStringMatchingAtTheBeginning() {
Person person = new Person();
person.setFirstname("Fro");
Person person = new Person(null, "Fro", null, null, null, null);
ExampleMatcher matcher = matching().withStringMatcher(STARTING);
Example<Person> example = Example.of(person, matcher);
@ -177,8 +168,7 @@ public class RelationalExampleMapperTests { @@ -177,8 +168,7 @@ public class RelationalExampleMapperTests {
@Test // GH-929
void queryByExampleWithFirstnameWithStringMatchingOnTheEnding() {
Person person = new Person();
person.setFirstname("do");
Person person = new Person(null, "do", null, null, null, null);
ExampleMatcher matcher = matching().withStringMatcher(ENDING);
Example<Person> example = Example.of(person, matcher);
@ -193,8 +183,7 @@ public class RelationalExampleMapperTests { @@ -193,8 +183,7 @@ public class RelationalExampleMapperTests {
@Test // GH-929
void queryByExampleWithFirstnameWithStringMatchingContaining() {
Person person = new Person();
person.setFirstname("do");
Person person = new Person(null, "do", null, null, null, null);
ExampleMatcher matcher = matching().withStringMatcher(CONTAINING);
Example<Person> example = Example.of(person, matcher);
@ -209,8 +198,7 @@ public class RelationalExampleMapperTests { @@ -209,8 +198,7 @@ public class RelationalExampleMapperTests {
@Test // GH-929
void queryByExampleWithFirstnameWithStringMatchingRegEx() {
Person person = new Person();
person.setFirstname("do");
Person person = new Person(null, "do", null, null, null, null);
ExampleMatcher matcher = matching().withStringMatcher(ExampleMatcher.StringMatcher.REGEX);
Example<Person> example = Example.of(person, matcher);
@ -222,8 +210,7 @@ public class RelationalExampleMapperTests { @@ -222,8 +210,7 @@ public class RelationalExampleMapperTests {
@Test // GH-929
void queryByExampleWithFirstnameWithFieldSpecificStringMatcherEndsWith() {
Person person = new Person();
person.setFirstname("do");
Person person = new Person(null, "do", null, null, null, null);
ExampleMatcher matcher = matching().withMatcher("firstname", endsWith());
Example<Person> example = Example.of(person, matcher);
@ -238,8 +225,7 @@ public class RelationalExampleMapperTests { @@ -238,8 +225,7 @@ public class RelationalExampleMapperTests {
@Test // GH-929
void queryByExampleWithFirstnameWithFieldSpecificStringMatcherStartsWith() {
Person person = new Person();
person.setFirstname("Fro");
Person person = new Person(null, "Fro", null, null, null, null);
ExampleMatcher matcher = matching().withMatcher("firstname", startsWith());
Example<Person> example = Example.of(person, matcher);
@ -254,8 +240,7 @@ public class RelationalExampleMapperTests { @@ -254,8 +240,7 @@ public class RelationalExampleMapperTests {
@Test // GH-929
void queryByExampleWithFirstnameWithFieldSpecificStringMatcherContains() {
Person person = new Person();
person.setFirstname("do");
Person person = new Person(null, "do", null, null, null, null);
ExampleMatcher matcher = matching().withMatcher("firstname", contains());
Example<Person> example = Example.of(person, matcher);
@ -270,8 +255,7 @@ public class RelationalExampleMapperTests { @@ -270,8 +255,7 @@ public class RelationalExampleMapperTests {
@Test // GH-929
void queryByExampleWithFirstnameWithStringMatchingAtTheBeginningIncludingNull() {
Person person = new Person();
person.setFirstname("Fro");
Person person = new Person(null, "Fro", null, null, null, null);
ExampleMatcher matcher = matching().withStringMatcher(STARTING).withIncludeNullValues();
Example<Person> example = Example.of(person, matcher);
@ -286,8 +270,7 @@ public class RelationalExampleMapperTests { @@ -286,8 +270,7 @@ public class RelationalExampleMapperTests {
@Test // GH-929
void queryByExampleWithFirstnameWithStringMatchingOnTheEndingIncludingNull() {
Person person = new Person();
person.setFirstname("do");
Person person = new Person(null, "do", null, null, null, null);
ExampleMatcher matcher = matching().withStringMatcher(ENDING).withIncludeNullValues();
Example<Person> example = Example.of(person, matcher);
@ -302,8 +285,7 @@ public class RelationalExampleMapperTests { @@ -302,8 +285,7 @@ public class RelationalExampleMapperTests {
@Test // GH-929
void queryByExampleWithFirstnameIgnoreCaseFieldLevel() {
Person person = new Person();
person.setFirstname("fro");
Person person = new Person(null, "fro", null, null, null, null);
ExampleMatcher matcher = matching().withMatcher("firstname", startsWith().ignoreCase());
Example<Person> example = Example.of(person, matcher);
@ -320,8 +302,7 @@ public class RelationalExampleMapperTests { @@ -320,8 +302,7 @@ public class RelationalExampleMapperTests {
@Test // GH-929
void queryByExampleWithFirstnameWithStringMatchingContainingIncludingNull() {
Person person = new Person();
person.setFirstname("do");
Person person = new Person(null, "do", null, null, null, null);
ExampleMatcher matcher = matching().withStringMatcher(CONTAINING).withIncludeNullValues();
Example<Person> example = Example.of(person, matcher);
@ -336,8 +317,7 @@ public class RelationalExampleMapperTests { @@ -336,8 +317,7 @@ public class RelationalExampleMapperTests {
@Test // GH-929
void queryByExampleWithFirstnameIgnoreCase() {
Person person = new Person();
person.setFirstname("Frodo");
Person person = new Person(null, "Frodo", null, null, null, null);
ExampleMatcher matcher = matching().withIgnoreCase(true);
Example<Person> example = Example.of(person, matcher);
@ -354,9 +334,7 @@ public class RelationalExampleMapperTests { @@ -354,9 +334,7 @@ public class RelationalExampleMapperTests {
@Test // GH-929
void queryByExampleWithFirstnameOrLastname() {
Person person = new Person();
person.setFirstname("Frodo");
person.setLastname("Baggins");
Person person = new Person(null, "Frodo", "Baggins", null, null, null);
ExampleMatcher matcher = matchingAny();
Example<Person> example = Example.of(person, matcher);
@ -371,9 +349,7 @@ public class RelationalExampleMapperTests { @@ -371,9 +349,7 @@ public class RelationalExampleMapperTests {
@Test // GH-929
void queryByExampleEvenHandlesInvisibleFields() {
Person person = new Person();
person.setFirstname("Frodo");
person.setSecret("I have the ring!");
Person person = new Person(null, "Frodo", null, "I have the ring!", null, null);
Example<Person> example = Example.of(person);
@ -388,10 +364,7 @@ public class RelationalExampleMapperTests { @@ -388,10 +364,7 @@ public class RelationalExampleMapperTests {
@Test // GH-929
void queryByExampleSupportsPropertyTransforms() {
Person person = new Person();
person.setFirstname("Frodo");
person.setLastname("Baggins");
person.setSecret("I have the ring!");
Person person = new Person(null, "Frodo", "Baggins", "I have the ring!", null, null);
ExampleMatcher matcher = matching() //
.withTransformer("firstname", o -> {
@ -418,55 +391,33 @@ public class RelationalExampleMapperTests { @@ -418,55 +391,33 @@ public class RelationalExampleMapperTests {
"(secret = 'I have the ring!')");
}
static class Person {
@Test // GH-1969
void collectionLikeAttributesGetIgnored() {
@Id
String id;
String firstname;
String lastname;
String secret;
Example<Person> example = Example.of(new Person(null, "Frodo", null, null, List.of(new Possession("Ring")), null));
public Person(String id, String firstname, String lastname, String secret) {
this.id = id;
this.firstname = firstname;
this.lastname = lastname;
this.secret = secret;
}
public Person() {
}
Query query = exampleMapper.getMappedExample(example);
// Override default visibility of getting the secret.
private String getSecret() {
return this.secret;
}
assertThat(query.getCriteria().orElseThrow().toString()).doesNotContainIgnoringCase("possession");
}
public String getId() {
return this.id;
}
@Test // GH-1969
void mapAttributesGetIgnored() {
public String getFirstname() {
return this.firstname;
}
Example<Person> example = Example.of(new Person(null, "Frodo", null, null, null, Map.of("Home", new Address("Bag End"))));
public String getLastname() {
return this.lastname;
}
Query query = exampleMapper.getMappedExample(example);
public void setId(String id) {
this.id = id;
}
assertThat(query.getCriteria().orElseThrow().toString()).doesNotContainIgnoringCase("address");
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
record Person(@Id @Nullable String id, @Nullable String firstname, @Nullable String lastname, @Nullable String secret,
@Nullable List<Possession> possessions,@Nullable Map<String,Address> addresses) {
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
record Possession(String name) {
}
public void setSecret(String secret) {
this.secret = secret;
}
record Address(String description) {
}
}

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

@ -1,3 +1,4 @@ @@ -1,3 +1,4 @@
:support-qbe-collection: false
include::{commons}@data-commons::query-by-example.adoc[]
Here's an example:

Loading…
Cancel
Save