Skip to content

Commit 7693971

Browse files
committed
Use implicit join to create Predicate if possible
Fix spring-projectsGH-3349
1 parent fc8092d commit 7693971

File tree

2 files changed

+95
-0
lines changed

2 files changed

+95
-0
lines changed

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java

+14
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
* @author Moritz Becker
5757
* @author Andrey Kovalev
5858
* @author Greg Turnquist
59+
* @author Yanming Zhou
5960
*/
6061
public class JpaQueryCreator extends AbstractQueryCreator<CriteriaQuery<? extends Object>, Predicate> {
6162

@@ -384,6 +385,19 @@ private Expression<? extends Comparable> getComparablePath(Root<?> root, Part pa
384385
}
385386

386387
private <T> Expression<T> getTypedPath(Root<?> root, Part part) {
388+
389+
// use implicit join if possible
390+
Path<?> path = root;
391+
PropertyPath property = part.getProperty();
392+
while (property != null && !property.isCollection()) {
393+
path = path.get(property.getSegment());
394+
if (property.hasNext()) {
395+
property = property.next();
396+
} else {
397+
return (Expression<T>) path;
398+
}
399+
}
400+
387401
return toExpressionRecursively(root, part.getProperty());
388402
}
389403

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright 2017-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.jpa.repository.query;
17+
18+
import jakarta.persistence.EntityManager;
19+
import jakarta.persistence.PersistenceContext;
20+
import jakarta.persistence.TypedQuery;
21+
import org.hibernate.query.spi.SqmQuery;
22+
import org.hibernate.query.sqm.tree.from.SqmRoot;
23+
import org.hibernate.query.sqm.tree.select.SqmQuerySpec;
24+
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
25+
import org.junit.jupiter.api.Test;
26+
import org.junit.jupiter.api.extension.ExtendWith;
27+
import org.springframework.data.jpa.domain.sample.User;
28+
import org.springframework.data.jpa.provider.PersistenceProvider;
29+
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
30+
import org.springframework.data.repository.Repository;
31+
import org.springframework.data.repository.core.support.AbstractRepositoryMetadata;
32+
import org.springframework.data.repository.query.parser.PartTree;
33+
import org.springframework.test.context.ContextConfiguration;
34+
import org.springframework.test.context.junit.jupiter.SpringExtension;
35+
36+
import java.lang.reflect.Method;
37+
import java.util.List;
38+
39+
import static org.assertj.core.api.Assertions.assertThat;
40+
41+
/**
42+
* Integration tests for {@link JpaQueryCreator}.
43+
*
44+
* @author Yanming Zhou
45+
*/
46+
@ExtendWith(SpringExtension.class)
47+
@ContextConfiguration("classpath:infrastructure.xml")
48+
class JpaQueryCreatorIntegrationTests {
49+
50+
@PersistenceContext
51+
EntityManager entityManager;
52+
53+
@Test // GH-3349
54+
void implicitJoin() throws Exception {
55+
56+
Method method = SomeRepository.class.getMethod("findByManagerId", Integer.class);
57+
58+
PersistenceProvider provider = PersistenceProvider.fromEntityManager(entityManager);
59+
JpaQueryMethod queryMethod = new JpaQueryMethod(method,
60+
AbstractRepositoryMetadata.getMetadata(SomeRepository.class), new SpelAwareProxyProjectionFactory(), provider);
61+
62+
PartTree tree = new PartTree("findByManagerId", User.class);
63+
ParameterMetadataProvider metadataProvider = new ParameterMetadataProvider(entityManager.getCriteriaBuilder(),
64+
queryMethod.getParameters(), EscapeCharacter.DEFAULT);
65+
66+
JpaQueryCreator creator = new JpaQueryCreator(tree, queryMethod.getResultProcessor().getReturnedType(),
67+
entityManager.getCriteriaBuilder(), metadataProvider);
68+
69+
TypedQuery<?> query = entityManager.createQuery(creator.createQuery());
70+
SqmQuery sqmQuery = ((SqmQuery) query);
71+
SqmSelectStatement<?> statement = (SqmSelectStatement<?>) sqmQuery.getSqmStatement();
72+
SqmQuerySpec<?> spec = (SqmQuerySpec<?>) statement.getQueryPart();
73+
SqmRoot<?> root = spec.getFromClause().getRoots().get(0);
74+
75+
assertThat(root.getJoins()).isEmpty();
76+
}
77+
78+
interface SomeRepository extends Repository<User, Integer> {
79+
List<User> findByManagerId(Integer managerId);
80+
}
81+
}

0 commit comments

Comments
 (0)