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.
This commit is contained in:
committed by
Greg L. Turnquist
parent
0d8c06d661
commit
09bf268ffb
4
pom.xml
4
pom.xml
@@ -28,9 +28,9 @@
|
||||
|
||||
<properties>
|
||||
<source.level>16</source.level>
|
||||
<!-- AspectJ maven plugin can't handle 17 yet -->
|
||||
<!-- AspectJ maven plugin can't handle 17 yet -->
|
||||
|
||||
<antlr>4.11.1</antlr>
|
||||
<antlr>4.10.1</antlr> <!-- align with Hibernate's parser -->
|
||||
<eclipselink>3.0.3</eclipselink>
|
||||
<hibernate>6.1.4.Final</hibernate>
|
||||
<hsqldb>2.7.1</hsqldb>
|
||||
|
||||
@@ -20,16 +20,23 @@ import org.antlr.v4.runtime.RecognitionException;
|
||||
import org.antlr.v4.runtime.Recognizer;
|
||||
|
||||
/**
|
||||
* A {@link BaseErrorListener} that will throw a {@link JpaQueryParsingSyntaxError} if the query is invalid.
|
||||
* A {@link BaseErrorListener} that will throw a {@link BadJpqlGrammarException} if the query is invalid.
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
* @since 3.1
|
||||
*/
|
||||
class JpaQueryParsingSyntaxErrorListener extends BaseErrorListener {
|
||||
class BadJpqlGrammarErrorListener extends BaseErrorListener {
|
||||
|
||||
private final String query;
|
||||
|
||||
BadJpqlGrammarErrorListener(String query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine,
|
||||
String msg, RecognitionException e) {
|
||||
throw new JpaQueryParsingSyntaxError("line " + line + ":" + charPositionInLine + " " + msg);
|
||||
throw new BadJpqlGrammarException("Line " + line + ":" + charPositionInLine + " " + msg, query, null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,17 +16,26 @@
|
||||
package org.springframework.data.jpa.repository.query;
|
||||
|
||||
import org.springframework.dao.InvalidDataAccessResourceUsageException;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* An exception thrown if the JPQL query is invalid.
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
* @author Mark Paluch
|
||||
* @since 3.1
|
||||
*/
|
||||
class JpaQueryParsingSyntaxError extends InvalidDataAccessResourceUsageException {
|
||||
public class BadJpqlGrammarException extends InvalidDataAccessResourceUsageException {
|
||||
|
||||
public JpaQueryParsingSyntaxError(String message) {
|
||||
super(message);
|
||||
private final String jpql;
|
||||
|
||||
public BadJpqlGrammarException(String message, String jpql, @Nullable Throwable cause) {
|
||||
super(message + "; Bad JPQL grammar [" + jpql + "]", cause);
|
||||
this.jpql = jpql;
|
||||
}
|
||||
|
||||
public String getJpql() {
|
||||
return this.jpql;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,46 +24,43 @@ import org.springframework.data.domain.Sort;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Implements the parsing operations of a {@link JpaQueryParser} using the ANTLR-generated {@link HqlParser} and
|
||||
* {@link HqlQueryTransformer}.
|
||||
*
|
||||
* Implements the {@code HQL} parsing operations of a {@link JpaQueryParserSupport} using the ANTLR-generated
|
||||
* {@link HqlParser} and {@link HqlQueryTransformer}.
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
* @author Mark Paluch
|
||||
* @since 3.1
|
||||
*/
|
||||
class HqlQueryParser extends JpaQueryParser {
|
||||
|
||||
HqlQueryParser(DeclaredQuery declaredQuery) {
|
||||
super(declaredQuery);
|
||||
}
|
||||
class HqlQueryParser extends JpaQueryParserSupport {
|
||||
|
||||
HqlQueryParser(String query) {
|
||||
super(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to parse an HQL query. Will throw a {@link JpaQueryParsingSyntaxError} if the query is invalid.
|
||||
* Convenience method to parse an HQL query. Will throw a {@link BadJpqlGrammarException} if the query is invalid.
|
||||
*
|
||||
* @param query
|
||||
* @return a parsed query, ready for postprocessing
|
||||
*/
|
||||
static ParserRuleContext parse(String query) {
|
||||
public static ParserRuleContext parseQuery(String query) {
|
||||
|
||||
HqlLexer lexer = new HqlLexer(CharStreams.fromString(query));
|
||||
HqlParser parser = new HqlParser(new CommonTokenStream(lexer));
|
||||
|
||||
parser.addErrorListener(new JpaQueryParsingSyntaxErrorListener());
|
||||
configureParser(query, lexer, parser);
|
||||
|
||||
return parser.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the query using {@link #parse(String)}.
|
||||
* Parse the query using {@link #parseQuery(String)}.
|
||||
*
|
||||
* @return a parsed query
|
||||
*/
|
||||
@Override
|
||||
protected ParserRuleContext parse() {
|
||||
return parse(getQuery());
|
||||
protected ParserRuleContext parse(String query) {
|
||||
return parseQuery(query);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,7 +71,7 @@ class HqlQueryParser extends JpaQueryParser {
|
||||
* @return list of {@link JpaQueryParsingToken}s
|
||||
*/
|
||||
@Override
|
||||
protected List<JpaQueryParsingToken> doCreateQuery(ParserRuleContext parsedQuery, Sort sort) {
|
||||
protected List<JpaQueryParsingToken> applySort(ParserRuleContext parsedQuery, Sort sort) {
|
||||
return new HqlQueryTransformer(sort).visit(parsedQuery);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,11 +18,13 @@ package org.springframework.data.jpa.repository.query;
|
||||
import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.antlr.v4.runtime.ParserRuleContext;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An ANTLR {@link org.antlr.v4.runtime.tree.ParseTreeVisitor} that transforms a parsed HQL query.
|
||||
@@ -32,30 +34,35 @@ import org.springframework.lang.Nullable;
|
||||
*/
|
||||
class HqlQueryTransformer extends HqlQueryRenderer {
|
||||
|
||||
@Nullable private Sort sort;
|
||||
private boolean countQuery;
|
||||
// TODO: Separate input from result parameters, encapsulation...
|
||||
|
||||
@Nullable private String countProjection;
|
||||
private final Sort sort;
|
||||
private final boolean countQuery;
|
||||
|
||||
@Nullable private String alias = null;
|
||||
private final @Nullable String countProjection;
|
||||
|
||||
private List<JpaQueryParsingToken> projection = null;
|
||||
private @Nullable String alias = null;
|
||||
|
||||
private List<JpaQueryParsingToken> projection = Collections.emptyList();
|
||||
private boolean projectionProcessed;
|
||||
|
||||
private boolean hasConstructorExpression = false;
|
||||
|
||||
HqlQueryTransformer() {
|
||||
this(null, false, null);
|
||||
this(Sort.unsorted(), false, null);
|
||||
}
|
||||
|
||||
HqlQueryTransformer(@Nullable Sort sort) {
|
||||
HqlQueryTransformer(Sort sort) {
|
||||
this(sort, false, null);
|
||||
}
|
||||
|
||||
HqlQueryTransformer(boolean countQuery, @Nullable String countProjection) {
|
||||
this(null, countQuery, countProjection);
|
||||
this(Sort.unsorted(), countQuery, countProjection);
|
||||
}
|
||||
|
||||
private HqlQueryTransformer(@Nullable Sort sort, boolean countQuery, @Nullable String countProjection) {
|
||||
private HqlQueryTransformer(Sort sort, boolean countQuery, @Nullable String countProjection) {
|
||||
|
||||
Assert.notNull(sort, "Sort must not be null");
|
||||
|
||||
this.sort = sort;
|
||||
this.countQuery = countQuery;
|
||||
@@ -94,7 +101,7 @@ class HqlQueryTransformer extends HqlQueryRenderer {
|
||||
@Override
|
||||
public List<JpaQueryParsingToken> visitOrderedQuery(HqlParser.OrderedQueryContext ctx) {
|
||||
|
||||
List<JpaQueryParsingToken> tokens = new ArrayList<>();
|
||||
List<JpaQueryParsingToken> tokens = newArrayList();
|
||||
|
||||
if (ctx.query() != null) {
|
||||
tokens.addAll(visit(ctx.query()));
|
||||
@@ -111,7 +118,7 @@ class HqlQueryTransformer extends HqlQueryRenderer {
|
||||
tokens.addAll(visit(ctx.queryOrder()));
|
||||
}
|
||||
|
||||
if (this.sort != null && this.sort.isSorted()) {
|
||||
if (this.sort.isSorted()) {
|
||||
|
||||
if (ctx.queryOrder() != null) {
|
||||
|
||||
@@ -125,7 +132,7 @@ class HqlQueryTransformer extends HqlQueryRenderer {
|
||||
|
||||
this.sort.forEach(order -> {
|
||||
|
||||
JpaQueryParser.checkSortExpression(order);
|
||||
JpaQueryParserSupport.checkSortExpression(order);
|
||||
|
||||
if (order.isIgnoreCase()) {
|
||||
tokens.add(TOKEN_LOWER_FUNC);
|
||||
@@ -160,7 +167,7 @@ class HqlQueryTransformer extends HqlQueryRenderer {
|
||||
@Override
|
||||
public List<JpaQueryParsingToken> visitFromQuery(HqlParser.FromQueryContext ctx) {
|
||||
|
||||
List<JpaQueryParsingToken> tokens = new ArrayList<>();
|
||||
List<JpaQueryParsingToken> tokens = newArrayList();
|
||||
|
||||
if (countQuery && !isSubquery(ctx) && ctx.selectClause() == null) {
|
||||
|
||||
@@ -201,7 +208,7 @@ class HqlQueryTransformer extends HqlQueryRenderer {
|
||||
@Override
|
||||
public List<JpaQueryParsingToken> visitQueryOrder(HqlParser.QueryOrderContext ctx) {
|
||||
|
||||
List<JpaQueryParsingToken> tokens = new ArrayList<>();
|
||||
List<JpaQueryParsingToken> tokens = newArrayList();
|
||||
|
||||
if (!countQuery) {
|
||||
tokens.addAll(visit(ctx.orderByClause()));
|
||||
@@ -224,7 +231,7 @@ class HqlQueryTransformer extends HqlQueryRenderer {
|
||||
@Override
|
||||
public List<JpaQueryParsingToken> visitFromRoot(HqlParser.FromRootContext ctx) {
|
||||
|
||||
List<JpaQueryParsingToken> tokens = new ArrayList<>();
|
||||
List<JpaQueryParsingToken> tokens = newArrayList();
|
||||
|
||||
if (ctx.entityName() != null) {
|
||||
|
||||
@@ -261,7 +268,7 @@ class HqlQueryTransformer extends HqlQueryRenderer {
|
||||
@Override
|
||||
public List<JpaQueryParsingToken> visitAlias(HqlParser.AliasContext ctx) {
|
||||
|
||||
List<JpaQueryParsingToken> tokens = new ArrayList<>();
|
||||
List<JpaQueryParsingToken> tokens = newArrayList();
|
||||
|
||||
if (ctx.AS() != null) {
|
||||
tokens.add(new JpaQueryParsingToken(ctx.AS()));
|
||||
@@ -279,7 +286,7 @@ class HqlQueryTransformer extends HqlQueryRenderer {
|
||||
@Override
|
||||
public List<JpaQueryParsingToken> visitSelectClause(HqlParser.SelectClauseContext ctx) {
|
||||
|
||||
List<JpaQueryParsingToken> tokens = new ArrayList<>();
|
||||
List<JpaQueryParsingToken> tokens = newArrayList();
|
||||
|
||||
tokens.add(new JpaQueryParsingToken(ctx.SELECT()));
|
||||
|
||||
@@ -321,8 +328,9 @@ class HqlQueryTransformer extends HqlQueryRenderer {
|
||||
tokens.addAll(selectionListTokens);
|
||||
}
|
||||
|
||||
if (projection == null && !isSubquery(ctx)) {
|
||||
if (!projectionProcessed && !isSubquery(ctx)) {
|
||||
this.projection = selectionListTokens;
|
||||
this.projectionProcessed = true;
|
||||
}
|
||||
|
||||
return tokens;
|
||||
@@ -335,4 +343,8 @@ class HqlQueryTransformer extends HqlQueryRenderer {
|
||||
|
||||
return super.visitInstantiation(ctx);
|
||||
}
|
||||
|
||||
static <T> ArrayList<T> newArrayList() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,37 +17,63 @@ package org.springframework.data.jpa.repository.query;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Implementation of {@link QueryEnhancer} using a {@link JpaQueryParser}.<br/>
|
||||
* <br/>
|
||||
* NOTE: The parser can find everything it needs to created sorted and count queries. Thus, looking up the alias or the
|
||||
* projection isn't needed for its primary function, and are simply implemented for test purposes.
|
||||
* Implementation of {@link QueryEnhancer} to enhance JPA queries using a {@link JpaQueryParserSupport}.
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
* @author Mark Paluch
|
||||
* @since 3.1
|
||||
* @see JpqlQueryParser
|
||||
* @see HqlQueryParser
|
||||
*/
|
||||
class JpaQueryParsingEnhancer implements QueryEnhancer {
|
||||
class JpaQueryEnhancer implements QueryEnhancer {
|
||||
|
||||
private final JpaQueryParser queryParser;
|
||||
private final DeclaredQuery query;
|
||||
private final JpaQueryParserSupport queryParser;
|
||||
|
||||
/**
|
||||
* Initialize with an {@link JpaQueryParser}.
|
||||
*
|
||||
* Initialize with an {@link JpaQueryParserSupport}.
|
||||
*
|
||||
* @param query
|
||||
* @param queryParser
|
||||
*/
|
||||
public JpaQueryParsingEnhancer(JpaQueryParser queryParser) {
|
||||
private JpaQueryEnhancer(DeclaredQuery query, JpaQueryParserSupport queryParser) {
|
||||
|
||||
Assert.notNull(queryParser, "queryParse must not be null!");
|
||||
this.query = query;
|
||||
this.queryParser = queryParser;
|
||||
}
|
||||
|
||||
public JpaQueryParser getQueryParsingStrategy() {
|
||||
/**
|
||||
* Factory method to create a {@link JpaQueryParserSupport} for {@link DeclaredQuery} using JPQL grammar.
|
||||
*
|
||||
* @param query must not be {@literal null}.
|
||||
* @return a new {@link JpaQueryEnhancer} using JPQL.
|
||||
*/
|
||||
public static JpaQueryEnhancer forJpql(DeclaredQuery query) {
|
||||
|
||||
Assert.notNull(query, "DeclaredQuery must not be null!");
|
||||
|
||||
return new JpaQueryEnhancer(query, new JpqlQueryParser(query.getQueryString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create a {@link JpaQueryParserSupport} for {@link DeclaredQuery} using HQL grammar.
|
||||
*
|
||||
* @param query must not be {@literal null}.
|
||||
* @return a new {@link JpaQueryEnhancer} using HQL.
|
||||
*/
|
||||
public static JpaQueryEnhancer forHql(DeclaredQuery query) {
|
||||
|
||||
Assert.notNull(query, "DeclaredQuery must not be null!");
|
||||
|
||||
return new JpaQueryEnhancer(query, new HqlQueryParser(query.getQueryString()));
|
||||
}
|
||||
|
||||
protected JpaQueryParserSupport getQueryParsingStrategy() {
|
||||
return queryParser;
|
||||
}
|
||||
|
||||
@@ -59,7 +85,7 @@ class JpaQueryParsingEnhancer implements QueryEnhancer {
|
||||
*/
|
||||
@Override
|
||||
public String applySorting(Sort sort) {
|
||||
return queryParser.createQuery(sort);
|
||||
return queryParser.renderSortedQuery(sort);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,8 +101,8 @@ class JpaQueryParsingEnhancer implements QueryEnhancer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the alias for the entity in the FROM clause from the JPA query. Since the {@link JpaQueryParser} can
|
||||
* already find the alias when generating sorted and count queries, this is mainly to serve test cases.
|
||||
* Resolves the alias for the entity in the FROM clause from the JPA query. Since the {@link JpaQueryParserSupport}
|
||||
* can already find the alias when generating sorted and count queries, this is mainly to serve test cases.
|
||||
*/
|
||||
@Override
|
||||
public String detectAlias() {
|
||||
@@ -85,7 +111,7 @@ class JpaQueryParsingEnhancer implements QueryEnhancer {
|
||||
|
||||
/**
|
||||
* Creates a count query from the original query, with no count projection.
|
||||
*
|
||||
*
|
||||
* @return Guaranteed to be not {@literal null};
|
||||
*/
|
||||
@Override
|
||||
@@ -114,8 +140,8 @@ class JpaQueryParsingEnhancer implements QueryEnhancer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up the projection of the JPA query. Since the {@link JpaQueryParser} can already find the projection when
|
||||
* generating sorted and count queries, this is mainly to serve test cases.
|
||||
* Looks up the projection of the JPA query. Since the {@link JpaQueryParserSupport} can already find the projection
|
||||
* when generating sorted and count queries, this is mainly to serve test cases.
|
||||
*/
|
||||
@Override
|
||||
public String getProjection() {
|
||||
@@ -123,7 +149,7 @@ class JpaQueryParsingEnhancer implements QueryEnhancer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Since the {@link JpaQueryParser} can already fully transform sorted and count queries by itself, this is a
|
||||
* Since the {@link JpaQueryParserSupport} can already fully transform sorted and count queries by itself, this is a
|
||||
* placeholder method.
|
||||
*
|
||||
* @return empty set
|
||||
@@ -134,10 +160,10 @@ class JpaQueryParsingEnhancer implements QueryEnhancer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up the {@link DeclaredQuery} from the {@link JpaQueryParser}.
|
||||
* Look up the {@link DeclaredQuery} from the {@link JpaQueryParserSupport}.
|
||||
*/
|
||||
@Override
|
||||
public DeclaredQuery getQuery() {
|
||||
return queryParser.getDeclaredQuery();
|
||||
return query;
|
||||
}
|
||||
}
|
||||
@@ -20,19 +20,24 @@ import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.antlr.v4.runtime.Lexer;
|
||||
import org.antlr.v4.runtime.Parser;
|
||||
import org.antlr.v4.runtime.ParserRuleContext;
|
||||
import org.antlr.v4.runtime.atn.PredictionMode;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.JpaSort;
|
||||
import org.springframework.data.util.Lazy;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Operations needed to parse a JPA query.
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
* @author Mark Paluch
|
||||
* @since 3.1
|
||||
*/
|
||||
abstract class JpaQueryParser {
|
||||
abstract class JpaQueryParserSupport {
|
||||
|
||||
private static final Pattern PUNCTUATION_PATTERN = Pattern.compile(".*((?![._])[\\p{Punct}|\\s])");
|
||||
|
||||
@@ -40,22 +45,10 @@ abstract class JpaQueryParser {
|
||||
+ "aliases used in the select clause; If you really want to use something other than that for sorting, please use "
|
||||
+ "JpaSort.unsafe(…)";
|
||||
|
||||
private final DeclaredQuery declaredQuery;
|
||||
private final ParseState state;
|
||||
|
||||
JpaQueryParser(DeclaredQuery declaredQuery) {
|
||||
this.declaredQuery = declaredQuery;
|
||||
}
|
||||
|
||||
JpaQueryParser(String query) {
|
||||
this(DeclaredQuery.of(query, false));
|
||||
}
|
||||
|
||||
DeclaredQuery getDeclaredQuery() {
|
||||
return declaredQuery;
|
||||
}
|
||||
|
||||
String getQuery() {
|
||||
return getDeclaredQuery().getQueryString();
|
||||
JpaQueryParserSupport(String query) {
|
||||
this.state = new ParseState(query);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,17 +57,11 @@ abstract class JpaQueryParser {
|
||||
*
|
||||
* @param sort can be {@literal null}
|
||||
*/
|
||||
String createQuery(Sort sort) {
|
||||
String renderSortedQuery(Sort sort) {
|
||||
|
||||
try {
|
||||
ParserRuleContext parsedQuery = parse();
|
||||
|
||||
if (parsedQuery == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return render(doCreateQuery(parsedQuery, sort));
|
||||
} catch (JpaQueryParsingSyntaxError e) {
|
||||
return render(applySort(state.getContext(), sort));
|
||||
} catch (BadJpqlGrammarException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
@@ -87,34 +74,21 @@ abstract class JpaQueryParser {
|
||||
String createCountQuery(@Nullable String countProjection) {
|
||||
|
||||
try {
|
||||
ParserRuleContext parsedQuery = parse();
|
||||
|
||||
if (parsedQuery == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return render(doCreateCountQuery(parsedQuery, countProjection));
|
||||
} catch (JpaQueryParsingSyntaxError e) {
|
||||
return render(doCreateCountQuery(state.getContext(), countProjection));
|
||||
} catch (BadJpqlGrammarException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the projection of the query.
|
||||
*
|
||||
* @param parsedQuery
|
||||
*/
|
||||
String projection() {
|
||||
|
||||
try {
|
||||
ParserRuleContext parsedQuery = parse();
|
||||
|
||||
if (parsedQuery == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return render(doFindProjection(parsedQuery));
|
||||
} catch (JpaQueryParsingSyntaxError e) {
|
||||
List<JpaQueryParsingToken> tokens = doFindProjection(state.getContext());
|
||||
return tokens.isEmpty() ? "" : render(tokens);
|
||||
} catch (BadJpqlGrammarException e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -124,17 +98,12 @@ abstract class JpaQueryParser {
|
||||
*
|
||||
* @return can be {@literal null}
|
||||
*/
|
||||
@Nullable
|
||||
String findAlias() {
|
||||
|
||||
try {
|
||||
ParserRuleContext parsedQuery = parse();
|
||||
|
||||
if (parsedQuery == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return doFindAlias(parsedQuery);
|
||||
} catch (JpaQueryParsingSyntaxError e) {
|
||||
return doFindAlias(state.getContext());
|
||||
} catch (BadJpqlGrammarException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -147,18 +116,67 @@ abstract class JpaQueryParser {
|
||||
boolean hasConstructorExpression() {
|
||||
|
||||
try {
|
||||
ParserRuleContext parsedQuery = parse();
|
||||
|
||||
if (parsedQuery == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return doCheckForConstructor(parsedQuery);
|
||||
} catch (JpaQueryParsingSyntaxError e) {
|
||||
return doCheckForConstructor(state.getContext());
|
||||
} catch (BadJpqlGrammarException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the JPA query using its corresponding ANTLR parser.
|
||||
*/
|
||||
protected abstract ParserRuleContext parse(String query);
|
||||
|
||||
/**
|
||||
* Apply common configuration (SLL prediction for performance, our own error listeners).
|
||||
*
|
||||
* @param query
|
||||
* @param lexer
|
||||
* @param parser
|
||||
*/
|
||||
static void configureParser(String query, Lexer lexer, Parser parser) {
|
||||
|
||||
BadJpqlGrammarErrorListener errorListener = new BadJpqlGrammarErrorListener(query);
|
||||
|
||||
lexer.removeErrorListeners();
|
||||
lexer.addErrorListener(errorListener);
|
||||
|
||||
parser.getInterpreter().setPredictionMode(PredictionMode.SLL);
|
||||
|
||||
parser.removeErrorListeners();
|
||||
parser.addErrorListener(errorListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link JpaQueryParsingToken}-based query with an {@literal order by} applied/amended based upon the
|
||||
* {@link Sort} parameter.
|
||||
*
|
||||
* @param parsedQuery
|
||||
* @param sort can be {@literal null}
|
||||
*/
|
||||
protected abstract List<JpaQueryParsingToken> applySort(ParserRuleContext parsedQuery, Sort sort);
|
||||
|
||||
/**
|
||||
* Create a {@link JpaQueryParsingToken}-based count query.
|
||||
*
|
||||
* @param parsedQuery
|
||||
* @param countProjection
|
||||
*/
|
||||
protected abstract List<JpaQueryParsingToken> doCreateCountQuery(ParserRuleContext parsedQuery,
|
||||
@Nullable String countProjection);
|
||||
|
||||
@Nullable
|
||||
protected abstract String doFindAlias(ParserRuleContext parsedQuery);
|
||||
|
||||
/**
|
||||
* Find the projection of the query's primary SELECT clause.
|
||||
*
|
||||
* @param parsedQuery
|
||||
*/
|
||||
protected abstract List<JpaQueryParsingToken> doFindProjection(ParserRuleContext parsedQuery);
|
||||
|
||||
protected abstract boolean doCheckForConstructor(ParserRuleContext parsedQuery);
|
||||
|
||||
/**
|
||||
* Check any given {@link JpaSort.JpaOrder#isUnsafe()} order for presence of at least one property offending the
|
||||
* {@link #PUNCTUATION_PATTERN} and throw an {@link Exception} indicating potential unsafe order by expression.
|
||||
@@ -177,37 +195,38 @@ abstract class JpaQueryParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the JPA query using its corresponding ANTLR parser.
|
||||
* Parser state capturing the lazily-parsed parser context.
|
||||
*/
|
||||
protected abstract ParserRuleContext parse();
|
||||
class ParseState {
|
||||
|
||||
/**
|
||||
* Create a {@link JpaQueryParsingToken}-based query with an {@literal order by} applied/amended based upon the
|
||||
* {@link Sort} parameter.
|
||||
*
|
||||
* @param parsedQuery
|
||||
* @param sort can be {@literal null}
|
||||
*/
|
||||
protected abstract List<JpaQueryParsingToken> doCreateQuery(ParserRuleContext parsedQuery, Sort sort);
|
||||
private final Lazy<ParserRuleContext> parsedQuery;
|
||||
private volatile @Nullable BadJpqlGrammarException error;
|
||||
private final String query;
|
||||
|
||||
/**
|
||||
* Create a {@link JpaQueryParsingToken}-based count query.
|
||||
*
|
||||
* @param parsedQuery
|
||||
* @param countProjection
|
||||
*/
|
||||
protected abstract List<JpaQueryParsingToken> doCreateCountQuery(ParserRuleContext parsedQuery,
|
||||
@Nullable String countProjection);
|
||||
public ParseState(String query) {
|
||||
this.query = query;
|
||||
this.parsedQuery = Lazy.of(() -> parse(query));
|
||||
}
|
||||
|
||||
protected abstract String doFindAlias(ParserRuleContext parsedQuery);
|
||||
public ParserRuleContext getContext() {
|
||||
|
||||
/**
|
||||
* Find the projection of the query's primary SELECT clause.
|
||||
*
|
||||
* @param parsedQuery
|
||||
*/
|
||||
protected abstract List<JpaQueryParsingToken> doFindProjection(ParserRuleContext parsedQuery);
|
||||
BadJpqlGrammarException error = this.error;
|
||||
|
||||
protected abstract boolean doCheckForConstructor(ParserRuleContext parsedQuery);
|
||||
if (error != null) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
try {
|
||||
return parsedQuery.get();
|
||||
} catch (BadJpqlGrammarException e) {
|
||||
this.error = error = e;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public String getQuery() {
|
||||
return query;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -162,10 +162,6 @@ class JpaQueryParsingToken {
|
||||
*/
|
||||
static String render(List<JpaQueryParsingToken> tokens) {
|
||||
|
||||
if (tokens == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
StringBuilder results = new StringBuilder();
|
||||
|
||||
tokens.forEach(token -> {
|
||||
|
||||
@@ -24,46 +24,44 @@ import org.springframework.data.domain.Sort;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Implements the parsing operations of a {@link JpaQueryParser} using the ANTLR-generated {@link JpqlParser} and
|
||||
* {@link JpqlQueryTransformer}.
|
||||
* Implements the {@code JPQL} parsing operations of a {@link JpaQueryParserSupport} using the ANTLR-generated
|
||||
* {@link JpqlParser} and {@link JpqlQueryTransformer}.
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
* @author Mark Paluch
|
||||
* @since 3.1
|
||||
*/
|
||||
class JpqlQueryParser extends JpaQueryParser {
|
||||
|
||||
JpqlQueryParser(DeclaredQuery declaredQuery) {
|
||||
super(declaredQuery);
|
||||
}
|
||||
class JpqlQueryParser extends JpaQueryParserSupport {
|
||||
|
||||
JpqlQueryParser(String query) {
|
||||
super(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to parse a JPQL query. Will throw a {@link JpaQueryParsingSyntaxError} if the query is invalid.
|
||||
* Convenience method to parse a JPQL query. Will throw a {@link BadJpqlGrammarException} if the query is invalid.
|
||||
*
|
||||
* @param query
|
||||
* @return a parsed query, ready for postprocessing
|
||||
*/
|
||||
static ParserRuleContext parse(String query) {
|
||||
public static ParserRuleContext parseQuery(String query) {
|
||||
|
||||
JpqlLexer lexer = new JpqlLexer(CharStreams.fromString(query));
|
||||
JpqlParser parser = new JpqlParser(new CommonTokenStream(lexer));
|
||||
|
||||
parser.addErrorListener(new JpaQueryParsingSyntaxErrorListener());
|
||||
configureParser(query, lexer, parser);
|
||||
|
||||
return parser.start();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse the query using {@link #parse(String)}.
|
||||
* Parse the query using {@link #parseQuery(String)}.
|
||||
*
|
||||
* @return a parsed query
|
||||
*/
|
||||
@Override
|
||||
protected ParserRuleContext parse() {
|
||||
return parse(getQuery());
|
||||
protected ParserRuleContext parse(String query) {
|
||||
return parseQuery(query);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,7 +72,7 @@ class JpqlQueryParser extends JpaQueryParser {
|
||||
* @return list of {@link JpaQueryParsingToken}s
|
||||
*/
|
||||
@Override
|
||||
protected List<JpaQueryParsingToken> doCreateQuery(ParserRuleContext parsedQuery, Sort sort) {
|
||||
protected List<JpaQueryParsingToken> applySort(ParserRuleContext parsedQuery, Sort sort) {
|
||||
return new JpqlQueryTransformer(sort).visit(parsedQuery);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,10 +18,12 @@ package org.springframework.data.jpa.repository.query;
|
||||
import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An ANTLR {@link org.antlr.v4.runtime.tree.ParseTreeVisitor} that transforms a parsed JPQL query.
|
||||
@@ -31,30 +33,34 @@ import org.springframework.lang.Nullable;
|
||||
*/
|
||||
class JpqlQueryTransformer extends JpqlQueryRenderer {
|
||||
|
||||
@Nullable private Sort sort;
|
||||
private boolean countQuery;
|
||||
// TODO: Separate input from result parameters, encapsulation...
|
||||
private final Sort sort;
|
||||
private final boolean countQuery;
|
||||
|
||||
@Nullable private String countProjection;
|
||||
private final @Nullable String countProjection;
|
||||
|
||||
@Nullable private String alias = null;
|
||||
private @Nullable String alias = null;
|
||||
|
||||
private List<JpaQueryParsingToken> projection = null;
|
||||
private List<JpaQueryParsingToken> projection = Collections.emptyList();
|
||||
private boolean projectionProcessed;
|
||||
|
||||
private boolean hasConstructorExpression = false;
|
||||
|
||||
JpqlQueryTransformer() {
|
||||
this(null, false, null);
|
||||
this(Sort.unsorted(), false, null);
|
||||
}
|
||||
|
||||
JpqlQueryTransformer(@Nullable Sort sort) {
|
||||
JpqlQueryTransformer(Sort sort) {
|
||||
this(sort, false, null);
|
||||
}
|
||||
|
||||
JpqlQueryTransformer(boolean countQuery, @Nullable String countProjection) {
|
||||
this(null, countQuery, countProjection);
|
||||
this(Sort.unsorted(), countQuery, countProjection);
|
||||
}
|
||||
|
||||
private JpqlQueryTransformer(@Nullable Sort sort, boolean countQuery, @Nullable String countProjection) {
|
||||
private JpqlQueryTransformer(Sort sort, boolean countQuery, @Nullable String countProjection) {
|
||||
|
||||
Assert.notNull(sort, "Sort must not be null");
|
||||
|
||||
this.sort = sort;
|
||||
this.countQuery = countQuery;
|
||||
@@ -77,7 +83,7 @@ class JpqlQueryTransformer extends JpqlQueryRenderer {
|
||||
@Override
|
||||
public List<JpaQueryParsingToken> visitSelect_statement(JpqlParser.Select_statementContext ctx) {
|
||||
|
||||
List<JpaQueryParsingToken> tokens = new ArrayList<>();
|
||||
List<JpaQueryParsingToken> tokens = newArrayList();
|
||||
|
||||
tokens.addAll(visit(ctx.select_clause()));
|
||||
tokens.addAll(visit(ctx.from_clause()));
|
||||
@@ -100,7 +106,7 @@ class JpqlQueryTransformer extends JpqlQueryRenderer {
|
||||
tokens.addAll(visit(ctx.orderby_clause()));
|
||||
}
|
||||
|
||||
if (this.sort != null && this.sort.isSorted()) {
|
||||
if (this.sort.isSorted()) {
|
||||
|
||||
if (ctx.orderby_clause() != null) {
|
||||
|
||||
@@ -114,7 +120,7 @@ class JpqlQueryTransformer extends JpqlQueryRenderer {
|
||||
|
||||
this.sort.forEach(order -> {
|
||||
|
||||
JpaQueryParser.checkSortExpression(order);
|
||||
JpaQueryParserSupport.checkSortExpression(order);
|
||||
|
||||
if (order.isIgnoreCase()) {
|
||||
tokens.add(TOKEN_LOWER_FUNC);
|
||||
@@ -144,7 +150,7 @@ class JpqlQueryTransformer extends JpqlQueryRenderer {
|
||||
@Override
|
||||
public List<JpaQueryParsingToken> visitSelect_clause(JpqlParser.Select_clauseContext ctx) {
|
||||
|
||||
List<JpaQueryParsingToken> tokens = new ArrayList<>();
|
||||
List<JpaQueryParsingToken> tokens = newArrayList();
|
||||
|
||||
tokens.add(new JpaQueryParsingToken(ctx.SELECT()));
|
||||
|
||||
@@ -156,7 +162,7 @@ class JpqlQueryTransformer extends JpqlQueryRenderer {
|
||||
tokens.add(new JpaQueryParsingToken(ctx.DISTINCT()));
|
||||
}
|
||||
|
||||
List<JpaQueryParsingToken> selectItemTokens = new ArrayList<>();
|
||||
List<JpaQueryParsingToken> selectItemTokens = newArrayList();
|
||||
|
||||
ctx.select_item().forEach(selectItemContext -> {
|
||||
selectItemTokens.addAll(visit(selectItemContext));
|
||||
@@ -192,8 +198,9 @@ class JpqlQueryTransformer extends JpqlQueryRenderer {
|
||||
tokens.addAll(selectItemTokens);
|
||||
}
|
||||
|
||||
if (projection == null) {
|
||||
if (!projectionProcessed) {
|
||||
this.projection = selectItemTokens;
|
||||
this.projectionProcessed = true;
|
||||
}
|
||||
|
||||
return tokens;
|
||||
@@ -202,7 +209,7 @@ class JpqlQueryTransformer extends JpqlQueryRenderer {
|
||||
@Override
|
||||
public List<JpaQueryParsingToken> visitRange_variable_declaration(JpqlParser.Range_variable_declarationContext ctx) {
|
||||
|
||||
List<JpaQueryParsingToken> tokens = new ArrayList<>();
|
||||
List<JpaQueryParsingToken> tokens = newArrayList();
|
||||
|
||||
tokens.addAll(visit(ctx.entity_name()));
|
||||
|
||||
@@ -226,4 +233,8 @@ class JpqlQueryTransformer extends JpqlQueryRenderer {
|
||||
|
||||
return super.visitConstructor_expression(ctx);
|
||||
}
|
||||
|
||||
private static <T> ArrayList<T> newArrayList() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,21 +17,36 @@ package org.springframework.data.jpa.repository.query;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Encapsulates different strategies for the creation of a {@link QueryEnhancer} from a {@link DeclaredQuery}.
|
||||
*
|
||||
* @author Diego Krupitza
|
||||
* @author Greg Turnquist
|
||||
* @author Mark Paluch
|
||||
* @since 2.7.0
|
||||
*/
|
||||
public final class QueryEnhancerFactory {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(QueryEnhancerFactory.class);
|
||||
|
||||
private static final boolean JSQLPARSER_IN_CLASSPATH = isJSqlParserInClassPath();
|
||||
private static final boolean jSqlParserPresent = ClassUtils.isPresent("net.sf.jsqlparser.parser.JSqlParser",
|
||||
QueryEnhancerFactory.class.getClassLoader());
|
||||
|
||||
private static final boolean HIBERNATE_IN_CLASSPATH = isHibernateInClassPath();
|
||||
private static final boolean hibernatePresent = ClassUtils.isPresent("org.hibernate.query.TypedParameterValue",
|
||||
QueryEnhancerFactory.class.getClassLoader());
|
||||
|
||||
static {
|
||||
|
||||
if (jSqlParserPresent) {
|
||||
LOG.info("JSqlParser is in classpath; If applicable, JSqlParser will be used");
|
||||
}
|
||||
|
||||
if (hibernatePresent) {
|
||||
LOG.info("Hibernate is in classpath; If applicable, HQL parser will be used.");
|
||||
}
|
||||
}
|
||||
|
||||
private QueryEnhancerFactory() {}
|
||||
|
||||
@@ -45,81 +60,17 @@ public final class QueryEnhancerFactory {
|
||||
|
||||
if (query.isNativeQuery()) {
|
||||
|
||||
if (qualifiesForJSqlParserUsage(query)) {
|
||||
/**
|
||||
if (jSqlParserPresent) {
|
||||
/*
|
||||
* If JSqlParser fails, throw some alert signaling that people should write a custom Impl.
|
||||
*/
|
||||
return new JSqlParserQueryEnhancer(query);
|
||||
} else {
|
||||
return new DefaultQueryEnhancer(query);
|
||||
}
|
||||
} else {
|
||||
|
||||
if (qualifiedForHqlParserUsage(query)) {
|
||||
return new JpaQueryParsingEnhancer(new HqlQueryParser(query));
|
||||
} else if (qualifiesForJpqlParserUsage(query)) {
|
||||
return new JpaQueryParsingEnhancer(new JpqlQueryParser(query));
|
||||
} else {
|
||||
return new DefaultQueryEnhancer(query);
|
||||
}
|
||||
return new DefaultQueryEnhancer(query);
|
||||
}
|
||||
|
||||
return hibernatePresent ? JpaQueryEnhancer.forHql(query) : JpaQueryEnhancer.forJpql(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given query can be process with the JSqlParser under the condition that the parser is in the classpath.
|
||||
*
|
||||
* @param query the query we want to check
|
||||
* @return <code>true</code> if JSqlParser is in the classpath and the query is classified as a native query and not
|
||||
* to be bypassed otherwise <code>false</code>
|
||||
*/
|
||||
private static boolean qualifiesForJSqlParserUsage(DeclaredQuery query) {
|
||||
return JSQLPARSER_IN_CLASSPATH && query.isNativeQuery();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the query is a candidate for the HQL parser.
|
||||
*
|
||||
* @param query the query we want to check
|
||||
* @return <code>true</code> if Hibernate is in the classpath and the query is NOT classified as native
|
||||
*/
|
||||
private static boolean qualifiedForHqlParserUsage(DeclaredQuery query) {
|
||||
return HIBERNATE_IN_CLASSPATH && !query.isNativeQuery();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the query is a candidate for the JPQL spec parser.
|
||||
*
|
||||
* @param query the query we want to check
|
||||
* @return <code>true</code> if the query is NOT classified as a native query
|
||||
*/
|
||||
private static boolean qualifiesForJpqlParserUsage(DeclaredQuery query) {
|
||||
return !query.isNativeQuery();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether JSqlParser is in classpath or not.
|
||||
*
|
||||
* @return <code>true</code> when in classpath otherwise <code>false</code>
|
||||
*/
|
||||
private static boolean isJSqlParserInClassPath() {
|
||||
|
||||
try {
|
||||
Class.forName("net.sf.jsqlparser.parser.JSqlParser", false, QueryEnhancerFactory.class.getClassLoader());
|
||||
LOG.info("JSqlParser is in classpath; If applicable JSqlParser will be used");
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isHibernateInClassPath() {
|
||||
|
||||
try {
|
||||
Class.forName("org.hibernate.query.TypedParameterValue", false, QueryEnhancerFactory.class.getClassLoader());
|
||||
LOG.info("Hibernate is in classpath; If applicable Hql61Parser will be used.");
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
/**
|
||||
* TCK Tests for {@link HqlQueryParser} mixed into {@link JpaQueryParsingEnhancer}.
|
||||
* TCK Tests for {@link HqlQueryParser} mixed into {@link JpaQueryEnhancer}.
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
* @since 3.1
|
||||
@@ -32,8 +32,8 @@ public class HqlParserQueryEnhancerUnitTests extends QueryEnhancerTckTests {
|
||||
public static final String HQL_PARSER_DOES_NOT_SUPPORT_NATIVE_QUERIES = "HqlParser does not support native queries";
|
||||
|
||||
@Override
|
||||
QueryEnhancer createQueryEnhancer(DeclaredQuery declaredQuery) {
|
||||
return new JpaQueryParsingEnhancer(new HqlQueryParser(declaredQuery));
|
||||
QueryEnhancer createQueryEnhancer(DeclaredQuery query) {
|
||||
return JpaQueryEnhancer.forHql(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -39,7 +39,7 @@ class HqlQueryRendererTests {
|
||||
|
||||
/**
|
||||
* Parse the query using {@link HqlParser} then run it through the query-preserving {@link HqlQueryRenderer}.
|
||||
*
|
||||
*
|
||||
* @param query
|
||||
*/
|
||||
private static String parseWithoutChanges(String query) {
|
||||
@@ -47,7 +47,7 @@ class HqlQueryRendererTests {
|
||||
HqlLexer lexer = new HqlLexer(CharStreams.fromString(query));
|
||||
HqlParser parser = new HqlParser(new CommonTokenStream(lexer));
|
||||
|
||||
parser.addErrorListener(new JpaQueryParsingSyntaxErrorListener());
|
||||
parser.addErrorListener(new BadJpqlGrammarErrorListener(query));
|
||||
|
||||
HqlParser.StartContext parsedQuery = parser.start();
|
||||
|
||||
|
||||
@@ -27,8 +27,7 @@ import org.springframework.data.jpa.domain.JpaSort;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Verify that HQL queries are properly transformed through the {@link JpaQueryParsingEnhancer} and the
|
||||
* {@link HqlQueryParser}.
|
||||
* Verify that HQL queries are properly transformed through the {@link JpaQueryEnhancer} and the {@link HqlQueryParser}.
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
* @since 3.1
|
||||
@@ -118,7 +117,7 @@ class HqlQueryTransformerTests {
|
||||
var original = "select e from Employee e join e.manager m";
|
||||
|
||||
// when
|
||||
var results = createQueryFor(original, null);
|
||||
var results = createQueryFor(original, Sort.unsorted());
|
||||
|
||||
// then
|
||||
assertThat(results).isEqualTo("select e from Employee e join e.manager m");
|
||||
@@ -208,12 +207,12 @@ class HqlQueryTransformerTests {
|
||||
|
||||
Sort sort = Sort.by(Sort.Order.desc("age"));
|
||||
|
||||
assertThat(new JpaQueryParsingEnhancer(new HqlQueryParser("select u\n" + //
|
||||
assertThat(newParser("select u\n" + //
|
||||
"from user u\n" + //
|
||||
"where exists (select u2\n" + //
|
||||
"from user u2\n" + //
|
||||
")\n" + //
|
||||
"")).applySorting(sort)).isEqualToIgnoringWhitespace("select u\n" + //
|
||||
"").applySorting(sort)).isEqualToIgnoringWhitespace("select u\n" + //
|
||||
"from user u\n" + //
|
||||
"where exists (select u2\n" + //
|
||||
"from user u2\n" + //
|
||||
@@ -790,7 +789,7 @@ class HqlQueryTransformerTests {
|
||||
}
|
||||
|
||||
private String createQueryFor(String query, Sort sort) {
|
||||
return new JpaQueryParsingEnhancer(new HqlQueryParser(query)).applySorting(sort);
|
||||
return newParser(query).applySorting(sort);
|
||||
}
|
||||
|
||||
private String createCountQueryFor(String query) {
|
||||
@@ -798,18 +797,23 @@ class HqlQueryTransformerTests {
|
||||
}
|
||||
|
||||
private String createCountQueryFor(String query, @Nullable String countProjection) {
|
||||
return new JpaQueryParsingEnhancer(new HqlQueryParser(query)).createCountQueryFor(countProjection);
|
||||
return newParser(query).createCountQueryFor(countProjection);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String alias(String query) {
|
||||
return new JpaQueryParsingEnhancer(new HqlQueryParser(query)).detectAlias();
|
||||
return newParser(query).detectAlias();
|
||||
}
|
||||
|
||||
private boolean hasConstructorExpression(String query) {
|
||||
return new JpaQueryParsingEnhancer(new HqlQueryParser(query)).hasConstructorExpression();
|
||||
return newParser(query).hasConstructorExpression();
|
||||
}
|
||||
|
||||
private String projection(String query) {
|
||||
return new JpaQueryParsingEnhancer(new HqlQueryParser(query)).getProjection();
|
||||
return newParser(query).getProjection();
|
||||
}
|
||||
|
||||
private QueryEnhancer newParser(String query) {
|
||||
return JpaQueryEnhancer.forHql(DeclaredQuery.of(query, false));
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,7 +22,7 @@ import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
/**
|
||||
* TCK Tests for {@link JpqlQueryParser} mixed into {@link JpaQueryParsingEnhancer}.
|
||||
* TCK Tests for {@link JpqlQueryParser} mixed into {@link JpaQueryEnhancer}.
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
* @since 3.1
|
||||
@@ -33,7 +33,7 @@ public class JpqlParserQueryEnhancerUnitTests extends QueryEnhancerTckTests {
|
||||
|
||||
@Override
|
||||
QueryEnhancer createQueryEnhancer(DeclaredQuery declaredQuery) {
|
||||
return new JpaQueryParsingEnhancer(new JpqlQueryParser(declaredQuery));
|
||||
return JpaQueryEnhancer.forJpql(declaredQuery);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -46,7 +46,7 @@ class JpqlQueryRendererTests {
|
||||
JpqlLexer lexer = new JpqlLexer(CharStreams.fromString(query));
|
||||
JpqlParser parser = new JpqlParser(new CommonTokenStream(lexer));
|
||||
|
||||
parser.addErrorListener(new JpaQueryParsingSyntaxErrorListener());
|
||||
parser.addErrorListener(new BadJpqlGrammarErrorListener(query));
|
||||
|
||||
JpqlParser.StartContext parsedQuery = parser.start();
|
||||
|
||||
@@ -762,7 +762,7 @@ class JpqlQueryRendererTests {
|
||||
@Test
|
||||
void theRest24() {
|
||||
|
||||
assertThatExceptionOfType(JpaQueryParsingSyntaxError.class).isThrownBy(() -> {
|
||||
assertThatExceptionOfType(BadJpqlGrammarException.class).isThrownBy(() -> {
|
||||
assertQuery("""
|
||||
SELECT p.product_name
|
||||
FROM Order o, IN(o.lineItems) l JOIN o.customer c
|
||||
|
||||
@@ -27,7 +27,7 @@ import org.springframework.data.jpa.domain.JpaSort;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Verify that JPQL queries are properly transformed through the {@link JpaQueryParsingEnhancer} and the
|
||||
* Verify that JPQL queries are properly transformed through the {@link JpaQueryEnhancer} and the
|
||||
* {@link JpqlQueryParser}.
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
@@ -117,7 +117,7 @@ class JpqlQueryTransformerTests {
|
||||
var original = "select e from Employee e join e.manager m";
|
||||
|
||||
// when
|
||||
var results = createQueryFor(original, null);
|
||||
var results = createQueryFor(original, Sort.unsorted());
|
||||
|
||||
// then
|
||||
assertThat(results).isEqualTo("select e from Employee e join e.manager m");
|
||||
@@ -197,12 +197,12 @@ class JpqlQueryTransformerTests {
|
||||
|
||||
Sort sort = Sort.by(Sort.Order.desc("age"));
|
||||
|
||||
assertThat(new JpaQueryParsingEnhancer(new JpqlQueryParser("select u\n" + //
|
||||
assertThat(newParser("select u\n" + //
|
||||
"from user u\n" + //
|
||||
"where exists (select u2\n" + //
|
||||
"from user u2\n" + //
|
||||
")\n" + //
|
||||
"")).applySorting(sort)).isEqualToIgnoringWhitespace("select u\n" + //
|
||||
"").applySorting(sort)).isEqualToIgnoringWhitespace("select u\n" + //
|
||||
"from user u\n" + //
|
||||
"where exists (select u2\n" + //
|
||||
"from user u2\n" + //
|
||||
@@ -679,7 +679,7 @@ class JpqlQueryTransformerTests {
|
||||
}
|
||||
|
||||
private String createQueryFor(String query, Sort sort) {
|
||||
return new JpaQueryParsingEnhancer(new JpqlQueryParser(query)).applySorting(sort);
|
||||
return newParser(query).applySorting(sort);
|
||||
}
|
||||
|
||||
private String createCountQueryFor(String query) {
|
||||
@@ -687,18 +687,22 @@ class JpqlQueryTransformerTests {
|
||||
}
|
||||
|
||||
private String createCountQueryFor(String original, @Nullable String countProjection) {
|
||||
return new JpaQueryParsingEnhancer(new JpqlQueryParser(original)).createCountQueryFor(countProjection);
|
||||
return newParser(original).createCountQueryFor(countProjection);
|
||||
}
|
||||
|
||||
private String alias(String query) {
|
||||
return new JpaQueryParsingEnhancer(new JpqlQueryParser(query)).detectAlias();
|
||||
return newParser(query).detectAlias();
|
||||
}
|
||||
|
||||
private boolean hasConstructorExpression(String query) {
|
||||
return new JpaQueryParsingEnhancer(new JpqlQueryParser(query)).hasConstructorExpression();
|
||||
return newParser(query).hasConstructorExpression();
|
||||
}
|
||||
|
||||
private String projection(String query) {
|
||||
return new JpaQueryParsingEnhancer(new JpqlQueryParser(query)).getProjection();
|
||||
return newParser(query).getProjection();
|
||||
}
|
||||
|
||||
private QueryEnhancer newParser(String query) {
|
||||
return JpaQueryEnhancer.forJpql(DeclaredQuery.of(query, false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void joinExample1() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT DISTINCT o
|
||||
FROM Order AS o JOIN o.lineItems AS l
|
||||
WHERE l.shipped = FALSE
|
||||
@@ -53,7 +53,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void joinExample2() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT DISTINCT o
|
||||
FROM Order o JOIN o.lineItems l JOIN l.product p
|
||||
WHERE p.productType = 'office_supplies'
|
||||
@@ -66,7 +66,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void rangeVariableDeclarations() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT DISTINCT o1
|
||||
FROM Order o1, Order o2
|
||||
WHERE o1.quantity > o2.quantity AND
|
||||
@@ -81,7 +81,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void pathExpressionsExample1() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT i.name, VALUE(p)
|
||||
FROM Item i JOIN i.photos p
|
||||
WHERE KEY(p) LIKE '%egret'
|
||||
@@ -94,7 +94,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void pathExpressionsExample2() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT i.name, p
|
||||
FROM Item i JOIN i.photos p
|
||||
WHERE KEY(p) LIKE '%egret'
|
||||
@@ -107,7 +107,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void pathExpressionsExample3() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT p.vendor
|
||||
FROM Employee e JOIN e.contactInfo.phones p
|
||||
""");
|
||||
@@ -119,7 +119,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void pathExpressionsExample4() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT p.vendor
|
||||
FROM Employee e JOIN e.contactInfo c JOIN c.phones p
|
||||
WHERE e.contactInfo.address.zipcode = '95054'
|
||||
@@ -129,7 +129,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void pathExpressionSyntaxExample1() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT DISTINCT l.product
|
||||
FROM Order AS o JOIN o.lineItems l
|
||||
""");
|
||||
@@ -138,7 +138,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void joinsExample1() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT c FROM Customer c, Employee e WHERE c.hatsize = e.shoesize
|
||||
""");
|
||||
}
|
||||
@@ -146,7 +146,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void joinsExample2() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT c FROM Customer c JOIN c.orders o WHERE c.status = 1
|
||||
""");
|
||||
}
|
||||
@@ -154,7 +154,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void joinsInnerExample() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT c FROM Customer c INNER JOIN c.orders o WHERE c.status = 1
|
||||
""");
|
||||
}
|
||||
@@ -162,7 +162,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void joinsInExample() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT OBJECT(c) FROM Customer c, IN(c.orders) o WHERE c.status = 1
|
||||
""");
|
||||
}
|
||||
@@ -170,7 +170,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void doubleJoinExample() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT p.vendor
|
||||
FROM Employee e JOIN e.contactInfo c JOIN c.phones p
|
||||
WHERE c.address.zipcode = '95054'
|
||||
@@ -180,7 +180,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void leftJoinExample() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT s.name, COUNT(p)
|
||||
FROM Suppliers s LEFT JOIN s.products p
|
||||
GROUP BY s.name
|
||||
@@ -190,7 +190,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void leftJoinOnExample() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT s.name, COUNT(p)
|
||||
FROM Suppliers s LEFT JOIN s.products p
|
||||
ON p.status = 'inStock'
|
||||
@@ -201,7 +201,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void leftJoinWhereExample() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT s.name, COUNT(p)
|
||||
FROM Suppliers s LEFT JOIN s.products p
|
||||
WHERE p.status = 'inStock'
|
||||
@@ -212,7 +212,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void leftJoinFetchExample() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT d
|
||||
FROM Department d LEFT JOIN FETCH d.employees
|
||||
WHERE d.deptno = 1
|
||||
@@ -222,7 +222,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void collectionMemberExample() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT DISTINCT o
|
||||
FROM Order o JOIN o.lineItems l
|
||||
WHERE l.product.productType = 'office_supplies'
|
||||
@@ -232,7 +232,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void collectionMemberInExample() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT DISTINCT o
|
||||
FROM Order o, IN(o.lineItems) l
|
||||
WHERE l.product.productType = 'office_supplies'
|
||||
@@ -242,7 +242,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void fromClauseExample() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT o
|
||||
FROM Order AS o JOIN o.lineItems l JOIN l.product p
|
||||
""");
|
||||
@@ -251,7 +251,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void fromClauseDowncastingExample1() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT b.name, b.ISBN
|
||||
FROM Order o JOIN TREAT(o.product AS Book) b
|
||||
""");
|
||||
@@ -260,7 +260,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void fromClauseDowncastingExample2() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT e FROM Employee e JOIN TREAT(e.projects AS LargeProject) lp
|
||||
WHERE lp.budget > 1000
|
||||
""");
|
||||
@@ -273,7 +273,7 @@ class JpqlSpecificationTests {
|
||||
@Disabled(SPEC_FAULT + "Use double-quotes when it should be using single-quotes for a string literal")
|
||||
void fromClauseDowncastingExample3_SPEC_BUG() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT e FROM Employee e JOIN e.projects p
|
||||
WHERE TREAT(p AS LargeProject).budget > 1000
|
||||
OR TREAT(p AS SmallProject).name LIKE 'Persist%'
|
||||
@@ -284,7 +284,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void fromClauseDowncastingExample3fixed() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT e FROM Employee e JOIN e.projects p
|
||||
WHERE TREAT(p AS LargeProject).budget > 1000
|
||||
OR TREAT(p AS SmallProject).name LIKE 'Persist%'
|
||||
@@ -295,7 +295,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void fromClauseDowncastingExample4() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT e FROM Employee e
|
||||
WHERE TREAT(e AS Exempt).vacationDays > 10
|
||||
OR TREAT(e AS Contractor).hours > 100
|
||||
@@ -305,7 +305,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void pathExpressionsNamedParametersExample() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT c
|
||||
FROM Customer c
|
||||
WHERE c.status = :stat
|
||||
@@ -315,7 +315,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void betweenExpressionsExample() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT t
|
||||
FROM CreditCard c JOIN c.transactionHistory t
|
||||
WHERE c.holder.name = 'John Doe' AND INDEX(t) BETWEEN 0 AND 9
|
||||
@@ -325,7 +325,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void isEmptyExample() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT o
|
||||
FROM Order o
|
||||
WHERE o.lineItems IS EMPTY
|
||||
@@ -335,7 +335,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void memberOfExample() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT p
|
||||
FROM Person p
|
||||
WHERE 'Joe' MEMBER OF p.nicknames
|
||||
@@ -345,7 +345,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void existsSubSelectExample1() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT DISTINCT emp
|
||||
FROM Employee emp
|
||||
WHERE EXISTS (
|
||||
@@ -358,7 +358,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void allExample() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT emp
|
||||
FROM Employee emp
|
||||
WHERE emp.salary > ALL (
|
||||
@@ -371,7 +371,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void existsSubSelectExample2() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT DISTINCT emp
|
||||
FROM Employee emp
|
||||
WHERE EXISTS (
|
||||
@@ -384,7 +384,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void subselectNumericComparisonExample1() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT c
|
||||
FROM Customer c
|
||||
WHERE (SELECT AVG(o.price) FROM c.orders o) > 100
|
||||
@@ -394,7 +394,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void subselectNumericComparisonExample2() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT goodCustomer
|
||||
FROM Customer goodCustomer
|
||||
WHERE goodCustomer.balanceOwed < (
|
||||
@@ -405,7 +405,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void indexExample() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT w.name
|
||||
FROM Course c JOIN c.studentWaitlist w
|
||||
WHERE c.name = 'Calculus'
|
||||
@@ -420,7 +420,7 @@ class JpqlSpecificationTests {
|
||||
@Disabled(SPEC_FAULT + "FUNCTION calls needs a comparator")
|
||||
void functionInvocationExample_SPEC_BUG() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT c
|
||||
FROM Customer c
|
||||
WHERE FUNCTION('hasGoodCredit', c.balance, c.creditLimit)
|
||||
@@ -430,7 +430,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void functionInvocationExampleWithCorrection() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT c
|
||||
FROM Customer c
|
||||
WHERE FUNCTION('hasGoodCredit', c.balance, c.creditLimit) = TRUE
|
||||
@@ -440,7 +440,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void updateCaseExample1() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
UPDATE Employee e
|
||||
SET e.salary =
|
||||
CASE WHEN e.rating = 1 THEN e.salary * 1.1
|
||||
@@ -453,7 +453,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void updateCaseExample2() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
UPDATE Employee e
|
||||
SET e.salary =
|
||||
CASE e.rating WHEN 1 THEN e.salary * 1.1
|
||||
@@ -466,7 +466,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void selectCaseExample1() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT e.name,
|
||||
CASE TYPE(e) WHEN Exempt THEN 'Exempt'
|
||||
WHEN Contractor THEN 'Contractor'
|
||||
@@ -481,7 +481,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void selectCaseExample2() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT e.name,
|
||||
f.name,
|
||||
CONCAT(CASE WHEN f.annualMiles > 50000 THEN 'Platinum '
|
||||
@@ -496,7 +496,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT e
|
||||
FROM Employee e
|
||||
WHERE TYPE(e) IN (Exempt, Contractor)
|
||||
@@ -506,7 +506,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest2() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT e
|
||||
FROM Employee e
|
||||
WHERE TYPE(e) IN (:empType1, :empType2)
|
||||
@@ -516,7 +516,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest3() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT e
|
||||
FROM Employee e
|
||||
WHERE TYPE(e) IN :empTypes
|
||||
@@ -526,7 +526,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest4() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT TYPE(e)
|
||||
FROM Employee e
|
||||
WHERE TYPE(e) <> Exempt
|
||||
@@ -536,7 +536,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest5() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT c.status, AVG(c.filledOrderCount), COUNT(c)
|
||||
FROM Customer c
|
||||
GROUP BY c.status
|
||||
@@ -547,7 +547,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest6() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT c.country, COUNT(c)
|
||||
FROM Customer c
|
||||
GROUP BY c.country
|
||||
@@ -558,7 +558,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest7() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT c, COUNT(o)
|
||||
FROM Customer c JOIN c.orders o
|
||||
GROUP BY c
|
||||
@@ -569,7 +569,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest8() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT c.id, c.status
|
||||
FROM Customer c JOIN c.orders o
|
||||
WHERE o.count > 100
|
||||
@@ -579,7 +579,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest9() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT v.location.street, KEY(i).title, VALUE(i)
|
||||
FROM VideoStore v JOIN v.videoInventory i
|
||||
WHERE v.location.zipcode = '94301' AND VALUE(i) > 0
|
||||
@@ -589,7 +589,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest10() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT o.lineItems FROM Order AS o
|
||||
""");
|
||||
}
|
||||
@@ -597,7 +597,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest11() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT c, COUNT(l) AS itemCount
|
||||
FROM Customer c JOIN c.Orders o JOIN o.lineItems l
|
||||
WHERE c.address.state = 'CA'
|
||||
@@ -609,7 +609,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest12() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT NEW com.acme.example.CustomerDetails(c.id, c.status, o.count)
|
||||
FROM Customer c JOIN c.orders o
|
||||
WHERE o.count > 100
|
||||
@@ -619,7 +619,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest13() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT e.address AS addr
|
||||
FROM Employee e
|
||||
""");
|
||||
@@ -628,7 +628,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest14() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT AVG(o.quantity) FROM Order o
|
||||
""");
|
||||
}
|
||||
@@ -636,7 +636,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest15() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT SUM(l.price)
|
||||
FROM Order o JOIN o.lineItems l JOIN o.customer c
|
||||
WHERE c.lastname = 'Smith' AND c.firstname = 'John'
|
||||
@@ -646,7 +646,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest16() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT COUNT(o) FROM Order o
|
||||
""");
|
||||
}
|
||||
@@ -654,7 +654,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest17() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT COUNT(l.price)
|
||||
FROM Order o JOIN o.lineItems l JOIN o.customer c
|
||||
WHERE c.lastname = 'Smith' AND c.firstname = 'John'
|
||||
@@ -664,7 +664,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest18() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT COUNT(l)
|
||||
FROM Order o JOIN o.lineItems l JOIN o.customer c
|
||||
WHERE c.lastname = 'Smith' AND c.firstname = 'John' AND l.price IS NOT NULL
|
||||
@@ -674,7 +674,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest19() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT o
|
||||
FROM Customer c JOIN c.orders o JOIN c.address a
|
||||
WHERE a.state = 'CA'
|
||||
@@ -685,7 +685,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest20() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT o.quantity, a.zipcode
|
||||
FROM Customer c JOIN c.orders o JOIN c.address a
|
||||
WHERE a.state = 'CA'
|
||||
@@ -696,7 +696,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest21() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT o.quantity, o.cost*1.08 AS taxedCost, a.zipcode
|
||||
FROM Customer c JOIN c.orders o JOIN c.address a
|
||||
WHERE a.state = 'CA' AND a.county = 'Santa Clara'
|
||||
@@ -707,7 +707,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest22() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT AVG(o.quantity) as q, a.zipcode
|
||||
FROM Customer c JOIN c.orders o JOIN c.address a
|
||||
WHERE a.state = 'CA'
|
||||
@@ -719,7 +719,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest23() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT p.product_name
|
||||
FROM Order o JOIN o.lineItems l JOIN l.product p JOIN o.customer c
|
||||
WHERE c.lastname = 'Smith' AND c.firstname = 'John'
|
||||
@@ -733,8 +733,8 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest24() {
|
||||
|
||||
assertThatExceptionOfType(JpaQueryParsingSyntaxError.class).isThrownBy(() -> {
|
||||
JpqlQueryParser.parse("""
|
||||
assertThatExceptionOfType(BadJpqlGrammarException.class).isThrownBy(() -> {
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT p.product_name
|
||||
FROM Order o, IN(o.lineItems) l JOIN o.customer c
|
||||
WHERE c.lastname = 'Smith' AND c.firstname = 'John'
|
||||
@@ -746,7 +746,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest25() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
DELETE
|
||||
FROM Customer c
|
||||
WHERE c.status = 'inactive'
|
||||
@@ -756,7 +756,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest26() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
DELETE
|
||||
FROM Customer c
|
||||
WHERE c.status = 'inactive'
|
||||
@@ -767,7 +767,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest27() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
UPDATE Customer c
|
||||
SET c.status = 'outstanding'
|
||||
WHERE c.balance < 10000
|
||||
@@ -777,7 +777,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest28() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
UPDATE Employee e
|
||||
SET e.address.building = 22
|
||||
WHERE e.address.building = 14
|
||||
@@ -789,7 +789,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest29() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT o
|
||||
FROM Order o
|
||||
""");
|
||||
@@ -798,7 +798,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest30() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT o
|
||||
FROM Order o
|
||||
WHERE o.shippingAddress.state = 'CA'
|
||||
@@ -808,7 +808,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest31() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT DISTINCT o.shippingAddress.state
|
||||
FROM Order o
|
||||
""");
|
||||
@@ -817,7 +817,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest32() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT DISTINCT o
|
||||
FROM Order o JOIN o.lineItems l
|
||||
""");
|
||||
@@ -826,7 +826,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest33() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT o
|
||||
FROM Order o
|
||||
WHERE o.lineItems IS NOT EMPTY
|
||||
@@ -836,7 +836,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest34() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT o
|
||||
FROM Order o
|
||||
WHERE o.lineItems IS EMPTY
|
||||
@@ -846,7 +846,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest35() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT DISTINCT o
|
||||
FROM Order o JOIN o.lineItems l
|
||||
WHERE l.shipped = FALSE
|
||||
@@ -856,7 +856,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest36() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT o
|
||||
FROM Order o
|
||||
WHERE
|
||||
@@ -869,7 +869,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest37() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT o
|
||||
FROM Order o
|
||||
WHERE o.shippingAddress <> o.billingAddress
|
||||
@@ -879,7 +879,7 @@ class JpqlSpecificationTests {
|
||||
@Test
|
||||
void theRest38() {
|
||||
|
||||
JpqlQueryParser.parse("""
|
||||
JpqlQueryParser.parseQuery("""
|
||||
SELECT DISTINCT o
|
||||
FROM Order o JOIN o.lineItems l
|
||||
WHERE l.product.name = ?1
|
||||
|
||||
@@ -35,9 +35,9 @@ class QueryEnhancerFactoryUnitTests {
|
||||
QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(query);
|
||||
|
||||
assertThat(queryEnhancer) //
|
||||
.isInstanceOf(JpaQueryParsingEnhancer.class);
|
||||
.isInstanceOf(JpaQueryEnhancer.class);
|
||||
|
||||
JpaQueryParsingEnhancer queryParsingEnhancer = (JpaQueryParsingEnhancer) queryEnhancer;
|
||||
JpaQueryEnhancer queryParsingEnhancer = (JpaQueryEnhancer) queryEnhancer;
|
||||
|
||||
assertThat(queryParsingEnhancer.getQueryParsingStrategy()).isInstanceOf(HqlQueryParser.class);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.assertj.core.api.SoftAssertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.jpa.repository.query.StringQuery.InParameterBinding;
|
||||
import org.springframework.data.jpa.repository.query.StringQuery.LikeParameterBinding;
|
||||
@@ -40,8 +39,6 @@ import org.springframework.data.repository.query.parser.Part.Type;
|
||||
*/
|
||||
class StringQueryUnitTests {
|
||||
|
||||
private SoftAssertions softly = new SoftAssertions();
|
||||
|
||||
@Test // DATAJPA-341
|
||||
void doesNotConsiderPlainLikeABinding() {
|
||||
|
||||
@@ -115,7 +112,6 @@ class StringQueryUnitTests {
|
||||
|
||||
assertNamedBinding(InParameterBinding.class, "ids", bindings.get(0));
|
||||
|
||||
softly.assertAll();
|
||||
}
|
||||
|
||||
@Test // DATAJPA-461
|
||||
@@ -133,8 +129,6 @@ class StringQueryUnitTests {
|
||||
assertNamedBinding(InParameterBinding.class, "ids", bindings.get(0));
|
||||
assertNamedBinding(InParameterBinding.class, "names", bindings.get(1));
|
||||
assertNamedBinding(ParameterBinding.class, "bar", bindings.get(2));
|
||||
|
||||
softly.assertAll();
|
||||
}
|
||||
|
||||
@Test // DATAJPA-461
|
||||
@@ -151,7 +145,6 @@ class StringQueryUnitTests {
|
||||
|
||||
assertPositionalBinding(InParameterBinding.class, 1, bindings.get(0));
|
||||
|
||||
softly.assertAll();
|
||||
}
|
||||
|
||||
@Test // DATAJPA-461
|
||||
@@ -170,7 +163,6 @@ class StringQueryUnitTests {
|
||||
assertPositionalBinding(InParameterBinding.class, 2, bindings.get(1));
|
||||
assertPositionalBinding(ParameterBinding.class, 3, bindings.get(2));
|
||||
|
||||
softly.assertAll();
|
||||
}
|
||||
|
||||
@Test // DATAJPA-373
|
||||
@@ -193,7 +185,6 @@ class StringQueryUnitTests {
|
||||
assertThat(bindings).hasSize(1);
|
||||
assertPositionalBinding(ParameterBinding.class, 1, bindings.get(0));
|
||||
|
||||
softly.assertAll();
|
||||
}
|
||||
|
||||
@Test // DATAJPA-473
|
||||
@@ -208,11 +199,8 @@ class StringQueryUnitTests {
|
||||
assertNamedBinding(LikeParameterBinding.class, "escapedWord", bindings.get(0));
|
||||
assertNamedBinding(ParameterBinding.class, "word", bindings.get(1));
|
||||
|
||||
softly.assertThat(query.getQueryString())
|
||||
.isEqualTo("SELECT a FROM Article a WHERE a.overview LIKE :escapedWord ESCAPE '~'"
|
||||
+ " OR a.content LIKE :escapedWord ESCAPE '~' OR a.title = :word ORDER BY a.articleId DESC");
|
||||
|
||||
softly.assertAll();
|
||||
assertThat(query.getQueryString()).isEqualTo("SELECT a FROM Article a WHERE a.overview LIKE :escapedWord ESCAPE '~'"
|
||||
+ " OR a.content LIKE :escapedWord ESCAPE '~' OR a.title = :word ORDER BY a.articleId DESC");
|
||||
}
|
||||
|
||||
@Test // DATAJPA-483
|
||||
@@ -224,8 +212,6 @@ class StringQueryUnitTests {
|
||||
|
||||
assertThat(bindings).hasSize(1);
|
||||
assertNamedBinding(InParameterBinding.class, "statuses", bindings.get(0));
|
||||
|
||||
softly.assertAll();
|
||||
}
|
||||
|
||||
@Test // DATAJPA-545
|
||||
@@ -238,7 +224,6 @@ class StringQueryUnitTests {
|
||||
assertThat(bindings).hasSize(1);
|
||||
assertNamedBinding(InParameterBinding.class, "abonnés", bindings.get(0));
|
||||
|
||||
softly.assertAll();
|
||||
}
|
||||
|
||||
@Test // DATAJPA-545
|
||||
@@ -250,8 +235,6 @@ class StringQueryUnitTests {
|
||||
|
||||
assertThat(bindings).hasSize(1);
|
||||
assertNamedBinding(InParameterBinding.class, "øre", bindings.get(0));
|
||||
|
||||
softly.assertAll();
|
||||
}
|
||||
|
||||
@Test // DATAJPA-545
|
||||
@@ -263,8 +246,6 @@ class StringQueryUnitTests {
|
||||
|
||||
assertThat(bindings).hasSize(1);
|
||||
assertNamedBinding(InParameterBinding.class, "생일", bindings.get(0));
|
||||
|
||||
softly.assertAll();
|
||||
}
|
||||
|
||||
@Test // DATAJPA-545
|
||||
@@ -276,8 +257,6 @@ class StringQueryUnitTests {
|
||||
|
||||
assertThat(bindings).hasSize(1);
|
||||
assertNamedBinding(InParameterBinding.class, "ab1babc생일233", bindings.get(0));
|
||||
|
||||
softly.assertAll();
|
||||
}
|
||||
|
||||
@Test // DATAJPA-362
|
||||
@@ -301,27 +280,20 @@ class StringQueryUnitTests {
|
||||
StringQuery query = new StringQuery("select a from A a where a.b in ?#{#bs} and a.c in ?#{#cs}", true);
|
||||
String queryString = query.getQueryString();
|
||||
|
||||
softly.assertThat(queryString).isEqualTo("select a from A a where a.b in ?1 and a.c in ?2");
|
||||
softly.assertThat(query.getParameterBindings().get(0).getExpression()).isEqualTo("#bs");
|
||||
softly.assertThat(query.getParameterBindings().get(1).getExpression()).isEqualTo("#cs");
|
||||
|
||||
softly.assertAll();
|
||||
assertThat(queryString).isEqualTo("select a from A a where a.b in ?1 and a.c in ?2");
|
||||
assertThat(query.getParameterBindings().get(0).getExpression()).isEqualTo("#bs");
|
||||
assertThat(query.getParameterBindings().get(1).getExpression()).isEqualTo("#cs");
|
||||
}
|
||||
|
||||
@Test // DATAJPA-864
|
||||
void detectsConstructorExpressions() {
|
||||
|
||||
softly
|
||||
.assertThat(
|
||||
new StringQuery("select new com.example.Dto(a.foo, a.bar) from A a", false).hasConstructorExpression())
|
||||
assertThat(
|
||||
new StringQuery("select new com.example.Dto(a.foo, a.bar) from A a", false).hasConstructorExpression())
|
||||
.isTrue();
|
||||
assertThat(new StringQuery("select new com.example.Dto (a.foo, a.bar) from A a", false).hasConstructorExpression())
|
||||
.isTrue();
|
||||
softly
|
||||
.assertThat(
|
||||
new StringQuery("select new com.example.Dto (a.foo, a.bar) from A a", false).hasConstructorExpression())
|
||||
.isTrue();
|
||||
softly.assertThat(new StringQuery("select a from A a", true).hasConstructorExpression()).isFalse();
|
||||
|
||||
softly.assertAll();
|
||||
assertThat(new StringQuery("select a from A a", true).hasConstructorExpression()).isFalse();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -332,10 +304,8 @@ class StringQueryUnitTests {
|
||||
void detectsConstructorExpressionForDefaultConstructor() {
|
||||
|
||||
// Parentheses required
|
||||
softly.assertThat(new StringQuery("select new com.example.Dto(a.name) from A a", false).hasConstructorExpression())
|
||||
assertThat(new StringQuery("select new com.example.Dto(a.name) from A a", false).hasConstructorExpression())
|
||||
.isTrue();
|
||||
|
||||
softly.assertAll();
|
||||
}
|
||||
|
||||
@Test // DATAJPA-1179
|
||||
@@ -344,15 +314,13 @@ class StringQueryUnitTests {
|
||||
StringQuery query = new StringQuery("select a from A a where a.first = :#{#exp} or a.second = :#{#exp}", true);
|
||||
|
||||
List<ParameterBinding> bindings = query.getParameterBindings();
|
||||
softly.assertThat(bindings).isNotEmpty();
|
||||
assertThat(bindings).isNotEmpty();
|
||||
|
||||
for (ParameterBinding binding : bindings) {
|
||||
softly.assertThat(binding.getName()).isNotNull();
|
||||
softly.assertThat(query.getQueryString()).contains(binding.getName());
|
||||
softly.assertThat(binding.getExpression()).isEqualTo("#exp");
|
||||
assertThat(binding.getName()).isNotNull();
|
||||
assertThat(query.getQueryString()).contains(binding.getName());
|
||||
assertThat(binding.getExpression()).isEqualTo("#exp");
|
||||
}
|
||||
|
||||
softly.assertAll();
|
||||
}
|
||||
|
||||
@Test // DATAJPA-1235
|
||||
@@ -364,13 +332,11 @@ class StringQueryUnitTests {
|
||||
|
||||
checkProjection("sect x, y, z from Entity something", "", "missing select", false);
|
||||
checkProjection("select x, y, z fron Entity something", "", "missing from", false);
|
||||
|
||||
softly.assertAll();
|
||||
}
|
||||
|
||||
void checkProjection(String query, String expected, String description, boolean nativeQuery) {
|
||||
|
||||
softly.assertThat(new StringQuery(query, nativeQuery).getProjection()) //
|
||||
assertThat(new StringQuery(query, nativeQuery).getProjection()) //
|
||||
.as("%s (%s)", description, query) //
|
||||
.isEqualTo(expected);
|
||||
}
|
||||
@@ -390,13 +356,11 @@ class StringQueryUnitTests {
|
||||
checkAlias("from User as bs", "bs", "ignored as", false);
|
||||
checkAlias("from User as AS", "AS", "ignored as using the second", false);
|
||||
checkAlias("from User asas", "asas", "asas is weird but legal", false);
|
||||
|
||||
softly.assertAll();
|
||||
}
|
||||
|
||||
private void checkAlias(String query, String expected, String description, boolean nativeQuery) {
|
||||
|
||||
softly.assertThat(new StringQuery(query, nativeQuery).getAlias()) //
|
||||
assertThat(new StringQuery(query, nativeQuery).getAlias()) //
|
||||
.as("%s (%s)", description, query) //
|
||||
.isEqualTo(expected);
|
||||
}
|
||||
@@ -430,8 +394,6 @@ class StringQueryUnitTests {
|
||||
checkHasNamedParameter("::id", false, "double colon with identifier", false);
|
||||
checkHasNamedParameter("\\:id", false, "escaped colon with identifier", false);
|
||||
checkHasNamedParameter("select something from x where id = #something", false, "hash", true);
|
||||
|
||||
softly.assertAll();
|
||||
}
|
||||
|
||||
@Test // DATAJPA-1235
|
||||
@@ -445,8 +407,6 @@ class StringQueryUnitTests {
|
||||
// checkNumberOfNamedParameters("select something from blah where x = \"'0\":name", 1, "single quote in double
|
||||
// quotes",
|
||||
// false);
|
||||
|
||||
softly.assertAll();
|
||||
}
|
||||
|
||||
@Test // DATAJPA-1307
|
||||
@@ -455,11 +415,9 @@ class StringQueryUnitTests {
|
||||
String queryString = "select u from User u where u.id in ? and u.names in ? and foo = ?";
|
||||
StringQuery query = new StringQuery(queryString, false);
|
||||
|
||||
softly.assertThat(query.getQueryString()).isEqualTo(queryString);
|
||||
softly.assertThat(query.hasParameterBindings()).isTrue();
|
||||
softly.assertThat(query.getParameterBindings()).hasSize(3);
|
||||
|
||||
softly.assertAll();
|
||||
assertThat(query.getQueryString()).isEqualTo(queryString);
|
||||
assertThat(query.hasParameterBindings()).isTrue();
|
||||
assertThat(query.getParameterBindings()).hasSize(3);
|
||||
}
|
||||
|
||||
@Test // DATAJPA-1307
|
||||
@@ -482,7 +440,7 @@ class StringQueryUnitTests {
|
||||
@Test // DATAJPA-1307
|
||||
void makesUsageOfJdbcStyleParameterAvailable() {
|
||||
|
||||
softly.assertThat(new StringQuery("from Something something where something = ?", false).usesJdbcStyleParameters())
|
||||
assertThat(new StringQuery("from Something something where something = ?", false).usesJdbcStyleParameters())
|
||||
.isTrue();
|
||||
|
||||
List<String> testQueries = Arrays.asList( //
|
||||
@@ -493,13 +451,11 @@ class StringQueryUnitTests {
|
||||
|
||||
for (String testQuery : testQueries) {
|
||||
|
||||
softly.assertThat(new StringQuery(testQuery, false) //
|
||||
assertThat(new StringQuery(testQuery, false) //
|
||||
.usesJdbcStyleParameters()) //
|
||||
.describedAs(testQuery) //
|
||||
.isFalse();
|
||||
.describedAs(testQuery) //
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
softly.assertAll();
|
||||
}
|
||||
|
||||
@Test // DATAJPA-1307
|
||||
@@ -508,11 +464,10 @@ class StringQueryUnitTests {
|
||||
String queryString = "select '? ' from dual";
|
||||
StringQuery query = new StringQuery(queryString, true);
|
||||
|
||||
softly.assertThat(query.getQueryString()).isEqualTo(queryString);
|
||||
softly.assertThat(query.hasParameterBindings()).isFalse();
|
||||
softly.assertThat(query.getParameterBindings()).isEmpty();
|
||||
assertThat(query.getQueryString()).isEqualTo(queryString);
|
||||
assertThat(query.hasParameterBindings()).isFalse();
|
||||
assertThat(query.getParameterBindings()).isEmpty();
|
||||
|
||||
softly.assertAll();
|
||||
}
|
||||
|
||||
@Test // DATAJPA-1318
|
||||
@@ -527,7 +482,7 @@ class StringQueryUnitTests {
|
||||
"select a, b from C");
|
||||
|
||||
for (String queryString : queriesWithoutDefaultProjection) {
|
||||
softly.assertThat(new StringQuery(queryString, true).isDefaultProjection()) //
|
||||
assertThat(new StringQuery(queryString, true).isDefaultProjection()) //
|
||||
.describedAs(queryString) //
|
||||
.isFalse();
|
||||
}
|
||||
@@ -544,12 +499,10 @@ class StringQueryUnitTests {
|
||||
);
|
||||
|
||||
for (String queryString : queriesWithDefaultProjection) {
|
||||
softly.assertThat(new StringQuery(queryString, true).isDefaultProjection()) //
|
||||
assertThat(new StringQuery(queryString, true).isDefaultProjection()) //
|
||||
.describedAs(queryString) //
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
softly.assertAll();
|
||||
}
|
||||
|
||||
@Test // DATAJPA-1652
|
||||
@@ -578,17 +531,17 @@ class StringQueryUnitTests {
|
||||
|
||||
DeclaredQuery declaredQuery = DeclaredQuery.of(query, nativeQuery);
|
||||
|
||||
softly.assertThat(declaredQuery.hasNamedParameter()) //
|
||||
assertThat(declaredQuery.hasNamedParameter()) //
|
||||
.describedAs("hasNamed Parameter " + label) //
|
||||
.isEqualTo(expectedSize > 0);
|
||||
softly.assertThat(declaredQuery.getParameterBindings()) //
|
||||
assertThat(declaredQuery.getParameterBindings()) //
|
||||
.describedAs("parameterBindings " + label) //
|
||||
.hasSize(expectedSize);
|
||||
}
|
||||
|
||||
private void checkHasNamedParameter(String query, boolean expected, String label, boolean nativeQuery) {
|
||||
|
||||
softly.assertThat(new StringQuery(query, nativeQuery).hasNamedParameter()) //
|
||||
assertThat(new StringQuery(query, nativeQuery).hasNamedParameter()) //
|
||||
.describedAs(String.format("<%s> (%s)", query, label)) //
|
||||
.isEqualTo(expected);
|
||||
}
|
||||
@@ -596,16 +549,16 @@ class StringQueryUnitTests {
|
||||
private void assertPositionalBinding(Class<? extends ParameterBinding> bindingType, Integer position,
|
||||
ParameterBinding expectedBinding) {
|
||||
|
||||
softly.assertThat(bindingType.isInstance(expectedBinding)).isTrue();
|
||||
softly.assertThat(expectedBinding).isNotNull();
|
||||
softly.assertThat(expectedBinding.hasPosition(position)).isTrue();
|
||||
assertThat(bindingType.isInstance(expectedBinding)).isTrue();
|
||||
assertThat(expectedBinding).isNotNull();
|
||||
assertThat(expectedBinding.hasPosition(position)).isTrue();
|
||||
}
|
||||
|
||||
private void assertNamedBinding(Class<? extends ParameterBinding> bindingType, String parameterName,
|
||||
ParameterBinding expectedBinding) {
|
||||
|
||||
softly.assertThat(bindingType.isInstance(expectedBinding)).isTrue();
|
||||
softly.assertThat(expectedBinding).isNotNull();
|
||||
softly.assertThat(expectedBinding.hasName(parameterName)).isTrue();
|
||||
assertThat(bindingType.isInstance(expectedBinding)).isTrue();
|
||||
assertThat(expectedBinding).isNotNull();
|
||||
assertThat(expectedBinding.hasName(parameterName)).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user