Skip to content

Commit 897ec5e

Browse files
committed
Allow to add a special parameter type from a Spring Data JPA extension
1 parent bf43e14 commit 897ec5e

File tree

7 files changed

+114
-19
lines changed

7 files changed

+114
-19
lines changed

src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ protected JpaParameters createFrom(List<JpaParameter> parameters) {
7373
* @author Thomas Darimont
7474
* @author Oliver Gierke
7575
*/
76-
static class JpaParameter extends Parameter {
76+
public static class JpaParameter extends Parameter {
7777

7878
private final @Nullable Temporal annotation;
7979
private @Nullable TemporalType temporalType;
@@ -83,7 +83,7 @@ static class JpaParameter extends Parameter {
8383
*
8484
* @param parameter must not be {@literal null}.
8585
*/
86-
JpaParameter(MethodParameter parameter) {
86+
protected JpaParameter(MethodParameter parameter) {
8787

8888
super(parameter);
8989

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

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -56,17 +56,21 @@ private abstract static class AbstractQueryLookupStrategy implements QueryLookup
5656

5757
private final EntityManager em;
5858
private final QueryExtractor provider;
59+
private final JpaQueryMethodFactory queryMethodFactory;
5960

6061
/**
6162
* Creates a new {@link AbstractQueryLookupStrategy}.
6263
*
6364
* @param em
6465
* @param extractor
66+
* @param queryMethodFactory
6567
*/
66-
public AbstractQueryLookupStrategy(EntityManager em, QueryExtractor extractor) {
68+
public AbstractQueryLookupStrategy(EntityManager em, QueryExtractor extractor,
69+
JpaQueryMethodFactory queryMethodFactory) {
6770

6871
this.em = em;
6972
this.provider = extractor;
73+
this.queryMethodFactory = queryMethodFactory;
7074
}
7175

7276
/*
@@ -76,7 +80,7 @@ public AbstractQueryLookupStrategy(EntityManager em, QueryExtractor extractor) {
7680
@Override
7781
public final RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, ProjectionFactory factory,
7882
NamedQueries namedQueries) {
79-
return resolveQuery(new JpaQueryMethod(method, metadata, factory, provider), em, namedQueries);
83+
return resolveQuery(queryMethodFactory.build(method, metadata, factory, provider), em, namedQueries);
8084
}
8185

8286
protected abstract RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em, NamedQueries namedQueries);
@@ -92,9 +96,10 @@ private static class CreateQueryLookupStrategy extends AbstractQueryLookupStrate
9296

9397
private final PersistenceProvider persistenceProvider;
9498

95-
public CreateQueryLookupStrategy(EntityManager em, QueryExtractor extractor) {
99+
public CreateQueryLookupStrategy(EntityManager em, QueryExtractor extractor,
100+
JpaQueryMethodFactory queryMethodFactory) {
96101

97-
super(em, extractor);
102+
super(em, extractor, queryMethodFactory);
98103
this.persistenceProvider = PersistenceProvider.fromEntityManager(em);
99104
}
100105

@@ -120,12 +125,13 @@ private static class DeclaredQueryLookupStrategy extends AbstractQueryLookupStra
120125
*
121126
* @param em
122127
* @param extractor
128+
* @param queryMethodFactory
123129
* @param evaluationContextProvider
124130
*/
125131
public DeclaredQueryLookupStrategy(EntityManager em, QueryExtractor extractor,
126-
QueryMethodEvaluationContextProvider evaluationContextProvider) {
132+
JpaQueryMethodFactory queryMethodFactory, QueryMethodEvaluationContextProvider evaluationContextProvider) {
127133

128-
super(em, extractor);
134+
super(em, extractor, queryMethodFactory);
129135
this.evaluationContextProvider = evaluationContextProvider;
130136
}
131137

@@ -183,13 +189,15 @@ private static class CreateIfNotFoundQueryLookupStrategy extends AbstractQueryLo
183189
*
184190
* @param em
185191
* @param extractor
192+
* @param queryMethodFactory
186193
* @param createStrategy
187194
* @param lookupStrategy
188195
*/
189196
public CreateIfNotFoundQueryLookupStrategy(EntityManager em, QueryExtractor extractor,
190-
CreateQueryLookupStrategy createStrategy, DeclaredQueryLookupStrategy lookupStrategy) {
197+
JpaQueryMethodFactory queryMethodFactory, CreateQueryLookupStrategy createStrategy,
198+
DeclaredQueryLookupStrategy lookupStrategy) {
191199

192-
super(em, extractor);
200+
super(em, extractor, queryMethodFactory);
193201

194202
this.createStrategy = createStrategy;
195203
this.lookupStrategy = lookupStrategy;
@@ -216,24 +224,26 @@ protected RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em,
216224
* @param em must not be {@literal null}.
217225
* @param key may be {@literal null}.
218226
* @param extractor must not be {@literal null}.
227+
* @param queryMethodFactory must not be {@literal null}.
219228
* @param evaluationContextProvider must not be {@literal null}.
220229
* @return
221230
*/
222231
public static QueryLookupStrategy create(EntityManager em, @Nullable Key key, QueryExtractor extractor,
223-
QueryMethodEvaluationContextProvider evaluationContextProvider) {
232+
JpaQueryMethodFactory queryMethodFactory, QueryMethodEvaluationContextProvider evaluationContextProvider) {
224233

225234
Assert.notNull(em, "EntityManager must not be null!");
226235
Assert.notNull(extractor, "QueryExtractor must not be null!");
227236
Assert.notNull(evaluationContextProvider, "EvaluationContextProvider must not be null!");
228237

229238
switch (key != null ? key : Key.CREATE_IF_NOT_FOUND) {
230239
case CREATE:
231-
return new CreateQueryLookupStrategy(em, extractor);
240+
return new CreateQueryLookupStrategy(em, extractor, queryMethodFactory);
232241
case USE_DECLARED_QUERY:
233-
return new DeclaredQueryLookupStrategy(em, extractor, evaluationContextProvider);
242+
return new DeclaredQueryLookupStrategy(em, extractor, queryMethodFactory, evaluationContextProvider);
234243
case CREATE_IF_NOT_FOUND:
235-
return new CreateIfNotFoundQueryLookupStrategy(em, extractor, new CreateQueryLookupStrategy(em, extractor),
236-
new DeclaredQueryLookupStrategy(em, extractor, evaluationContextProvider));
244+
return new CreateIfNotFoundQueryLookupStrategy(em, extractor, queryMethodFactory,
245+
new CreateQueryLookupStrategy(em, extractor, queryMethodFactory),
246+
new DeclaredQueryLookupStrategy(em, extractor, queryMethodFactory, evaluationContextProvider));
237247
default:
238248
throw new IllegalArgumentException(String.format("Unsupported query lookup strategy %s!", key));
239249
}

src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ public class JpaQueryMethod extends QueryMethod {
8888
* @param factory must not be {@literal null}
8989
* @param extractor must not be {@literal null}
9090
*/
91-
public JpaQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory factory,
91+
protected JpaQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory factory,
9292
QueryExtractor extractor) {
9393

9494
super(method, metadata, factory);
@@ -403,4 +403,19 @@ StoredProcedureAttributes getProcedureAttributes() {
403403

404404
return storedProcedureAttributes;
405405
}
406+
407+
public static class Factory implements JpaQueryMethodFactory {
408+
409+
public static final Factory INSTANCE = new Factory();
410+
411+
private Factory() {
412+
413+
}
414+
415+
@Override
416+
public JpaQueryMethod build(Method method, RepositoryMetadata metadata, ProjectionFactory factory,
417+
QueryExtractor extractor) {
418+
return new JpaQueryMethod(method, metadata, factory, extractor);
419+
}
420+
}
406421
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2012-2019 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+
* http://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 java.lang.reflect.Method;
19+
20+
import org.springframework.data.jpa.provider.QueryExtractor;
21+
import org.springframework.data.projection.ProjectionFactory;
22+
import org.springframework.data.repository.core.RepositoryMetadata;
23+
24+
/**
25+
* @author Reda.Housni-Alaoui
26+
*/
27+
public interface JpaQueryMethodFactory {
28+
29+
/**
30+
* Creates a {@link JpaQueryMethod}.
31+
*
32+
* @param method must not be {@literal null}
33+
* @param metadata must not be {@literal null}
34+
* @param factory must not be {@literal null}
35+
* @param extractor must not be {@literal null}
36+
*/
37+
JpaQueryMethod build(Method method, RepositoryMetadata metadata, ProjectionFactory factory, QueryExtractor extractor);
38+
39+
}

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.springframework.data.jpa.repository.query.AbstractJpaQuery;
3535
import org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy;
3636
import org.springframework.data.jpa.repository.query.JpaQueryMethod;
37+
import org.springframework.data.jpa.repository.query.JpaQueryMethodFactory;
3738
import org.springframework.data.jpa.util.JpaMetamodel;
3839
import org.springframework.data.projection.ProjectionFactory;
3940
import org.springframework.data.querydsl.EntityPathResolver;
@@ -68,6 +69,7 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport {
6869
private final CrudMethodMetadataPostProcessor crudMethodMetadataPostProcessor;
6970

7071
private EntityPathResolver entityPathResolver;
72+
private JpaQueryMethodFactory queryMethodFactory;
7173

7274
/**
7375
* Creates a new {@link JpaRepositoryFactory}.
@@ -82,6 +84,7 @@ public JpaRepositoryFactory(EntityManager entityManager) {
8284
this.extractor = PersistenceProvider.fromEntityManager(entityManager);
8385
this.crudMethodMetadataPostProcessor = new CrudMethodMetadataPostProcessor();
8486
this.entityPathResolver = SimpleEntityPathResolver.INSTANCE;
87+
this.queryMethodFactory = JpaQueryMethod.Factory.INSTANCE;
8588

8689
addRepositoryProxyPostProcessor(crudMethodMetadataPostProcessor);
8790

@@ -113,6 +116,17 @@ public void setEntityPathResolver(EntityPathResolver entityPathResolver) {
113116
this.entityPathResolver = entityPathResolver;
114117
}
115118

119+
/**
120+
* Configures the {@link JpaQueryMethodFactory} to be used. Defaults to {@link JpaQueryMethod.Factory#INSTANCE}.
121+
*
122+
* @param queryMethodFactory must not be {@literal null}.
123+
*/
124+
public void setQueryMethodFactory(JpaQueryMethodFactory queryMethodFactory) {
125+
Assert.notNull(queryMethodFactory, "QueryMethodFactory must not be null!");
126+
127+
this.queryMethodFactory = queryMethodFactory;
128+
}
129+
116130
/*
117131
* (non-Javadoc)
118132
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getTargetRepository(org.springframework.data.repository.core.RepositoryMetadata)
@@ -174,7 +188,8 @@ protected ProjectionFactory getProjectionFactory(ClassLoader classLoader, BeanFa
174188
@Override
175189
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(@Nullable Key key,
176190
QueryMethodEvaluationContextProvider evaluationContextProvider) {
177-
return Optional.of(JpaQueryLookupStrategy.create(entityManager, key, extractor, evaluationContextProvider));
191+
return Optional.of(
192+
JpaQueryLookupStrategy.create(entityManager, key, extractor, queryMethodFactory, evaluationContextProvider));
178193
}
179194

180195
/*

src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
import org.springframework.beans.factory.ObjectProvider;
2222
import org.springframework.beans.factory.annotation.Autowired;
23+
import org.springframework.data.jpa.repository.query.JpaQueryMethod;
24+
import org.springframework.data.jpa.repository.query.JpaQueryMethodFactory;
2325
import org.springframework.data.mapping.context.MappingContext;
2426
import org.springframework.data.querydsl.EntityPathResolver;
2527
import org.springframework.data.querydsl.SimpleEntityPathResolver;
@@ -44,6 +46,7 @@ public class JpaRepositoryFactoryBean<T extends Repository<S, ID>, S, ID>
4446

4547
private @Nullable EntityManager entityManager;
4648
private EntityPathResolver entityPathResolver;
49+
private JpaQueryMethodFactory queryMethodFactory;
4750

4851
/**
4952
* Creates a new {@link JpaRepositoryFactoryBean} for the given repository interface.
@@ -84,6 +87,17 @@ public void setEntityPathResolver(ObjectProvider<EntityPathResolver> resolver) {
8487
this.entityPathResolver = resolver.getIfAvailable(() -> SimpleEntityPathResolver.INSTANCE);
8588
}
8689

90+
/**
91+
* Configures the {@link JpaQueryMethodFactory} to be used. Will expect a canonical bean to be present but fallback to
92+
* {@link JpaQueryMethod.Factory#INSTANCE} in case none is available.
93+
*
94+
* @param resolver must not be {@literal null}.
95+
*/
96+
@Autowired
97+
public void setQueryMethodFactory(ObjectProvider<JpaQueryMethodFactory> resolver) {
98+
this.queryMethodFactory = resolver.getIfAvailable(() -> JpaQueryMethod.Factory.INSTANCE);
99+
}
100+
87101
/*
88102
* (non-Javadoc)
89103
* @see org.springframework.data.repository.core.support.TransactionalRepositoryFactoryBeanSupport#doCreateRepositoryFactory()
@@ -103,6 +117,7 @@ protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityM
103117

104118
JpaRepositoryFactory jpaRepositoryFactory = new JpaRepositoryFactory(entityManager);
105119
jpaRepositoryFactory.setEntityPathResolver(entityPathResolver);
120+
jpaRepositoryFactory.setQueryMethodFactory(queryMethodFactory);
106121

107122
return jpaRepositoryFactory;
108123
}

src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
public class JpaQueryLookupStrategyUnitTests {
5656

5757
private static final QueryMethodEvaluationContextProvider EVALUATION_CONTEXT_PROVIDER = QueryMethodEvaluationContextProvider.DEFAULT;
58+
private static final JpaQueryMethodFactory QUERY_METHOD_FACTORY = JpaQueryMethod.Factory.INSTANCE;
5859

5960
@Mock EntityManager em;
6061
@Mock EntityManagerFactory emf;
@@ -76,7 +77,7 @@ public void setUp() {
7677
public void invalidAnnotatedQueryCausesException() throws Exception {
7778

7879
QueryLookupStrategy strategy = JpaQueryLookupStrategy.create(em, Key.CREATE_IF_NOT_FOUND, extractor,
79-
EVALUATION_CONTEXT_PROVIDER);
80+
QUERY_METHOD_FACTORY, EVALUATION_CONTEXT_PROVIDER);
8081
Method method = UserRepository.class.getMethod("findByFoo", String.class);
8182
RepositoryMetadata metadata = new DefaultRepositoryMetadata(UserRepository.class);
8283

@@ -92,7 +93,7 @@ public void invalidAnnotatedQueryCausesException() throws Exception {
9293
public void sholdThrowMorePreciseExceptionIfTryingToUsePaginationInNativeQueries() throws Exception {
9394

9495
QueryLookupStrategy strategy = JpaQueryLookupStrategy.create(em, Key.CREATE_IF_NOT_FOUND, extractor,
95-
EVALUATION_CONTEXT_PROVIDER);
96+
QUERY_METHOD_FACTORY, EVALUATION_CONTEXT_PROVIDER);
9697
Method method = UserRepository.class.getMethod("findByInvalidNativeQuery", String.class, Sort.class);
9798
RepositoryMetadata metadata = new DefaultRepositoryMetadata(UserRepository.class);
9899

0 commit comments

Comments
 (0)