Skip to content

Commit

Permalink
Fix JoinType on associations when using MatchMode.ANY
Browse files Browse the repository at this point in the history
Signed-off-by: Arnaud Lecointre <[email protected]>
  • Loading branch information
ArnaudLec committed Feb 28, 2025
1 parent 28924f6 commit a9f31e5
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
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;
Expand Down Expand Up @@ -57,6 +58,7 @@
* @author Oliver Gierke
* @author Jens Schauder
* @author Greg Turnquist
* @author Arnaud Lecointre
* @since 1.10
*/
public class QueryByExamplePredicateBuilder {
Expand Down Expand Up @@ -103,7 +105,8 @@ public static <T> Predicate getPredicate(Root<T> root, CriteriaBuilder cb, Examp
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()) {
Expand All @@ -121,7 +124,7 @@ public static <T> Predicate getPredicate(Root<T> root, CriteriaBuilder cb, Examp

@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<>();
Expand Down Expand Up @@ -158,7 +161,7 @@ static List<Predicate> getPredicates(String path, CriteriaBuilder cb, Path<?> fr

predicates
.addAll(getPredicates(currentPath, cb, from.get(attribute.getName()), (ManagedType<?>) attribute.getType(),
attributeValue, probeType, exampleAccessor, currentNode, escapeCharacter));
attributeValue, probeType, matcher, exampleAccessor, currentNode, escapeCharacter));
continue;
}

Expand All @@ -171,8 +174,10 @@ static List<Predicate> getPredicates(String path, CriteriaBuilder cb, Path<?> fr
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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@
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.*;

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;
Expand All @@ -39,6 +41,8 @@
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;
Expand All @@ -47,6 +51,7 @@
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;

Expand All @@ -57,6 +62,7 @@
* @author Mark Paluch
* @author Oliver Gierke
* @author Jens Schauder
* @author Arnaud Lecointre
*/
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
Expand Down Expand Up @@ -271,6 +277,21 @@ void likePatternsGetEscapedEnding() {
verify(cb, times(1)).like(any(Expression.class), eq("%f\\\\o\\_o"), eq('\\'));
}

@ParameterizedTest(name = "Matching {0} on association should use 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 {

Expand Down

0 comments on commit a9f31e5

Please sign in to comment.