Skip to content

Commit 38f5c11

Browse files
committed
Introduce QueryEnhancerSelector to configure which QueryEnhancerFactory to use.
Introduce QueryEnhancerSelector to EnableJpaRepositories. Also, split DeclaredQuery into two interfaces to resolve the inner cycle of query introspection while just a value object is being created. Introduce JpaQueryConfiguration to capture a multitude of configuration elements. Remove `spring.data.jpa.query.native.parser` option introduced earlier with #2989 Closes #3622 Original pull request: #3527
1 parent a6f8716 commit 38f5c11

File tree

49 files changed

+1064
-704
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1064
-704
lines changed

spring-data-jpa/src/jmh/java/org/springframework/data/jpa/repository/query/HqlParserBenchmarks.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ OR TREAT(p AS SmallProject).name LIKE 'Persist%'
5555
OR p.description LIKE "cost overrun"
5656
""";
5757

58-
query = DeclaredQuery.of(s, false);
59-
enhancer = QueryEnhancerFactory.forQuery(query);
58+
query = DeclaredQuery.ofJpql(s);
59+
enhancer = QueryEnhancerFactory.forQuery(query).create(query);
6060
}
6161
}
6262

spring-data-jpa/src/jmh/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancerBenchmarks.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public void doSetup() throws IOException {
5656
select SOME_COLUMN from SOME_OTHER_TABLE where REPORTING_DATE = :REPORTING_DATE
5757
union select SOME_COLUMN from SOME_OTHER_OTHER_TABLE""";
5858

59-
enhancer = new JSqlParserQueryEnhancer(DeclaredQuery.of(s, true));
59+
enhancer = new JSqlParserQueryEnhancer(DeclaredQuery.ofNative(s));
6060
}
6161
}
6262

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaRepositories.java

+13-16
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.springframework.context.annotation.ComponentScan.Filter;
2929
import org.springframework.context.annotation.Import;
3030
import org.springframework.context.annotation.Lazy;
31+
import org.springframework.data.jpa.repository.query.QueryEnhancerSelector;
3132
import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
3233
import org.springframework.data.repository.config.BootstrapMode;
3334
import org.springframework.data.repository.config.DefaultRepositoryBaseClass;
@@ -83,46 +84,39 @@
8384
* Returns the postfix to be used when looking up custom repository implementations. Defaults to {@literal Impl}. So
8485
* for a repository named {@code PersonRepository} the corresponding implementation class will be looked up scanning
8586
* for {@code PersonRepositoryImpl}.
86-
*
87-
* @return
8887
*/
8988
String repositoryImplementationPostfix() default "Impl";
9089

9190
/**
9291
* Configures the location of where to find the Spring Data named queries properties file. Will default to
9392
* {@code META-INF/jpa-named-queries.properties}.
94-
*
95-
* @return
9693
*/
9794
String namedQueriesLocation() default "";
9895

9996
/**
10097
* Returns the key of the {@link QueryLookupStrategy} to be used for lookup queries for query methods. Defaults to
10198
* {@link Key#CREATE_IF_NOT_FOUND}.
102-
*
103-
* @return
10499
*/
105100
Key queryLookupStrategy() default Key.CREATE_IF_NOT_FOUND;
106101

107102
/**
108103
* Returns the {@link FactoryBean} class to be used for each repository instance. Defaults to
109104
* {@link JpaRepositoryFactoryBean}.
110-
*
111-
* @return
112105
*/
113106
Class<?> repositoryFactoryBeanClass() default JpaRepositoryFactoryBean.class;
114107

115108
/**
116109
* Configure the repository base class to be used to create repository proxies for this particular configuration.
117110
*
118-
* @return
119111
* @since 1.9
120112
*/
121113
Class<?> repositoryBaseClass() default DefaultRepositoryBaseClass.class;
122114

123115
/**
124116
* Configure a specific {@link BeanNameGenerator} to be used when creating the repository beans.
125-
* @return the {@link BeanNameGenerator} to be used or the base {@link BeanNameGenerator} interface to indicate context default.
117+
*
118+
* @return the {@link BeanNameGenerator} to be used or the base {@link BeanNameGenerator} interface to indicate
119+
* context default.
126120
* @since 3.4
127121
*/
128122
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
@@ -132,22 +126,18 @@
132126
/**
133127
* Configures the name of the {@link EntityManagerFactory} bean definition to be used to create repositories
134128
* discovered through this annotation. Defaults to {@code entityManagerFactory}.
135-
*
136-
* @return
137129
*/
138130
String entityManagerFactoryRef() default "entityManagerFactory";
139131

140132
/**
141133
* Configures the name of the {@link PlatformTransactionManager} bean definition to be used to create repositories
142134
* discovered through this annotation. Defaults to {@code transactionManager}.
143-
*
144-
* @return
145135
*/
146136
String transactionManagerRef() default "transactionManager";
147137

148138
/**
149139
* Configures whether nested repository-interfaces (e.g. defined as inner classes) should be discovered by the
150-
* repositories infrastructure.
140+
* repository infrastructure.
151141
*/
152142
boolean considerNestedRepositories() default false;
153143

@@ -169,7 +159,6 @@
169159
* completed its bootstrap. {@link BootstrapMode#DEFERRED} is fundamentally the same as {@link BootstrapMode#LAZY},
170160
* but triggers repository initialization when the application context finishes its bootstrap.
171161
*
172-
* @return
173162
* @since 2.1
174163
*/
175164
BootstrapMode bootstrapMode() default BootstrapMode.DEFAULT;
@@ -181,4 +170,12 @@
181170
* @return a single character used for escaping.
182171
*/
183172
char escapeCharacter() default '\\';
173+
174+
/**
175+
* Configures the {@link QueryEnhancerSelector} to select a query enhancer for query introspection and transformation.
176+
*
177+
* @return a {@link QueryEnhancerSelector} class providing a no-args constructor.
178+
* @since 4.0
179+
*/
180+
Class<? extends QueryEnhancerSelector> queryEnhancerSelector() default QueryEnhancerSelector.DefaultQueryEnhancerSelector.class;
184181
}

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java

+5
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,11 @@ public void postProcess(BeanDefinitionBuilder builder, RepositoryConfigurationSo
122122
}
123123
builder.addPropertyValue(ESCAPE_CHARACTER_PROPERTY, getEscapeCharacter(source).orElse('\\'));
124124
builder.addPropertyReference("mappingContext", JPA_MAPPING_CONTEXT_BEAN_NAME);
125+
126+
if (source instanceof AnnotationRepositoryConfigurationSource) {
127+
builder.addPropertyValue("queryEnhancerSelector",
128+
source.getAttribute("queryEnhancerSelector", Class.class).orElse(null));
129+
}
125130
}
126131

127132
@Override

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

+26-33
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@
4949
*/
5050
abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery {
5151

52-
private final DeclaredQuery query;
53-
private final Lazy<DeclaredQuery> countQuery;
52+
private final StringQuery query;
53+
private final Lazy<IntrospectedQuery> countQuery;
5454
private final ValueExpressionDelegate valueExpressionDelegate;
5555
private final QueryRewriter queryRewriter;
5656
private final QuerySortRewriter querySortRewriter;
@@ -65,37 +65,32 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery {
6565
* @param em must not be {@literal null}.
6666
* @param queryString must not be {@literal null}.
6767
* @param countQueryString must not be {@literal null}.
68-
* @param queryRewriter must not be {@literal null}.
69-
* @param valueExpressionDelegate must not be {@literal null}.
68+
* @param queryConfiguration must not be {@literal null}.
7069
*/
7170
public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, String queryString,
72-
@Nullable String countQueryString, QueryRewriter queryRewriter, ValueExpressionDelegate valueExpressionDelegate) {
71+
@Nullable String countQueryString, JpaQueryConfiguration queryConfiguration) {
7372

7473
super(method, em);
7574

7675
Assert.hasText(queryString, "Query string must not be null or empty");
77-
Assert.notNull(valueExpressionDelegate, "ValueExpressionDelegate must not be null");
78-
Assert.notNull(queryRewriter, "QueryRewriter must not be null");
76+
Assert.notNull(queryConfiguration, "JpaQueryConfiguration must not be null");
7977

80-
this.valueExpressionDelegate = valueExpressionDelegate;
78+
this.valueExpressionDelegate = queryConfiguration.getValueExpressionDelegate();
8179
this.valueExpressionContextProvider = valueExpressionDelegate.createValueContextProvider(method.getParameters());
82-
this.query = new ExpressionBasedStringQuery(queryString, method.getEntityInformation(), valueExpressionDelegate,
83-
method.isNativeQuery());
80+
this.query = ExpressionBasedStringQuery.create(queryString, method, queryConfiguration);
8481

8582
this.countQuery = Lazy.of(() -> {
8683

8784
if (StringUtils.hasText(countQueryString)) {
88-
89-
return new ExpressionBasedStringQuery(countQueryString, method.getEntityInformation(), valueExpressionDelegate,
90-
method.isNativeQuery());
85+
return ExpressionBasedStringQuery.create(countQueryString, method, queryConfiguration);
9186
}
9287

93-
return query.deriveCountQuery(method.getCountQueryProjection());
88+
return this.query.deriveCountQuery(method.getCountQueryProjection());
9489
});
9590

9691
this.countParameterBinder = Lazy.of(() -> this.createBinder(this.countQuery.get()));
9792

98-
this.queryRewriter = queryRewriter;
93+
this.queryRewriter = queryConfiguration.getQueryRewriter(method);
9994

10095
JpaParameters parameters = method.getParameters();
10196

@@ -109,7 +104,7 @@ public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, Stri
109104
}
110105
}
111106

112-
Assert.isTrue(method.isNativeQuery() || !query.usesJdbcStyleParameters(),
107+
Assert.isTrue(method.isNativeQuery() || !this.query.usesJdbcStyleParameters(),
113108
"JDBC style parameters (?) are not supported for JPA queries");
114109
}
115110

@@ -136,7 +131,7 @@ protected ParameterBinder createBinder() {
136131
return createBinder(query);
137132
}
138133

139-
protected ParameterBinder createBinder(DeclaredQuery query) {
134+
protected ParameterBinder createBinder(IntrospectedQuery query) {
140135
return ParameterBinderFactory.createQueryAwareBinder(getQueryMethod().getParameters(), query,
141136
valueExpressionDelegate, valueExpressionContextProvider);
142137
}
@@ -162,14 +157,14 @@ protected Query doCreateCountQuery(JpaParametersParameterAccessor accessor) {
162157
/**
163158
* @return the query
164159
*/
165-
public DeclaredQuery getQuery() {
160+
public EntityQuery getQuery() {
166161
return query;
167162
}
168163

169164
/**
170165
* @return the countQuery
171166
*/
172-
public DeclaredQuery getCountQuery() {
167+
public IntrospectedQuery getCountQuery() {
173168
return countQuery.get();
174169
}
175170

@@ -211,16 +206,15 @@ protected String potentiallyRewriteQuery(String originalQuery, Sort sort, @Nulla
211206
}
212207

213208
String applySorting(CachableQuery cachableQuery) {
214-
215-
return QueryEnhancerFactory.forQuery(cachableQuery.getDeclaredQuery())
209+
return cachableQuery.getDeclaredQuery().getQueryEnhancer()
216210
.rewrite(new DefaultQueryRewriteInformation(cachableQuery.getSort(), cachableQuery.getReturnedType()));
217211
}
218212

219213
/**
220214
* Query Sort Rewriter interface.
221215
*/
222216
interface QuerySortRewriter {
223-
String getSorted(DeclaredQuery query, Sort sort, ReturnedType returnedType);
217+
String getSorted(StringQuery query, Sort sort, ReturnedType returnedType);
224218
}
225219

226220
/**
@@ -230,25 +224,24 @@ enum SimpleQuerySortRewriter implements QuerySortRewriter {
230224

231225
INSTANCE;
232226

233-
public String getSorted(DeclaredQuery query, Sort sort, ReturnedType returnedType) {
234-
235-
return QueryEnhancerFactory.forQuery(query).rewrite(new DefaultQueryRewriteInformation(sort, returnedType));
227+
public String getSorted(StringQuery query, Sort sort, ReturnedType returnedType) {
228+
return query.getQueryEnhancer().rewrite(new DefaultQueryRewriteInformation(sort, returnedType));
236229
}
237230
}
238231

239232
static class UnsortedCachingQuerySortRewriter implements QuerySortRewriter {
240233

241234
private volatile @Nullable String cachedQueryString;
242235

243-
public String getSorted(DeclaredQuery query, Sort sort, ReturnedType returnedType) {
236+
public String getSorted(StringQuery query, Sort sort, ReturnedType returnedType) {
244237

245238
if (sort.isSorted()) {
246239
throw new UnsupportedOperationException("NoOpQueryCache does not support sorting");
247240
}
248241

249242
String cachedQueryString = this.cachedQueryString;
250243
if (cachedQueryString == null) {
251-
this.cachedQueryString = cachedQueryString = QueryEnhancerFactory.forQuery(query)
244+
this.cachedQueryString = cachedQueryString = query.getQueryEnhancer()
252245
.rewrite(new DefaultQueryRewriteInformation(sort, returnedType));
253246
}
254247

@@ -267,7 +260,7 @@ class CachingQuerySortRewriter implements QuerySortRewriter {
267260
private volatile @Nullable String cachedQueryString;
268261

269262
@Override
270-
public String getSorted(DeclaredQuery query, Sort sort, ReturnedType returnedType) {
263+
public String getSorted(StringQuery query, Sort sort, ReturnedType returnedType) {
271264

272265
if (sort.isUnsorted()) {
273266

@@ -292,21 +285,21 @@ public String getSorted(DeclaredQuery query, Sort sort, ReturnedType returnedTyp
292285
*/
293286
static class CachableQuery {
294287

295-
private final DeclaredQuery declaredQuery;
288+
private final StringQuery query;
296289
private final String queryString;
297290
private final Sort sort;
298291
private final ReturnedType returnedType;
299292

300-
CachableQuery(DeclaredQuery query, Sort sort, ReturnedType returnedType) {
293+
CachableQuery(StringQuery query, Sort sort, ReturnedType returnedType) {
301294

302-
this.declaredQuery = query;
295+
this.query = query;
303296
this.queryString = query.getQueryString();
304297
this.sort = sort;
305298
this.returnedType = returnedType;
306299
}
307300

308-
DeclaredQuery getDeclaredQuery() {
309-
return declaredQuery;
301+
StringQuery getDeclaredQuery() {
302+
return query;
310303
}
311304

312305
Sort getSort() {

0 commit comments

Comments
 (0)