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/2110/head
Jens Schauder 12 months ago committed by Mark Paluch
parent
commit
f2224a921e
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;
* *
* @since 2.2 * @since 2.2
* @author Greg Turnquist * @author Greg Turnquist
* @author Jens Schauder
*/ */
public class RelationalExampleMapper { public class RelationalExampleMapper {
@ -78,6 +79,10 @@ public class RelationalExampleMapper {
entity.doWithProperties((PropertyHandler<RelationalPersistentProperty>) property -> { entity.doWithProperties((PropertyHandler<RelationalPersistentProperty>) property -> {
if (property.isCollectionLike() || property.isMap()) {
return;
}
if (matcherAccessor.isIgnoredPath(property.getName())) { if (matcherAccessor.isIgnoredPath(property.getName())) {
return; return;
} }

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

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

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

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

Loading…
Cancel
Save