Skip to content

Commit 9a3b5de

Browse files
committed
Polishing.
Remove unused methods, fix naming, group DeclaredQuery implementations in DeclaredQueries.
1 parent ca643c9 commit 9a3b5de

30 files changed

+457
-500
lines changed

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
import org.openjdk.jmh.annotations.Warmup;
2828

2929
import org.springframework.data.domain.Sort;
30+
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
31+
import org.springframework.data.repository.query.ReturnedType;
3032

3133
/**
3234
* @author Mark Paluch
@@ -44,6 +46,7 @@ public static class BenchmarkParameters {
4446
DeclaredQuery query;
4547
Sort sort = Sort.by("foo");
4648
QueryEnhancer enhancer;
49+
QueryEnhancer.QueryRewriteInformation rewriteInformation;
4750

4851
@Setup(Level.Iteration)
4952
public void doSetup() {
@@ -57,12 +60,14 @@ OR TREAT(p AS SmallProject).name LIKE 'Persist%'
5760

5861
query = DeclaredQuery.jpqlQuery(s);
5962
enhancer = QueryEnhancerFactory.forQuery(query).create(query);
63+
rewriteInformation = new DefaultQueryRewriteInformation(sort,
64+
ReturnedType.of(Object.class, Object.class, new SpelAwareProxyProjectionFactory()));
6065
}
6166
}
6267

6368
@Benchmark
6469
public Object measure(BenchmarkParameters parameters) {
65-
return parameters.enhancer.applySorting(parameters.sort);
70+
return parameters.enhancer.rewrite(parameters.rewriteInformation);
6671
}
6772

6873
}

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import org.openjdk.jmh.annotations.Warmup;
3030

3131
import org.springframework.data.domain.Sort;
32+
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
33+
import org.springframework.data.repository.query.ReturnedType;
3234

3335
/**
3436
* @author Mark Paluch
@@ -46,6 +48,7 @@ public static class BenchmarkParameters {
4648
JSqlParserQueryEnhancer enhancer;
4749
Sort sort = Sort.by("foo");
4850
private byte[] serialized;
51+
private QueryEnhancer.QueryRewriteInformation rewriteInformation;
4952

5053
@Setup(Level.Iteration)
5154
public void doSetup() throws IOException {
@@ -57,12 +60,14 @@ public void doSetup() throws IOException {
5760
union select SOME_COLUMN from SOME_OTHER_OTHER_TABLE""";
5861

5962
enhancer = new JSqlParserQueryEnhancer(DeclaredQuery.nativeQuery(s));
63+
rewriteInformation = new DefaultQueryRewriteInformation(sort,
64+
ReturnedType.of(Object.class, Object.class, new SpelAwareProxyProjectionFactory()));
6065
}
6166
}
6267

6368
@Benchmark
6469
public Object applySortWithParsing(BenchmarkParameters p) {
65-
return p.enhancer.applySorting(p.sort);
70+
return p.enhancer.rewrite(p.rewriteInformation);
6671
}
6772

6873
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,4 +178,5 @@
178178
* @since 4.0
179179
*/
180180
Class<? extends QueryEnhancerSelector> queryEnhancerSelector() default QueryEnhancerSelector.DefaultQueryEnhancerSelector.class;
181+
181182
}

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

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import org.springframework.data.util.Lazy;
3333
import org.springframework.util.Assert;
3434
import org.springframework.util.ConcurrentLruCache;
35-
import org.springframework.util.StringUtils;
3635

3736
/**
3837
* Base class for {@link String} based JPA queries.
@@ -64,26 +63,42 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery {
6463
* @param method must not be {@literal null}.
6564
* @param em must not be {@literal null}.
6665
* @param queryString must not be {@literal null}.
67-
* @param countQueryString must not be {@literal null}.
66+
* @param countQuery can be {@literal null} if not defined.
6867
* @param queryConfiguration must not be {@literal null}.
6968
*/
70-
public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, String queryString,
69+
AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, String queryString,
7170
@Nullable String countQueryString, JpaQueryConfiguration queryConfiguration) {
71+
this(method, em, method.getDeclaredQuery(queryString),
72+
countQueryString != null ? method.getDeclaredQuery(countQueryString) : null, queryConfiguration);
73+
}
74+
75+
/**
76+
* Creates a new {@link AbstractStringBasedJpaQuery} from the given {@link JpaQueryMethod}, {@link EntityManager} and
77+
* query {@link String}.
78+
*
79+
* @param method must not be {@literal null}.
80+
* @param em must not be {@literal null}.
81+
* @param query must not be {@literal null}.
82+
* @param countQuery can be {@literal null}.
83+
* @param queryConfiguration must not be {@literal null}.
84+
*/
85+
public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, DeclaredQuery query,
86+
@Nullable DeclaredQuery countQuery, JpaQueryConfiguration queryConfiguration) {
7287

7388
super(method, em);
7489

75-
Assert.hasText(queryString, "Query string must not be null or empty");
90+
Assert.notNull(query, "Query must not be null");
7691
Assert.notNull(queryConfiguration, "JpaQueryConfiguration must not be null");
7792

7893
this.valueExpressionDelegate = queryConfiguration.getValueExpressionDelegate();
7994
this.valueExpressionContextProvider = valueExpressionDelegate.createValueContextProvider(method.getParameters());
8095

81-
this.query = TemplatedQuery.create(queryString, method, queryConfiguration);
96+
this.query = TemplatedQuery.create(query, method.getEntityInformation(), queryConfiguration);
8297

8398
this.countQuery = Lazy.of(() -> {
8499

85-
if (StringUtils.hasText(countQueryString)) {
86-
return TemplatedQuery.create(countQueryString, method, queryConfiguration);
100+
if (countQuery != null) {
101+
return TemplatedQuery.create(countQuery, method.getEntityInformation(), queryConfiguration);
87102
}
88103

89104
return this.query.deriveCountQuery(method.getCountQueryProjection());
@@ -119,15 +134,15 @@ public Query doCreateQuery(JpaParametersParameterAccessor accessor) {
119134
Sort sort = accessor.getSort();
120135
ResultProcessor processor = getQueryMethod().getResultProcessor().withDynamicProjection(accessor);
121136
ReturnedType returnedType = processor.getReturnedType();
122-
QueryProvider sortedQueryString = getSortedQueryString(sort, returnedType);
123-
Query query = createJpaQuery(sortedQueryString, sort, accessor.getPageable(), returnedType);
137+
QueryProvider sortedQuery = getSortedQuery(sort, returnedType);
138+
Query query = createJpaQuery(sortedQuery, sort, accessor.getPageable(), returnedType);
124139

125140
// it is ok to reuse the binding contained in the ParameterBinder, although we create a new query String because the
126141
// parameters in the query do not change.
127142
return parameterBinder.get().bindAndPrepare(query, accessor);
128143
}
129144

130-
QueryProvider getSortedQueryString(Sort sort, ReturnedType returnedType) {
145+
QueryProvider getSortedQuery(Sort sort, ReturnedType returnedType) {
131146
return querySortRewriter.getSorted(query, sort, returnedType);
132147
}
133148

@@ -175,20 +190,20 @@ public StructuredQuery getCountQuery() {
175190
* Creates an appropriate JPA query from an {@link EntityManager} according to the current {@link AbstractJpaQuery}
176191
* type.
177192
*/
178-
protected Query createJpaQuery(QueryProvider queryString, Sort sort, @Nullable Pageable pageable,
193+
protected Query createJpaQuery(QueryProvider query, Sort sort, @Nullable Pageable pageable,
179194
ReturnedType returnedType) {
180195

181196
EntityManager em = getEntityManager();
182197

183198
if (this.query.hasConstructorExpression() || this.query.isDefaultProjection()) {
184-
return em.createQuery(potentiallyRewriteQuery(queryString.getQueryString(), sort, pageable));
199+
return em.createQuery(potentiallyRewriteQuery(query.getQueryString(), sort, pageable));
185200
}
186201

187202
Class<?> typeToRead = getTypeToRead(returnedType);
188203

189204
return typeToRead == null //
190-
? em.createQuery(potentiallyRewriteQuery(queryString.getQueryString(), sort, pageable)) //
191-
: em.createQuery(potentiallyRewriteQuery(queryString.getQueryString(), sort, pageable), typeToRead);
205+
? em.createQuery(potentiallyRewriteQuery(query.getQueryString(), sort, pageable)) //
206+
: em.createQuery(potentiallyRewriteQuery(query.getQueryString(), sort, pageable), typeToRead);
192207
}
193208

194209
/**
@@ -233,21 +248,21 @@ public QueryProvider getSorted(EntityQuery query, Sort sort, ReturnedType return
233248

234249
static class UnsortedCachingQuerySortRewriter implements QuerySortRewriter {
235250

236-
private volatile @Nullable QueryProvider cachedQueryString;
251+
private volatile @Nullable QueryProvider cachedQuery;
237252

238253
public QueryProvider getSorted(EntityQuery query, Sort sort, ReturnedType returnedType) {
239254

240255
if (sort.isSorted()) {
241256
throw new UnsupportedOperationException("NoOpQueryCache does not support sorting");
242257
}
243258

244-
QueryProvider cachedQueryString = this.cachedQueryString;
245-
if (cachedQueryString == null) {
246-
this.cachedQueryString = cachedQueryString = query
259+
QueryProvider cachedQuery = this.cachedQuery;
260+
if (cachedQuery == null) {
261+
this.cachedQuery = cachedQuery = query
247262
.rewrite(new DefaultQueryRewriteInformation(sort, returnedType));
248263
}
249264

250-
return cachedQueryString;
265+
return cachedQuery;
251266
}
252267
}
253268

@@ -259,19 +274,19 @@ class CachingQuerySortRewriter implements QuerySortRewriter {
259274
private final ConcurrentLruCache<CachableQuery, QueryProvider> queryCache = new ConcurrentLruCache<>(16,
260275
AbstractStringBasedJpaQuery.this::applySorting);
261276

262-
private volatile @Nullable QueryProvider cachedQueryString;
277+
private volatile @Nullable QueryProvider cachedQuery;
263278

264279
@Override
265280
public QueryProvider getSorted(EntityQuery query, Sort sort, ReturnedType returnedType) {
266281

267282
if (sort.isUnsorted()) {
268283

269-
QueryProvider cachedQueryString = this.cachedQueryString;
270-
if (cachedQueryString == null) {
271-
this.cachedQueryString = cachedQueryString = queryCache.get(new CachableQuery(query, sort, returnedType));
284+
QueryProvider cachedQuery = this.cachedQuery;
285+
if (cachedQuery == null) {
286+
this.cachedQuery = cachedQuery = queryCache.get(new CachableQuery(query, sort, returnedType));
272287
}
273288

274-
return cachedQueryString;
289+
return cachedQuery;
275290
}
276291

277292
return queryCache.get(new CachableQuery(query, sort, returnedType));
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
/*
2+
* Copyright 2025 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 org.springframework.util.ObjectUtils;
19+
20+
/**
21+
* Utility class encapsulating {@code DeclaredQuery} implementations.
22+
*
23+
* @author Christoph Strobl
24+
* @author Mark Paluch
25+
* @since 4.0
26+
*/
27+
class DeclaredQueries {
28+
29+
static final class JpqlQuery implements DeclaredQuery {
30+
31+
private final String jpql;
32+
33+
JpqlQuery(String jpql) {
34+
this.jpql = jpql;
35+
}
36+
37+
@Override
38+
public boolean isNative() {
39+
return false;
40+
}
41+
42+
@Override
43+
public String getQueryString() {
44+
return jpql;
45+
}
46+
47+
@Override
48+
public boolean equals(Object o) {
49+
if (!(o instanceof JpqlQuery jpqlQuery)) {
50+
return false;
51+
}
52+
return ObjectUtils.nullSafeEquals(jpql, jpqlQuery.jpql);
53+
}
54+
55+
@Override
56+
public int hashCode() {
57+
return ObjectUtils.nullSafeHashCode(jpql);
58+
}
59+
60+
@Override
61+
public String toString() {
62+
return "JPQL[" + jpql + "]";
63+
}
64+
65+
}
66+
67+
static final class NativeQuery implements DeclaredQuery {
68+
69+
private final String sql;
70+
71+
NativeQuery(String sql) {
72+
this.sql = sql;
73+
}
74+
75+
@Override
76+
public boolean isNative() {
77+
return true;
78+
}
79+
80+
@Override
81+
public String getQueryString() {
82+
return sql;
83+
}
84+
85+
@Override
86+
public boolean equals(Object o) {
87+
if (!(o instanceof NativeQuery that)) {
88+
return false;
89+
}
90+
return ObjectUtils.nullSafeEquals(sql, that.sql);
91+
}
92+
93+
@Override
94+
public int hashCode() {
95+
return ObjectUtils.nullSafeHashCode(sql);
96+
}
97+
98+
@Override
99+
public String toString() {
100+
return "Native[" + sql + "]";
101+
}
102+
103+
}
104+
105+
/**
106+
* A rewritten {@link DeclaredQuery} holding a reference to its original query.
107+
*/
108+
static class RewrittenQuery implements DeclaredQuery {
109+
110+
private final DeclaredQuery source;
111+
private final String queryString;
112+
113+
public RewrittenQuery(DeclaredQuery source, String queryString) {
114+
this.source = source;
115+
this.queryString = queryString;
116+
}
117+
118+
@Override
119+
public boolean isNative() {
120+
return source.isNative();
121+
}
122+
123+
@Override
124+
public String getQueryString() {
125+
return queryString;
126+
}
127+
128+
@Override
129+
public boolean equals(Object o) {
130+
if (!(o instanceof RewrittenQuery that)) {
131+
return false;
132+
}
133+
return ObjectUtils.nullSafeEquals(queryString, that.queryString);
134+
}
135+
136+
@Override
137+
public int hashCode() {
138+
return ObjectUtils.nullSafeHashCode(queryString);
139+
}
140+
141+
@Override
142+
public String toString() {
143+
return isNative() ? "Rewritten Native[" + queryString + "]" : "Rewritten JPQL[" + queryString + "]";
144+
}
145+
146+
}
147+
148+
}

0 commit comments

Comments
 (0)