Skip to content

Commit 09bf268

Browse files
mp911degregturn
authored andcommitted
Polishing.
Align ANTLR version with Hibernate's ANTLR version to avoid version mismatch reports to syserr. Rename types for consistent naming. Avoid duplicate creation of DeclaredQuery within the parsers. Lazify parsing and reuse cached results to avoid parser overhead. Add common configuration for parsers for consistent parser and lexer configurations. Simplify factory methods for HQL and JPQL parsers. Simplify condition flow for query enhancer creation. Refine fields for non-nullability requirements, avoid handing out null for a List.
1 parent 0d8c06d commit 09bf268

21 files changed

+650
-663
lines changed

pom.xml

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@
2828

2929
<properties>
3030
<source.level>16</source.level>
31-
<!-- AspectJ maven plugin can't handle 17 yet -->
31+
<!-- AspectJ maven plugin can't handle 17 yet -->
3232

33-
<antlr>4.11.1</antlr>
33+
<antlr>4.10.1</antlr> <!-- align with Hibernate's parser -->
3434
<eclipselink>3.0.3</eclipselink>
3535
<hibernate>6.1.4.Final</hibernate>
3636
<hsqldb>2.7.1</hsqldb>
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,23 @@
2020
import org.antlr.v4.runtime.Recognizer;
2121

2222
/**
23-
* A {@link BaseErrorListener} that will throw a {@link JpaQueryParsingSyntaxError} if the query is invalid.
23+
* A {@link BaseErrorListener} that will throw a {@link BadJpqlGrammarException} if the query is invalid.
2424
*
2525
* @author Greg Turnquist
2626
* @since 3.1
2727
*/
28-
class JpaQueryParsingSyntaxErrorListener extends BaseErrorListener {
28+
class BadJpqlGrammarErrorListener extends BaseErrorListener {
29+
30+
private final String query;
31+
32+
BadJpqlGrammarErrorListener(String query) {
33+
this.query = query;
34+
}
2935

3036
@Override
3137
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine,
3238
String msg, RecognitionException e) {
33-
throw new JpaQueryParsingSyntaxError("line " + line + ":" + charPositionInLine + " " + msg);
39+
throw new BadJpqlGrammarException("Line " + line + ":" + charPositionInLine + " " + msg, query, null);
3440
}
41+
3542
}
+12-3
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,26 @@
1616
package org.springframework.data.jpa.repository.query;
1717

1818
import org.springframework.dao.InvalidDataAccessResourceUsageException;
19+
import org.springframework.lang.Nullable;
1920

2021
/**
2122
* An exception thrown if the JPQL query is invalid.
2223
*
2324
* @author Greg Turnquist
25+
* @author Mark Paluch
2426
* @since 3.1
2527
*/
26-
class JpaQueryParsingSyntaxError extends InvalidDataAccessResourceUsageException {
28+
public class BadJpqlGrammarException extends InvalidDataAccessResourceUsageException {
2729

28-
public JpaQueryParsingSyntaxError(String message) {
29-
super(message);
30+
private final String jpql;
31+
32+
public BadJpqlGrammarException(String message, String jpql, @Nullable Throwable cause) {
33+
super(message + "; Bad JPQL grammar [" + jpql + "]", cause);
34+
this.jpql = jpql;
35+
}
36+
37+
public String getJpql() {
38+
return this.jpql;
3039
}
3140

3241
}

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

+12-15
Original file line numberDiff line numberDiff line change
@@ -24,46 +24,43 @@
2424
import org.springframework.lang.Nullable;
2525

2626
/**
27-
* Implements the parsing operations of a {@link JpaQueryParser} using the ANTLR-generated {@link HqlParser} and
28-
* {@link HqlQueryTransformer}.
29-
*
27+
* Implements the {@code HQL} parsing operations of a {@link JpaQueryParserSupport} using the ANTLR-generated
28+
* {@link HqlParser} and {@link HqlQueryTransformer}.
29+
*
3030
* @author Greg Turnquist
31+
* @author Mark Paluch
3132
* @since 3.1
3233
*/
33-
class HqlQueryParser extends JpaQueryParser {
34-
35-
HqlQueryParser(DeclaredQuery declaredQuery) {
36-
super(declaredQuery);
37-
}
34+
class HqlQueryParser extends JpaQueryParserSupport {
3835

3936
HqlQueryParser(String query) {
4037
super(query);
4138
}
4239

4340
/**
44-
* Convenience method to parse an HQL query. Will throw a {@link JpaQueryParsingSyntaxError} if the query is invalid.
41+
* Convenience method to parse an HQL query. Will throw a {@link BadJpqlGrammarException} if the query is invalid.
4542
*
4643
* @param query
4744
* @return a parsed query, ready for postprocessing
4845
*/
49-
static ParserRuleContext parse(String query) {
46+
public static ParserRuleContext parseQuery(String query) {
5047

5148
HqlLexer lexer = new HqlLexer(CharStreams.fromString(query));
5249
HqlParser parser = new HqlParser(new CommonTokenStream(lexer));
5350

54-
parser.addErrorListener(new JpaQueryParsingSyntaxErrorListener());
51+
configureParser(query, lexer, parser);
5552

5653
return parser.start();
5754
}
5855

5956
/**
60-
* Parse the query using {@link #parse(String)}.
57+
* Parse the query using {@link #parseQuery(String)}.
6158
*
6259
* @return a parsed query
6360
*/
6461
@Override
65-
protected ParserRuleContext parse() {
66-
return parse(getQuery());
62+
protected ParserRuleContext parse(String query) {
63+
return parseQuery(query);
6764
}
6865

6966
/**
@@ -74,7 +71,7 @@ protected ParserRuleContext parse() {
7471
* @return list of {@link JpaQueryParsingToken}s
7572
*/
7673
@Override
77-
protected List<JpaQueryParsingToken> doCreateQuery(ParserRuleContext parsedQuery, Sort sort) {
74+
protected List<JpaQueryParsingToken> applySort(ParserRuleContext parsedQuery, Sort sort) {
7875
return new HqlQueryTransformer(sort).visit(parsedQuery);
7976
}
8077

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

+30-18
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@
1818
import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken.*;
1919

2020
import java.util.ArrayList;
21+
import java.util.Collections;
2122
import java.util.List;
2223

2324
import org.antlr.v4.runtime.ParserRuleContext;
2425
import org.springframework.data.domain.Sort;
2526
import org.springframework.lang.Nullable;
27+
import org.springframework.util.Assert;
2628

2729
/**
2830
* An ANTLR {@link org.antlr.v4.runtime.tree.ParseTreeVisitor} that transforms a parsed HQL query.
@@ -32,30 +34,35 @@
3234
*/
3335
class HqlQueryTransformer extends HqlQueryRenderer {
3436

35-
@Nullable private Sort sort;
36-
private boolean countQuery;
37+
// TODO: Separate input from result parameters, encapsulation...
3738

38-
@Nullable private String countProjection;
39+
private final Sort sort;
40+
private final boolean countQuery;
3941

40-
@Nullable private String alias = null;
42+
private final @Nullable String countProjection;
4143

42-
private List<JpaQueryParsingToken> projection = null;
44+
private @Nullable String alias = null;
45+
46+
private List<JpaQueryParsingToken> projection = Collections.emptyList();
47+
private boolean projectionProcessed;
4348

4449
private boolean hasConstructorExpression = false;
4550

4651
HqlQueryTransformer() {
47-
this(null, false, null);
52+
this(Sort.unsorted(), false, null);
4853
}
4954

50-
HqlQueryTransformer(@Nullable Sort sort) {
55+
HqlQueryTransformer(Sort sort) {
5156
this(sort, false, null);
5257
}
5358

5459
HqlQueryTransformer(boolean countQuery, @Nullable String countProjection) {
55-
this(null, countQuery, countProjection);
60+
this(Sort.unsorted(), countQuery, countProjection);
5661
}
5762

58-
private HqlQueryTransformer(@Nullable Sort sort, boolean countQuery, @Nullable String countProjection) {
63+
private HqlQueryTransformer(Sort sort, boolean countQuery, @Nullable String countProjection) {
64+
65+
Assert.notNull(sort, "Sort must not be null");
5966

6067
this.sort = sort;
6168
this.countQuery = countQuery;
@@ -94,7 +101,7 @@ private static boolean isSubquery(ParserRuleContext ctx) {
94101
@Override
95102
public List<JpaQueryParsingToken> visitOrderedQuery(HqlParser.OrderedQueryContext ctx) {
96103

97-
List<JpaQueryParsingToken> tokens = new ArrayList<>();
104+
List<JpaQueryParsingToken> tokens = newArrayList();
98105

99106
if (ctx.query() != null) {
100107
tokens.addAll(visit(ctx.query()));
@@ -111,7 +118,7 @@ public List<JpaQueryParsingToken> visitOrderedQuery(HqlParser.OrderedQueryContex
111118
tokens.addAll(visit(ctx.queryOrder()));
112119
}
113120

114-
if (this.sort != null && this.sort.isSorted()) {
121+
if (this.sort.isSorted()) {
115122

116123
if (ctx.queryOrder() != null) {
117124

@@ -125,7 +132,7 @@ public List<JpaQueryParsingToken> visitOrderedQuery(HqlParser.OrderedQueryContex
125132

126133
this.sort.forEach(order -> {
127134

128-
JpaQueryParser.checkSortExpression(order);
135+
JpaQueryParserSupport.checkSortExpression(order);
129136

130137
if (order.isIgnoreCase()) {
131138
tokens.add(TOKEN_LOWER_FUNC);
@@ -160,7 +167,7 @@ public List<JpaQueryParsingToken> visitOrderedQuery(HqlParser.OrderedQueryContex
160167
@Override
161168
public List<JpaQueryParsingToken> visitFromQuery(HqlParser.FromQueryContext ctx) {
162169

163-
List<JpaQueryParsingToken> tokens = new ArrayList<>();
170+
List<JpaQueryParsingToken> tokens = newArrayList();
164171

165172
if (countQuery && !isSubquery(ctx) && ctx.selectClause() == null) {
166173

@@ -201,7 +208,7 @@ public List<JpaQueryParsingToken> visitFromQuery(HqlParser.FromQueryContext ctx)
201208
@Override
202209
public List<JpaQueryParsingToken> visitQueryOrder(HqlParser.QueryOrderContext ctx) {
203210

204-
List<JpaQueryParsingToken> tokens = new ArrayList<>();
211+
List<JpaQueryParsingToken> tokens = newArrayList();
205212

206213
if (!countQuery) {
207214
tokens.addAll(visit(ctx.orderByClause()));
@@ -224,7 +231,7 @@ public List<JpaQueryParsingToken> visitQueryOrder(HqlParser.QueryOrderContext ct
224231
@Override
225232
public List<JpaQueryParsingToken> visitFromRoot(HqlParser.FromRootContext ctx) {
226233

227-
List<JpaQueryParsingToken> tokens = new ArrayList<>();
234+
List<JpaQueryParsingToken> tokens = newArrayList();
228235

229236
if (ctx.entityName() != null) {
230237

@@ -261,7 +268,7 @@ public List<JpaQueryParsingToken> visitFromRoot(HqlParser.FromRootContext ctx) {
261268
@Override
262269
public List<JpaQueryParsingToken> visitAlias(HqlParser.AliasContext ctx) {
263270

264-
List<JpaQueryParsingToken> tokens = new ArrayList<>();
271+
List<JpaQueryParsingToken> tokens = newArrayList();
265272

266273
if (ctx.AS() != null) {
267274
tokens.add(new JpaQueryParsingToken(ctx.AS()));
@@ -279,7 +286,7 @@ public List<JpaQueryParsingToken> visitAlias(HqlParser.AliasContext ctx) {
279286
@Override
280287
public List<JpaQueryParsingToken> visitSelectClause(HqlParser.SelectClauseContext ctx) {
281288

282-
List<JpaQueryParsingToken> tokens = new ArrayList<>();
289+
List<JpaQueryParsingToken> tokens = newArrayList();
283290

284291
tokens.add(new JpaQueryParsingToken(ctx.SELECT()));
285292

@@ -321,8 +328,9 @@ public List<JpaQueryParsingToken> visitSelectClause(HqlParser.SelectClauseContex
321328
tokens.addAll(selectionListTokens);
322329
}
323330

324-
if (projection == null && !isSubquery(ctx)) {
331+
if (!projectionProcessed && !isSubquery(ctx)) {
325332
this.projection = selectionListTokens;
333+
this.projectionProcessed = true;
326334
}
327335

328336
return tokens;
@@ -335,4 +343,8 @@ public List<JpaQueryParsingToken> visitInstantiation(HqlParser.InstantiationCont
335343

336344
return super.visitInstantiation(ctx);
337345
}
346+
347+
static <T> ArrayList<T> newArrayList() {
348+
return new ArrayList<>();
349+
}
338350
}

0 commit comments

Comments
 (0)