Skip to content

Commit 78abfcd

Browse files
committed
Fix JoinType on associations when using MatchMode.ANY
Signed-off-by: Arnaud Lecointre <[email protected]>
1 parent 28924f6 commit 78abfcd

File tree

2 files changed

+31
-5
lines changed

2 files changed

+31
-5
lines changed

spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java

+10-5
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import jakarta.persistence.criteria.CriteriaBuilder;
1919
import jakarta.persistence.criteria.Expression;
2020
import jakarta.persistence.criteria.From;
21+
import jakarta.persistence.criteria.JoinType;
2122
import jakarta.persistence.criteria.Path;
2223
import jakarta.persistence.criteria.Predicate;
2324
import jakarta.persistence.criteria.Root;
@@ -57,6 +58,7 @@
5758
* @author Oliver Gierke
5859
* @author Jens Schauder
5960
* @author Greg Turnquist
61+
* @author Arnaud Lecointre
6062
* @since 1.10
6163
*/
6264
public class QueryByExamplePredicateBuilder {
@@ -103,7 +105,8 @@ public static <T> Predicate getPredicate(Root<T> root, CriteriaBuilder cb, Examp
103105
ExampleMatcher matcher = example.getMatcher();
104106

105107
List<Predicate> predicates = getPredicates("", cb, root, root.getModel(), example.getProbe(),
106-
example.getProbeType(), new ExampleMatcherAccessor(matcher), new PathNode("root", null, example.getProbe()),
108+
example.getProbeType(), matcher, new ExampleMatcherAccessor(matcher),
109+
new PathNode("root", null, example.getProbe()),
107110
escapeCharacter);
108111

109112
if (predicates.isEmpty()) {
@@ -121,7 +124,7 @@ public static <T> Predicate getPredicate(Root<T> root, CriteriaBuilder cb, Examp
121124

122125
@SuppressWarnings({ "rawtypes", "unchecked" })
123126
static List<Predicate> getPredicates(String path, CriteriaBuilder cb, Path<?> from, ManagedType<?> type, Object value,
124-
Class<?> probeType, ExampleMatcherAccessor exampleAccessor, PathNode currentNode,
127+
Class<?> probeType, ExampleMatcher matcher, ExampleMatcherAccessor exampleAccessor, PathNode currentNode,
125128
EscapeCharacter escapeCharacter) {
126129

127130
List<Predicate> predicates = new ArrayList<>();
@@ -158,7 +161,7 @@ static List<Predicate> getPredicates(String path, CriteriaBuilder cb, Path<?> fr
158161

159162
predicates
160163
.addAll(getPredicates(currentPath, cb, from.get(attribute.getName()), (ManagedType<?>) attribute.getType(),
161-
attributeValue, probeType, exampleAccessor, currentNode, escapeCharacter));
164+
attributeValue, probeType, matcher, exampleAccessor, currentNode, escapeCharacter));
162165
continue;
163166
}
164167

@@ -171,8 +174,10 @@ static List<Predicate> getPredicates(String path, CriteriaBuilder cb, Path<?> fr
171174
ClassUtils.getShortName(probeType), node));
172175
}
173176

174-
predicates.addAll(getPredicates(currentPath, cb, ((From<?, ?>) from).join(attribute.getName()),
175-
(ManagedType<?>) attribute.getType(), attributeValue, probeType, exampleAccessor, node, escapeCharacter));
177+
JoinType joinType = matcher.isAllMatching() ? JoinType.INNER : JoinType.LEFT;
178+
predicates.addAll(getPredicates(currentPath, cb, ((From<?, ?>) from).join(attribute.getName(), joinType),
179+
(ManagedType<?>) attribute.getType(), attributeValue, probeType, matcher, exampleAccessor, node,
180+
escapeCharacter));
176181

177182
continue;
178183
}

spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilderUnitTests.java

+21
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@
1616
package org.springframework.data.jpa.convert;
1717

1818
import static org.assertj.core.api.Assertions.*;
19+
import static org.mockito.ArgumentMatchers.*;
1920
import static org.mockito.Mockito.*;
2021
import static org.springframework.data.domain.Example.*;
2122

2223
import jakarta.persistence.Id;
2324
import jakarta.persistence.criteria.CriteriaBuilder;
2425
import jakarta.persistence.criteria.Expression;
2526
import jakarta.persistence.criteria.Join;
27+
import jakarta.persistence.criteria.JoinType;
2628
import jakarta.persistence.criteria.Path;
2729
import jakarta.persistence.criteria.Predicate;
2830
import jakarta.persistence.criteria.Root;
@@ -39,6 +41,8 @@
3941
import org.junit.jupiter.api.BeforeEach;
4042
import org.junit.jupiter.api.Test;
4143
import org.junit.jupiter.api.extension.ExtendWith;
44+
import org.junit.jupiter.params.ParameterizedTest;
45+
import org.junit.jupiter.params.provider.CsvSource;
4246
import org.mockito.ArgumentMatchers;
4347
import org.mockito.Mock;
4448
import org.mockito.junit.jupiter.MockitoExtension;
@@ -47,6 +51,7 @@
4751
import org.springframework.data.domain.Example;
4852
import org.springframework.data.domain.ExampleMatcher;
4953
import org.springframework.data.domain.ExampleMatcher.GenericPropertyMatcher;
54+
import org.springframework.data.domain.ExampleMatcher.MatchMode;
5055
import org.springframework.data.jpa.repository.query.EscapeCharacter;
5156
import org.springframework.util.ObjectUtils;
5257

@@ -57,6 +62,7 @@
5762
* @author Mark Paluch
5863
* @author Oliver Gierke
5964
* @author Jens Schauder
65+
* @author Arnaud Lecointre
6066
*/
6167
@ExtendWith(MockitoExtension.class)
6268
@MockitoSettings(strictness = Strictness.LENIENT)
@@ -271,6 +277,21 @@ void likePatternsGetEscapedEnding() {
271277
verify(cb, times(1)).like(any(Expression.class), eq("%f\\\\o\\_o"), eq('\\'));
272278
}
273279

280+
@ParameterizedTest(name = "Matching {0} on association should use join using JoinType.{1} ") // DATAJPA-3763
281+
@CsvSource({ "ALL, INNER", "ANY, LEFT" })
282+
void matchingAssociationShouldUseTheCorrectJoinType(MatchMode matchMode, JoinType expectedJoinType) {
283+
284+
Person person = new Person();
285+
person.father = new Person();
286+
287+
ExampleMatcher matcher = matchMode == MatchMode.ALL ? ExampleMatcher.matchingAll() : ExampleMatcher.matchingAny();
288+
Example<Person> example = of(person, matcher);
289+
290+
QueryByExamplePredicateBuilder.getPredicate(root, cb, example, EscapeCharacter.DEFAULT);
291+
292+
verify(root, times(1)).join("father", expectedJoinType);
293+
}
294+
274295
@SuppressWarnings("unused")
275296
static class Person {
276297

0 commit comments

Comments
 (0)