Consider MatchMode when joining associations for QueryByExample.
This commit makes sure to use different join types depending on the actual match mode. Closes: #3763 Original Pull Request: #3794 Signed-off-by: ArnaudLec <lec.arnaud+github@gmail.com>
This commit is contained in:
committed by
Christoph Strobl
parent
4376c41244
commit
44c88dbbe7
@@ -18,6 +18,7 @@ package org.springframework.data.jpa.convert;
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.Expression;
|
||||
import jakarta.persistence.criteria.From;
|
||||
import jakarta.persistence.criteria.JoinType;
|
||||
import jakarta.persistence.criteria.Path;
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import jakarta.persistence.criteria.Root;
|
||||
@@ -57,6 +58,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Oliver Gierke
|
||||
* @author Jens Schauder
|
||||
* @author Greg Turnquist
|
||||
* @author Arnaud Lecointre
|
||||
* @since 1.10
|
||||
*/
|
||||
public class QueryByExamplePredicateBuilder {
|
||||
@@ -103,7 +105,8 @@ public class QueryByExamplePredicateBuilder {
|
||||
ExampleMatcher matcher = example.getMatcher();
|
||||
|
||||
List<Predicate> predicates = getPredicates("", cb, root, root.getModel(), example.getProbe(),
|
||||
example.getProbeType(), new ExampleMatcherAccessor(matcher), new PathNode("root", null, example.getProbe()),
|
||||
example.getProbeType(), matcher, new ExampleMatcherAccessor(matcher),
|
||||
new PathNode("root", null, example.getProbe()),
|
||||
escapeCharacter);
|
||||
|
||||
if (predicates.isEmpty()) {
|
||||
@@ -121,7 +124,7 @@ public class QueryByExamplePredicateBuilder {
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
static List<Predicate> getPredicates(String path, CriteriaBuilder cb, Path<?> from, ManagedType<?> type, Object value,
|
||||
Class<?> probeType, ExampleMatcherAccessor exampleAccessor, PathNode currentNode,
|
||||
Class<?> probeType, ExampleMatcher matcher, ExampleMatcherAccessor exampleAccessor, PathNode currentNode,
|
||||
EscapeCharacter escapeCharacter) {
|
||||
|
||||
List<Predicate> predicates = new ArrayList<>();
|
||||
@@ -158,7 +161,7 @@ public class QueryByExamplePredicateBuilder {
|
||||
|
||||
predicates
|
||||
.addAll(getPredicates(currentPath, cb, from.get(attribute.getName()), (ManagedType<?>) attribute.getType(),
|
||||
attributeValue, probeType, exampleAccessor, currentNode, escapeCharacter));
|
||||
attributeValue, probeType, matcher, exampleAccessor, currentNode, escapeCharacter));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -171,8 +174,10 @@ public class QueryByExamplePredicateBuilder {
|
||||
ClassUtils.getShortName(probeType), node));
|
||||
}
|
||||
|
||||
predicates.addAll(getPredicates(currentPath, cb, ((From<?, ?>) from).join(attribute.getName()),
|
||||
(ManagedType<?>) attribute.getType(), attributeValue, probeType, exampleAccessor, node, escapeCharacter));
|
||||
JoinType joinType = matcher.isAllMatching() ? JoinType.INNER : JoinType.LEFT;
|
||||
predicates.addAll(getPredicates(currentPath, cb, ((From<?, ?>) from).join(attribute.getName(), joinType),
|
||||
(ManagedType<?>) attribute.getType(), attributeValue, probeType, matcher, exampleAccessor, node,
|
||||
escapeCharacter));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.data.jpa.convert;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.data.domain.Example.*;
|
||||
|
||||
@@ -23,6 +24,7 @@ import jakarta.persistence.Id;
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.Expression;
|
||||
import jakarta.persistence.criteria.Join;
|
||||
import jakarta.persistence.criteria.JoinType;
|
||||
import jakarta.persistence.criteria.Path;
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import jakarta.persistence.criteria.Root;
|
||||
@@ -39,6 +41,8 @@ import java.util.Set;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
import org.mockito.ArgumentMatchers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
@@ -47,6 +51,7 @@ import org.mockito.quality.Strictness;
|
||||
import org.springframework.data.domain.Example;
|
||||
import org.springframework.data.domain.ExampleMatcher;
|
||||
import org.springframework.data.domain.ExampleMatcher.GenericPropertyMatcher;
|
||||
import org.springframework.data.domain.ExampleMatcher.MatchMode;
|
||||
import org.springframework.data.jpa.repository.query.EscapeCharacter;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
@@ -57,6 +62,7 @@ import org.springframework.util.ObjectUtils;
|
||||
* @author Mark Paluch
|
||||
* @author Oliver Gierke
|
||||
* @author Jens Schauder
|
||||
* @author Arnaud Lecointre
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
@@ -271,6 +277,21 @@ class QueryByExamplePredicateBuilderUnitTests {
|
||||
verify(cb, times(1)).like(any(Expression.class), eq("%f\\\\o\\_o"), eq('\\'));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "Matching {0} on association should join using JoinType.{1} ") // DATAJPA-3763
|
||||
@CsvSource({ "ALL, INNER", "ANY, LEFT" })
|
||||
void matchingAssociationShouldUseTheCorrectJoinType(MatchMode matchMode, JoinType expectedJoinType) {
|
||||
|
||||
Person person = new Person();
|
||||
person.father = new Person();
|
||||
|
||||
ExampleMatcher matcher = matchMode == MatchMode.ALL ? ExampleMatcher.matchingAll() : ExampleMatcher.matchingAny();
|
||||
Example<Person> example = of(person, matcher);
|
||||
|
||||
QueryByExamplePredicateBuilder.getPredicate(root, cb, example, EscapeCharacter.DEFAULT);
|
||||
|
||||
verify(root, times(1)).join("father", expectedJoinType);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class Person {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user