Skip to content

Commit d409c20

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 76538d8 commit d409c20

File tree

49 files changed

+1065
-701
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

+1065
-701
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
}
@@ -160,14 +155,14 @@ protected Query doCreateCountQuery(JpaParametersParameterAccessor accessor) {
160155
/**
161156
* @return the query
162157
*/
163-
public DeclaredQuery getQuery() {
158+
public EntityQuery getQuery() {
164159
return query;
165160
}
166161

167162
/**
168163
* @return the countQuery
169164
*/
170-
public DeclaredQuery getCountQuery() {
165+
public IntrospectedQuery getCountQuery() {
171166
return countQuery.get();
172167
}
173168

@@ -208,16 +203,15 @@ protected String potentiallyRewriteQuery(String originalQuery, Sort sort, @Nulla
208203
}
209204

210205
String applySorting(CachableQuery cachableQuery) {
211-
212-
return QueryEnhancerFactory.forQuery(cachableQuery.getDeclaredQuery())
206+
return cachableQuery.getDeclaredQuery().getQueryEnhancer()
213207
.rewrite(new DefaultQueryRewriteInformation(cachableQuery.getSort(), cachableQuery.getReturnedType()));
214208
}
215209

216210
/**
217211
* Query Sort Rewriter interface.
218212
*/
219213
interface QuerySortRewriter {
220-
String getSorted(DeclaredQuery query, Sort sort, ReturnedType returnedType);
214+
String getSorted(StringQuery query, Sort sort, ReturnedType returnedType);
221215
}
222216

223217
/**
@@ -227,25 +221,24 @@ enum SimpleQuerySortRewriter implements QuerySortRewriter {
227221

228222
INSTANCE;
229223

230-
public String getSorted(DeclaredQuery query, Sort sort, ReturnedType returnedType) {
231-
232-
return QueryEnhancerFactory.forQuery(query).rewrite(new DefaultQueryRewriteInformation(sort, returnedType));
224+
public String getSorted(StringQuery query, Sort sort, ReturnedType returnedType) {
225+
return query.getQueryEnhancer().rewrite(new DefaultQueryRewriteInformation(sort, returnedType));
233226
}
234227
}
235228

236229
static class UnsortedCachingQuerySortRewriter implements QuerySortRewriter {
237230

238231
private volatile @Nullable String cachedQueryString;
239232

240-
public String getSorted(DeclaredQuery query, Sort sort, ReturnedType returnedType) {
233+
public String getSorted(StringQuery query, Sort sort, ReturnedType returnedType) {
241234

242235
if (sort.isSorted()) {
243236
throw new UnsupportedOperationException("NoOpQueryCache does not support sorting");
244237
}
245238

246239
String cachedQueryString = this.cachedQueryString;
247240
if (cachedQueryString == null) {
248-
this.cachedQueryString = cachedQueryString = QueryEnhancerFactory.forQuery(query)
241+
this.cachedQueryString = cachedQueryString = query.getQueryEnhancer()
249242
.rewrite(new DefaultQueryRewriteInformation(sort, returnedType));
250243
}
251244

@@ -264,7 +257,7 @@ class CachingQuerySortRewriter implements QuerySortRewriter {
264257
private volatile @Nullable String cachedQueryString;
265258

266259
@Override
267-
public String getSorted(DeclaredQuery query, Sort sort, ReturnedType returnedType) {
260+
public String getSorted(StringQuery query, Sort sort, ReturnedType returnedType) {
268261

269262
if (sort.isUnsorted()) {
270263

@@ -289,21 +282,21 @@ public String getSorted(DeclaredQuery query, Sort sort, ReturnedType returnedTyp
289282
*/
290283
static class CachableQuery {
291284

292-
private final DeclaredQuery declaredQuery;
285+
private final StringQuery query;
293286
private final String queryString;
294287
private final Sort sort;
295288
private final ReturnedType returnedType;
296289

297-
CachableQuery(DeclaredQuery query, Sort sort, ReturnedType returnedType) {
290+
CachableQuery(StringQuery query, Sort sort, ReturnedType returnedType) {
298291

299-
this.declaredQuery = query;
292+
this.query = query;
300293
this.queryString = query.getQueryString();
301294
this.sort = sort;
302295
this.returnedType = returnedType;
303296
}
304297

305-
DeclaredQuery getDeclaredQuery() {
306-
return declaredQuery;
298+
StringQuery getDeclaredQuery() {
299+
return query;
307300
}
308301

309302
Sort getSort() {

0 commit comments

Comments
 (0)