Skip to content

Commit acd64a9

Browse files
committed
Migrate toward using a BeanFactory.
Also ensure it works for external QueryRewriters as well as repository-based QueryRewriters. See #2162.
1 parent 46f571b commit acd64a9

13 files changed

+433
-69
lines changed

pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
<jsqlparser>4.3</jsqlparser>
2828
<mysql-connector-java>8.0.23</mysql-connector-java>
2929
<postgresql>42.2.19</postgresql>
30-
<springdata.commons>3.0.0-SNAPSHOT</springdata.commons>
30+
<springdata.commons>3.0.0-gh-2162-SNAPSHOT</springdata.commons>
3131
<vavr>0.10.3</vavr>
3232

3333
<hibernate.groupId>org.hibernate</hibernate.groupId>

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

+36-13
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,12 @@
1818
import jakarta.persistence.EntityManager;
1919
import jakarta.persistence.Query;
2020

21-
import java.lang.reflect.Constructor;
22-
import java.lang.reflect.InvocationTargetException;
21+
import java.util.function.Supplier;
2322

23+
import org.apache.commons.logging.Log;
24+
import org.apache.commons.logging.LogFactory;
25+
import org.springframework.beans.BeansException;
26+
import org.springframework.beans.factory.BeanFactory;
2427
import org.springframework.data.domain.Pageable;
2528
import org.springframework.data.domain.Sort;
2629
import org.springframework.data.jpa.repository.QueryRewriter;
@@ -45,12 +48,15 @@
4548
*/
4649
abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery {
4750

51+
private static final Log LOGGER = LogFactory.getLog(AbstractStringBasedJpaQuery.class);
52+
4853
private final DeclaredQuery query;
4954
private final DeclaredQuery countQuery;
5055
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
5156
private final SpelExpressionParser parser;
5257
private final QueryParameterSetter.QueryMetadataCache metadataCache = new QueryParameterSetter.QueryMetadataCache();
53-
private final QueryRewriter queryRewriter;
58+
private final Supplier<QueryRewriter> queryRewriter;
59+
private final BeanFactory beanFactory;
5460

5561
/**
5662
* Creates a new {@link AbstractStringBasedJpaQuery} from the given {@link JpaQueryMethod}, {@link EntityManager} and
@@ -65,7 +71,7 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery {
6571
*/
6672
public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, String queryString,
6773
@Nullable String countQueryString, QueryMethodEvaluationContextProvider evaluationContextProvider,
68-
SpelExpressionParser parser) {
74+
SpelExpressionParser parser, BeanFactory beanFactory) {
6975

7076
super(method, em);
7177

@@ -82,7 +88,8 @@ public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, Stri
8288
method.isNativeQuery());
8389

8490
this.parser = parser;
85-
this.queryRewriter = findQueryRewriter(method);
91+
this.beanFactory = beanFactory;
92+
this.queryRewriter = () -> findQueryRewriter(method);
8693

8794
Assert.isTrue(method.isNativeQuery() || !query.usesJdbcStyleParameters(),
8895
"JDBC style parameters (?) are not supported for JPA queries.");
@@ -164,14 +171,14 @@ protected Query createJpaQuery(String queryString, Sort sort, @Nullable Pageable
164171
}
165172

166173
/**
167-
* Useing the {@link org.springframework.data.jpa.repository.QueryRewrite} annotation, look for a
168-
* {@link QueryRewriter} and instantiate one.
174+
* Using the {@link org.springframework.data.jpa.repository.QueryRewrite} annotation, look for a {@link QueryRewriter}
175+
* and instantiate one.
169176
*
170177
* @param method - {@link JpaQueryMethod} that has the annotation details
171178
* @return a {@link QueryRewriter for the method or {@code null}
172179
*/
173180
@Nullable
174-
protected QueryRewriter findQueryRewriter(JpaQueryMethod method) {
181+
private QueryRewriter findQueryRewriter(JpaQueryMethod method) {
175182

176183
Class<? extends QueryRewriter> queryRewriter = method.getQueryRewriter();
177184

@@ -180,21 +187,37 @@ protected QueryRewriter findQueryRewriter(JpaQueryMethod method) {
180187
}
181188

182189
try {
183-
return (QueryRewriter) ((Constructor<?>) queryRewriter.getDeclaredConstructor()).newInstance();
184-
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
185-
System.out.println(e);
190+
return beanFactory.getBean(queryRewriter);
191+
} catch (BeansException e) {
192+
LOGGER.error(e.toString());
186193
return null;
187194
}
195+
//
196+
// try {
197+
// return (QueryRewriter) ((Constructor<?>) queryRewriter.getDeclaredConstructor()).newInstance();
198+
// } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
199+
// System.out.println(e);
200+
// return null;
201+
// }
188202
}
189203

204+
/**
205+
* Use the {@link QueryRewriter}, potentially rewrite the query, using relevant {@link Sort} and {@link Pageable}
206+
* information.
207+
*
208+
* @param originalQuery
209+
* @param sort
210+
* @param pageable
211+
* @return
212+
*/
190213
protected String potentiallyRewriteQuery(String originalQuery, Sort sort, @Nullable Pageable pageable) {
191214

192215
if (this.queryRewriter == null) {
193216
return originalQuery;
194217
}
195218

196219
return pageable != null && pageable.isPaged() //
197-
? this.queryRewriter.rewrite(originalQuery, pageable) //
198-
: this.queryRewriter.rewrite(originalQuery, sort);
220+
? this.queryRewriter.get().rewrite(originalQuery, pageable) //
221+
: this.queryRewriter.get().rewrite(originalQuery, sort);
199222
}
200223
}

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

+5-4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import jakarta.persistence.EntityManager;
1919

20+
import org.springframework.beans.factory.BeanFactory;
2021
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
2122
import org.springframework.data.repository.query.RepositoryQuery;
2223
import org.springframework.expression.spel.standard.SpelExpressionParser;
@@ -45,12 +46,12 @@ enum JpaQueryFactory {
4546
* @return
4647
*/
4748
AbstractJpaQuery fromMethodWithQueryString(JpaQueryMethod method, EntityManager em, String queryString,
48-
@Nullable String countQueryString,
49-
QueryMethodEvaluationContextProvider evaluationContextProvider) {
49+
@Nullable String countQueryString, QueryMethodEvaluationContextProvider evaluationContextProvider,
50+
BeanFactory beanFactory) {
5051

5152
return method.isNativeQuery()
52-
? new NativeJpaQuery(method, em, queryString, countQueryString, evaluationContextProvider, PARSER)
53-
: new SimpleJpaQuery(method, em, queryString, countQueryString, evaluationContextProvider, PARSER);
53+
? new NativeJpaQuery(method, em, queryString, countQueryString, evaluationContextProvider, PARSER, beanFactory)
54+
: new SimpleJpaQuery(method, em, queryString, countQueryString, evaluationContextProvider, PARSER, beanFactory);
5455
}
5556

5657
/**

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

+27-19
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515
*/
1616
package org.springframework.data.jpa.repository.query;
1717

18-
import java.lang.reflect.Method;
19-
2018
import jakarta.persistence.EntityManager;
2119

20+
import java.lang.reflect.Method;
21+
2222
import org.apache.commons.logging.Log;
2323
import org.apache.commons.logging.LogFactory;
24-
24+
import org.springframework.beans.factory.BeanFactory;
2525
import org.springframework.data.jpa.repository.Query;
2626
import org.springframework.data.projection.ProjectionFactory;
2727
import org.springframework.data.repository.core.NamedQueries;
@@ -61,20 +61,23 @@ private abstract static class AbstractQueryLookupStrategy implements QueryLookup
6161

6262
private final EntityManager em;
6363
private final JpaQueryMethodFactory queryMethodFactory;
64+
private final BeanFactory beanFactory;
6465

6566
/**
6667
* Creates a new {@link AbstractQueryLookupStrategy}.
6768
*
6869
* @param em must not be {@literal null}.
6970
* @param queryMethodFactory must not be {@literal null}.
7071
*/
71-
public AbstractQueryLookupStrategy(EntityManager em, JpaQueryMethodFactory queryMethodFactory) {
72+
public AbstractQueryLookupStrategy(EntityManager em, JpaQueryMethodFactory queryMethodFactory,
73+
BeanFactory beanFactory) {
7274

7375
Assert.notNull(em, "EntityManager must not be null!");
7476
Assert.notNull(queryMethodFactory, "JpaQueryMethodFactory must not be null!");
7577

7678
this.em = em;
7779
this.queryMethodFactory = queryMethodFactory;
80+
this.beanFactory = beanFactory;
7881
}
7982

8083
@Override
@@ -84,6 +87,10 @@ public final RepositoryQuery resolveQuery(Method method, RepositoryMetadata meta
8487
}
8588

8689
protected abstract RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em, NamedQueries namedQueries);
90+
91+
protected BeanFactory getBeanFactory() {
92+
return beanFactory;
93+
}
8794
}
8895

8996
/**
@@ -97,9 +104,9 @@ private static class CreateQueryLookupStrategy extends AbstractQueryLookupStrate
97104
private final EscapeCharacter escape;
98105

99106
public CreateQueryLookupStrategy(EntityManager em, JpaQueryMethodFactory queryMethodFactory,
100-
EscapeCharacter escape) {
107+
BeanFactory beanFactory, EscapeCharacter escape) {
101108

102-
super(em, queryMethodFactory);
109+
super(em, queryMethodFactory, beanFactory);
103110

104111
this.escape = escape;
105112
}
@@ -130,9 +137,9 @@ private static class DeclaredQueryLookupStrategy extends AbstractQueryLookupStra
130137
* @param evaluationContextProvider must not be {@literal null}.
131138
*/
132139
public DeclaredQueryLookupStrategy(EntityManager em, JpaQueryMethodFactory queryMethodFactory,
133-
QueryMethodEvaluationContextProvider evaluationContextProvider) {
140+
QueryMethodEvaluationContextProvider evaluationContextProvider, BeanFactory beanFactory) {
134141

135-
super(em, queryMethodFactory);
142+
super(em, queryMethodFactory, beanFactory);
136143

137144
this.evaluationContextProvider = evaluationContextProvider;
138145
}
@@ -152,14 +159,13 @@ protected RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em,
152159
}
153160

154161
return JpaQueryFactory.INSTANCE.fromMethodWithQueryString(method, em, method.getRequiredAnnotatedQuery(),
155-
getCountQuery(method, namedQueries, em),
156-
evaluationContextProvider);
162+
getCountQuery(method, namedQueries, em), evaluationContextProvider, getBeanFactory());
157163
}
158164

159165
String name = method.getNamedQueryName();
160166
if (namedQueries.hasQuery(name)) {
161-
return JpaQueryFactory.INSTANCE.fromMethodWithQueryString(method, em, namedQueries.getQuery(name), getCountQuery(method, namedQueries, em),
162-
evaluationContextProvider);
167+
return JpaQueryFactory.INSTANCE.fromMethodWithQueryString(method, em, namedQueries.getQuery(name),
168+
getCountQuery(method, namedQueries, em), evaluationContextProvider, getBeanFactory());
163169
}
164170

165171
RepositoryQuery query = NamedQuery.lookupFrom(method, em);
@@ -221,9 +227,9 @@ private static class CreateIfNotFoundQueryLookupStrategy extends AbstractQueryLo
221227
* @param lookupStrategy must not be {@literal null}.
222228
*/
223229
public CreateIfNotFoundQueryLookupStrategy(EntityManager em, JpaQueryMethodFactory queryMethodFactory,
224-
CreateQueryLookupStrategy createStrategy, DeclaredQueryLookupStrategy lookupStrategy) {
230+
CreateQueryLookupStrategy createStrategy, DeclaredQueryLookupStrategy lookupStrategy, BeanFactory beanFactory) {
225231

226-
super(em, queryMethodFactory);
232+
super(em, queryMethodFactory, beanFactory);
227233

228234
Assert.notNull(createStrategy, "CreateQueryLookupStrategy must not be null!");
229235
Assert.notNull(lookupStrategy, "DeclaredQueryLookupStrategy must not be null!");
@@ -253,20 +259,22 @@ protected RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em,
253259
* @param escape must not be {@literal null}.
254260
*/
255261
public static QueryLookupStrategy create(EntityManager em, JpaQueryMethodFactory queryMethodFactory,
256-
@Nullable Key key, QueryMethodEvaluationContextProvider evaluationContextProvider, EscapeCharacter escape) {
262+
@Nullable Key key, QueryMethodEvaluationContextProvider evaluationContextProvider, BeanFactory beanFactory,
263+
EscapeCharacter escape) {
257264

258265
Assert.notNull(em, "EntityManager must not be null!");
259266
Assert.notNull(evaluationContextProvider, "EvaluationContextProvider must not be null!");
260267

261268
switch (key != null ? key : Key.CREATE_IF_NOT_FOUND) {
262269
case CREATE:
263-
return new CreateQueryLookupStrategy(em, queryMethodFactory, escape);
270+
return new CreateQueryLookupStrategy(em, queryMethodFactory, beanFactory, escape);
264271
case USE_DECLARED_QUERY:
265-
return new DeclaredQueryLookupStrategy(em, queryMethodFactory, evaluationContextProvider);
272+
return new DeclaredQueryLookupStrategy(em, queryMethodFactory, evaluationContextProvider, beanFactory);
266273
case CREATE_IF_NOT_FOUND:
267274
return new CreateIfNotFoundQueryLookupStrategy(em, queryMethodFactory,
268-
new CreateQueryLookupStrategy(em, queryMethodFactory, escape),
269-
new DeclaredQueryLookupStrategy(em, queryMethodFactory, evaluationContextProvider));
275+
new CreateQueryLookupStrategy(em, queryMethodFactory, beanFactory, escape),
276+
new DeclaredQueryLookupStrategy(em, queryMethodFactory, evaluationContextProvider, beanFactory),
277+
beanFactory);
270278
default:
271279
throw new IllegalArgumentException(String.format("Unsupported query lookup strategy %s!", key));
272280
}

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

+4-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import jakarta.persistence.Query;
2020
import jakarta.persistence.Tuple;
2121

22+
import org.springframework.beans.factory.BeanFactory;
2223
import org.springframework.data.domain.Pageable;
2324
import org.springframework.data.domain.Sort;
2425
import org.springframework.data.repository.query.Parameters;
@@ -51,9 +52,10 @@ final class NativeJpaQuery extends AbstractStringBasedJpaQuery {
5152
* @param evaluationContextProvider
5253
*/
5354
public NativeJpaQuery(JpaQueryMethod method, EntityManager em, String queryString, @Nullable String countQueryString,
54-
QueryMethodEvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser) {
55+
QueryMethodEvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser,
56+
BeanFactory beanFactory) {
5557

56-
super(method, em, queryString, countQueryString, evaluationContextProvider, parser);
58+
super(method, em, queryString, countQueryString, evaluationContextProvider, parser, beanFactory);
5759

5860
Parameters<?, ?> parameters = method.getParameters();
5961

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

+8-4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import jakarta.persistence.EntityManager;
1919
import jakarta.persistence.Query;
2020

21+
import org.springframework.beans.factory.BeanFactory;
2122
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
2223
import org.springframework.data.repository.query.RepositoryQuery;
2324
import org.springframework.expression.spel.standard.SpelExpressionParser;
@@ -44,8 +45,10 @@ final class SimpleJpaQuery extends AbstractStringBasedJpaQuery {
4445
* @param parser must not be {@literal null}
4546
*/
4647
public SimpleJpaQuery(JpaQueryMethod method, EntityManager em, @Nullable String countQueryString,
47-
QueryMethodEvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser) {
48-
this(method, em, method.getRequiredAnnotatedQuery(), countQueryString, evaluationContextProvider, parser);
48+
QueryMethodEvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser,
49+
BeanFactory beanFactory) {
50+
this(method, em, method.getRequiredAnnotatedQuery(), countQueryString, evaluationContextProvider, parser,
51+
beanFactory);
4952
}
5053

5154
/**
@@ -59,9 +62,10 @@ public SimpleJpaQuery(JpaQueryMethod method, EntityManager em, @Nullable String
5962
* @param parser must not be {@literal null}
6063
*/
6164
public SimpleJpaQuery(JpaQueryMethod method, EntityManager em, String queryString, @Nullable String countQueryString,
62-
QueryMethodEvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser) {
65+
QueryMethodEvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser,
66+
BeanFactory beanFactory) {
6367

64-
super(method, em, queryString, countQueryString, evaluationContextProvider, parser);
68+
super(method, em, queryString, countQueryString, evaluationContextProvider, parser, beanFactory);
6569

6670
validateQuery(getQuery().getQueryString(), "Validation failed for query for method %s!", method);
6771

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@
1717

1818
import static org.springframework.data.querydsl.QuerydslUtils.*;
1919

20+
import jakarta.persistence.EntityManager;
21+
import jakarta.persistence.Tuple;
22+
2023
import java.io.Serializable;
2124
import java.lang.reflect.Method;
2225
import java.util.Optional;
2326
import java.util.stream.Stream;
2427

25-
import jakarta.persistence.EntityManager;
26-
import jakarta.persistence.Tuple;
27-
2828
import org.apache.commons.logging.Log;
2929
import org.apache.commons.logging.LogFactory;
3030
import org.springframework.beans.factory.BeanFactory;
@@ -199,7 +199,7 @@ protected Optional<QueryLookupStrategy> getQueryLookupStrategy(@Nullable Key key
199199
QueryMethodEvaluationContextProvider evaluationContextProvider) {
200200

201201
return Optional.of(JpaQueryLookupStrategy.create(entityManager, queryMethodFactory, key, evaluationContextProvider,
202-
escapeCharacter));
202+
getBeanFactory(), escapeCharacter));
203203
}
204204

205205
@Override

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727

2828
import org.junit.jupiter.api.Test;
2929
import org.junit.jupiter.api.extension.ExtendWith;
30+
import org.springframework.beans.factory.BeanFactory;
31+
import org.springframework.beans.factory.annotation.Autowired;
3032
import org.springframework.data.domain.Sort;
3133
import org.springframework.data.jpa.domain.sample.Role;
3234
import org.springframework.data.jpa.domain.sample.User;
@@ -51,6 +53,8 @@ public class AbstractStringBasedJpaQueryIntegrationTests {
5153

5254
@PersistenceContext EntityManager em;
5355

56+
@Autowired BeanFactory beanFactory;
57+
5458
@Test // DATAJPA-885
5559
void createsNormalQueryForJpaManagedReturnTypes() throws Exception {
5660

@@ -61,7 +65,7 @@ void createsNormalQueryForJpaManagedReturnTypes() throws Exception {
6165

6266
JpaQueryMethod method = getMethod("findRolesByEmailAddress", String.class);
6367
AbstractStringBasedJpaQuery jpaQuery = new SimpleJpaQuery(method, mock, null,
64-
QueryMethodEvaluationContextProvider.DEFAULT, new SpelExpressionParser());
68+
QueryMethodEvaluationContextProvider.DEFAULT, new SpelExpressionParser(), beanFactory);
6569

6670
jpaQuery.createJpaQuery(method.getAnnotatedQuery(), Sort.unsorted(), null,
6771
method.getResultProcessor().getReturnedType());

0 commit comments

Comments
 (0)