Polishing.

Introduce QueryInformation to provide query introspection results. Refine generics.

See #3726
Original pull request: #3730
This commit is contained in:
Mark Paluch
2025-01-13 15:54:01 +01:00
parent 643a3a95f5
commit f2d1a358fb
16 changed files with 239 additions and 159 deletions

View File

@@ -36,10 +36,9 @@ class EqlCountQueryTransformer extends EqlQueryRenderer {
private final @Nullable String countProjection;
private final @Nullable String primaryFromAlias;
EqlCountQueryTransformer(@Nullable String countProjection, @Nullable String primaryFromAlias) {
EqlCountQueryTransformer(@Nullable String countProjection, QueryInformation queryInformation) {
this.countProjection = countProjection;
this.primaryFromAlias = primaryFromAlias;
this.primaryFromAlias = queryInformation.getAlias();
}
@Override

View File

@@ -15,7 +15,7 @@
*/
package org.springframework.data.jpa.repository.query;
import static org.springframework.data.jpa.repository.query.QueryTokens.TOKEN_COMMA;
import static org.springframework.data.jpa.repository.query.QueryTokens.*;
import java.util.ArrayList;
import java.util.Collections;
@@ -31,7 +31,7 @@ import org.springframework.lang.Nullable;
* @author Christoph Strobl
*/
@SuppressWarnings("UnreachableCode")
class EqlQueryIntrospector extends EqlBaseVisitor<Void> implements ParsedQueryIntrospector {
class EqlQueryIntrospector extends EqlBaseVisitor<Void> implements ParsedQueryIntrospector<QueryInformation> {
private final EqlQueryRenderer renderer = new EqlQueryRenderer();
@@ -41,18 +41,9 @@ class EqlQueryIntrospector extends EqlBaseVisitor<Void> implements ParsedQueryIn
private boolean hasConstructorExpression = false;
@Override
public String getAlias() {
return primaryFromAlias;
}
@Override
public List<QueryToken> getProjection() {
return projection == null ? Collections.emptyList() : projection;
}
@Override
public boolean hasConstructorExpression() {
return hasConstructorExpression;
public QueryInformation getParsedQueryInformation() {
return new QueryInformation(primaryFromAlias, projection == null ? Collections.emptyList() : projection,
hasConstructorExpression);
}
@Override

View File

@@ -43,12 +43,13 @@ class EqlSortedQueryTransformer extends EqlQueryRenderer {
private final @Nullable String primaryFromAlias;
private final @Nullable DtoProjectionTransformerDelegate dtoDelegate;
EqlSortedQueryTransformer(Sort sort, @Nullable String primaryFromAlias, @Nullable ReturnedType returnedType) {
EqlSortedQueryTransformer(Sort sort, QueryInformation queryInformation, @Nullable ReturnedType returnedType) {
Assert.notNull(sort, "Sort must not be null");
Assert.notNull(queryInformation, "ParsedHqlQueryInformation must not be null");
this.sort = sort;
this.primaryFromAlias = primaryFromAlias;
this.primaryFromAlias = queryInformation.getAlias();
this.dtoDelegate = returnedType == null ? null : new DtoProjectionTransformerDelegate(returnedType);
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jpa.repository.query;
import java.util.List;
import org.springframework.lang.Nullable;
/**
* Hibernate-specific query details capturing common table expression details.
*
* @author Mark Paluch
* @since 3.5
*/
class HibernateQueryInformation extends QueryInformation {
private final boolean hasCte;
public HibernateQueryInformation(@Nullable String alias, List<QueryToken> projection,
boolean hasConstructorExpression, boolean hasCte) {
super(alias, projection, hasConstructorExpression);
this.hasCte = hasCte;
}
public boolean hasCte() {
return hasCte;
}
}

View File

@@ -36,11 +36,12 @@ class HqlCountQueryTransformer extends HqlQueryRenderer {
private final @Nullable String countProjection;
private final @Nullable String primaryFromAlias;
private boolean containsCTE = false;
private final boolean containsCTE;
HqlCountQueryTransformer(@Nullable String countProjection, @Nullable String primaryFromAlias) {
HqlCountQueryTransformer(@Nullable String countProjection, HibernateQueryInformation queryInformation) {
this.countProjection = countProjection;
this.primaryFromAlias = primaryFromAlias;
this.primaryFromAlias = queryInformation.getAlias();
this.containsCTE = queryInformation.hasCte();
}
@Override
@@ -67,12 +68,6 @@ class HqlCountQueryTransformer extends HqlQueryRenderer {
return builder;
}
@Override
public QueryTokenStream visitCte(HqlParser.CteContext ctx) {
this.containsCTE = true;
return super.visitCte(ctx);
}
@Override
public QueryRendererBuilder visitFromQuery(HqlParser.FromQueryContext ctx) {

View File

@@ -30,7 +30,7 @@ import org.springframework.lang.Nullable;
* @author Mark Paluch
*/
@SuppressWarnings({ "UnreachableCode", "ConstantValue" })
class HqlQueryIntrospector extends HqlBaseVisitor<Void> implements ParsedQueryIntrospector {
class HqlQueryIntrospector extends HqlBaseVisitor<Void> implements ParsedQueryIntrospector<HibernateQueryInformation> {
private final HqlQueryRenderer renderer = new HqlQueryRenderer();
@@ -38,20 +38,12 @@ class HqlQueryIntrospector extends HqlBaseVisitor<Void> implements ParsedQueryIn
private @Nullable List<QueryToken> projection;
private boolean projectionProcessed;
private boolean hasConstructorExpression = false;
private boolean hasCte = false;
@Override
public String getAlias() {
return primaryFromAlias;
}
@Override
public List<QueryToken> getProjection() {
return projection == null ? Collections.emptyList() : projection;
}
@Override
public boolean hasConstructorExpression() {
return hasConstructorExpression;
public HibernateQueryInformation getParsedQueryInformation() {
return new HibernateQueryInformation(primaryFromAlias, projection == null ? Collections.emptyList() : projection,
hasConstructorExpression, hasCte);
}
@Override
@@ -65,6 +57,12 @@ class HqlQueryIntrospector extends HqlBaseVisitor<Void> implements ParsedQueryIn
return super.visitSelectClause(ctx);
}
@Override
public Void visitCte(HqlParser.CteContext ctx) {
this.hasCte = true;
return super.visitCte(ctx);
}
@Override
public Void visitFromRoot(HqlParser.FromRootContext ctx) {

View File

@@ -41,21 +41,14 @@ class HqlSortedQueryTransformer extends HqlQueryRenderer {
private final @Nullable String primaryFromAlias;
private final @Nullable DtoProjectionTransformerDelegate dtoDelegate;
HqlSortedQueryTransformer(Sort sort, @Nullable String primaryFromAlias) {
HqlSortedQueryTransformer(Sort sort, HibernateQueryInformation queryInformation,
@Nullable ReturnedType returnedType) {
Assert.notNull(sort, "Sort must not be null");
Assert.notNull(queryInformation, "ParsedHqlQueryInformation must not be null");
this.sort = sort;
this.primaryFromAlias = primaryFromAlias;
this.dtoDelegate = null;
}
HqlSortedQueryTransformer(Sort sort, @Nullable String primaryFromAlias, @Nullable ReturnedType returnedType) {
Assert.notNull(sort, "Sort must not be null");
this.sort = sort;
this.primaryFromAlias = primaryFromAlias;
this.primaryFromAlias = queryInformation.getAlias();
this.dtoDelegate = returnedType == null ? null : new DtoProjectionTransformerDelegate(returnedType);
}

View File

@@ -44,25 +44,27 @@ import org.springframework.util.Assert;
* @see HqlQueryParser
* @see EqlQueryParser
*/
class JpaQueryEnhancer implements QueryEnhancer {
@SuppressWarnings("removal")
class JpaQueryEnhancer<Q extends QueryInformation> implements QueryEnhancer {
private final ParserRuleContext context;
private final ParsedQueryIntrospector introspector;
private final Q queryInformation;
private final String projection;
private final SortedQueryRewriteFunction sortFunction;
private final BiFunction<String, String, ParseTreeVisitor<? extends Object>> countQueryFunction;
private final SortedQueryRewriteFunction<Q> sortFunction;
private final BiFunction<String, Q, ParseTreeVisitor<QueryTokenStream>> countQueryFunction;
JpaQueryEnhancer(ParserRuleContext context, ParsedQueryIntrospector introspector,
SortedQueryRewriteFunction sortFunction,
BiFunction<String, String, ParseTreeVisitor<? extends Object>> countQueryFunction) {
JpaQueryEnhancer(ParserRuleContext context, ParsedQueryIntrospector<Q> introspector,
SortedQueryRewriteFunction<Q> sortFunction,
BiFunction<String, Q, ParseTreeVisitor<QueryTokenStream>> countQueryFunction) {
this.context = context;
this.introspector = introspector;
this.sortFunction = sortFunction;
this.countQueryFunction = countQueryFunction;
this.introspector.visit(context);
introspector.visit(context);
List<QueryToken> tokens = introspector.getProjection();
this.queryInformation = introspector.getParsedQueryInformation();
List<QueryToken> tokens = queryInformation.getProjection();
this.projection = tokens.isEmpty() ? "" : new QueryRenderer.TokenRenderer(tokens).render();
}
@@ -86,9 +88,10 @@ class JpaQueryEnhancer implements QueryEnhancer {
/**
* Apply common configuration (SLL prediction for performance, our own error listeners).
*
* @param query
* @param lexer
* @param parser
* @param query the query input to parse.
* @param grammar name of the grammar.
* @param lexer lexer to configure.
* @param parser parser to configure.
*/
static void configureParser(String query, String grammar, Lexer lexer, Parser parser) {
@@ -109,7 +112,7 @@ class JpaQueryEnhancer implements QueryEnhancer {
* @param query must not be {@literal null}.
* @return a new {@link JpaQueryEnhancer} using JPQL.
*/
public static JpaQueryEnhancer forJpql(DeclaredQuery query) {
public static JpaQueryEnhancer<QueryInformation> forJpql(DeclaredQuery query) {
Assert.notNull(query, "DeclaredQuery must not be null!");
@@ -122,7 +125,7 @@ class JpaQueryEnhancer implements QueryEnhancer {
* @param query must not be {@literal null}.
* @return a new {@link JpaQueryEnhancer} using HQL.
*/
public static JpaQueryEnhancer forHql(DeclaredQuery query) {
public static JpaQueryEnhancer<HibernateQueryInformation> forHql(DeclaredQuery query) {
Assert.notNull(query, "DeclaredQuery must not be null!");
@@ -136,17 +139,27 @@ class JpaQueryEnhancer implements QueryEnhancer {
* @return a new {@link JpaQueryEnhancer} using EQL.
* @since 3.2
*/
public static JpaQueryEnhancer forEql(DeclaredQuery query) {
public static JpaQueryEnhancer<QueryInformation> forEql(DeclaredQuery query) {
Assert.notNull(query, "DeclaredQuery must not be null!");
return EqlQueryParser.parseQuery(query.getQueryString());
}
/**
* @return the parser context (AST) representing the parsed query.
*/
ParserRuleContext getContext() {
return context;
}
/**
* @return the parsed query information.
*/
Q getQueryInformation() {
return queryInformation;
}
/**
* Checks if the select clause has a new constructor instantiation in the JPA query.
*
@@ -154,7 +167,7 @@ class JpaQueryEnhancer implements QueryEnhancer {
*/
@Override
public boolean hasConstructorExpression() {
return this.introspector.hasConstructorExpression();
return this.queryInformation.hasConstructorExpression();
}
/**
@@ -163,7 +176,7 @@ class JpaQueryEnhancer implements QueryEnhancer {
*/
@Override
public String detectAlias() {
return this.introspector.getAlias();
return this.queryInformation.getAlias();
}
/**
@@ -176,8 +189,7 @@ class JpaQueryEnhancer implements QueryEnhancer {
}
/**
* Since the {@link JpaQueryParser} can already fully transform sorted and count queries by itself, this is a
* placeholder method.
* Since the parser can already fully transform sorted and count queries by itself, this is a placeholder method.
*
* @return empty set
*/
@@ -202,13 +214,14 @@ class JpaQueryEnhancer implements QueryEnhancer {
*/
@Override
public String applySorting(Sort sort) {
return QueryRenderer.TokenRenderer.render(sortFunction.apply(sort, detectAlias(), null).visit(context));
return QueryRenderer.TokenRenderer.render(sortFunction.apply(sort, this.queryInformation, null).visit(context));
}
@Override
public String rewrite(QueryRewriteInformation rewriteInformation) {
return QueryRenderer.TokenRenderer.render(sortFunction
.apply(rewriteInformation.getSort(), detectAlias(), rewriteInformation.getReturnedType()).visit(context));
return QueryRenderer.TokenRenderer.render(
sortFunction.apply(rewriteInformation.getSort(), this.queryInformation, rewriteInformation.getReturnedType())
.visit(context));
}
/**
@@ -240,7 +253,21 @@ class JpaQueryEnhancer implements QueryEnhancer {
*/
@Override
public String createCountQueryFor(@Nullable String countProjection) {
return QueryRenderer.TokenRenderer.render(countQueryFunction.apply(countProjection, detectAlias()).visit(context));
return QueryRenderer.TokenRenderer
.render(countQueryFunction.apply(countProjection, this.queryInformation).visit(context));
}
/**
* Functional interface to rewrite a query considering {@link Sort} and {@link ReturnedType}. The function returns a
* visitor object that can visit the parsed query tree.
*
* @since 3.5
*/
@FunctionalInterface
interface SortedQueryRewriteFunction<Q> {
ParseTreeVisitor<QueryTokenStream> apply(Sort sort, Q queryInformation, @Nullable ReturnedType returnedType);
}
/**
@@ -251,7 +278,7 @@ class JpaQueryEnhancer implements QueryEnhancer {
* @author Mark Paluch
* @since 3.1
*/
static class HqlQueryParser extends JpaQueryEnhancer {
static class HqlQueryParser extends JpaQueryEnhancer<HibernateQueryInformation> {
private HqlQueryParser(String query) {
super(parse(query, HqlLexer::new, HqlParser::new, HqlParser::start), new HqlQueryIntrospector(),
@@ -261,9 +288,9 @@ class JpaQueryEnhancer implements QueryEnhancer {
/**
* Parse a HQL query.
*
* @param query
* @param query the query to parse.
* @return the query parser.
* @throws BadJpqlGrammarException
* @throws BadJpqlGrammarException in case of malformed query.
*/
public static HqlQueryParser parseQuery(String query) throws BadJpqlGrammarException {
return new HqlQueryParser(query);
@@ -279,7 +306,7 @@ class JpaQueryEnhancer implements QueryEnhancer {
* @author Mark Paluch
* @since 3.2
*/
static class EqlQueryParser extends JpaQueryEnhancer {
static class EqlQueryParser extends JpaQueryEnhancer<QueryInformation> {
private EqlQueryParser(String query) {
super(parse(query, EqlLexer::new, EqlParser::new, EqlParser::start), new EqlQueryIntrospector(),
@@ -289,9 +316,9 @@ class JpaQueryEnhancer implements QueryEnhancer {
/**
* Parse a EQL query.
*
* @param query
* @param query the query to parse.
* @return the query parser.
* @throws BadJpqlGrammarException
* @throws BadJpqlGrammarException in case of malformed query.
*/
public static EqlQueryParser parseQuery(String query) throws BadJpqlGrammarException {
return new EqlQueryParser(query);
@@ -307,7 +334,7 @@ class JpaQueryEnhancer implements QueryEnhancer {
* @author Mark Paluch
* @since 3.1
*/
static class JpqlQueryParser extends JpaQueryEnhancer {
static class JpqlQueryParser extends JpaQueryEnhancer<QueryInformation> {
private JpqlQueryParser(String query) {
super(parse(query, JpqlLexer::new, JpqlParser::new, JpqlParser::start), new JpqlQueryIntrospector(),
@@ -317,25 +344,13 @@ class JpaQueryEnhancer implements QueryEnhancer {
/**
* Parse a JPQL query.
*
* @param query
* @param query the query to parse.
* @return the query parser.
* @throws BadJpqlGrammarException
* @throws BadJpqlGrammarException in case of malformed query.
*/
public static JpqlQueryParser parseQuery(String query) throws BadJpqlGrammarException {
return new JpqlQueryParser(query);
}
}
/**
* Functional interface to rewrite a query considering {@link Sort} and {@link ReturnedType}. The function returns a
* visitor object that can visit the parsed query tree.
*
* @since 3.5
*/
@FunctionalInterface
interface SortedQueryRewriteFunction {
ParseTreeVisitor<? extends Object> apply(Sort sort, String primaryAlias, @Nullable ReturnedType returnedType);
}
}

View File

@@ -36,9 +36,9 @@ class JpqlCountQueryTransformer extends JpqlQueryRenderer {
private final @Nullable String countProjection;
private final @Nullable String primaryFromAlias;
JpqlCountQueryTransformer(@Nullable String countProjection, @Nullable String primaryFromAlias) {
JpqlCountQueryTransformer(@Nullable String countProjection, QueryInformation queryInformation) {
this.countProjection = countProjection;
this.primaryFromAlias = primaryFromAlias;
this.primaryFromAlias = queryInformation.getAlias();
}
@Override

View File

@@ -15,7 +15,7 @@
*/
package org.springframework.data.jpa.repository.query;
import static org.springframework.data.jpa.repository.query.QueryTokens.TOKEN_COMMA;
import static org.springframework.data.jpa.repository.query.QueryTokens.*;
import java.util.ArrayList;
import java.util.Collections;
@@ -30,7 +30,7 @@ import org.springframework.lang.Nullable;
* @author Christoph Strobl
*/
@SuppressWarnings({ "UnreachableCode", "ConstantValue" })
class JpqlQueryIntrospector extends JpqlBaseVisitor<Void> implements ParsedQueryIntrospector {
class JpqlQueryIntrospector extends JpqlBaseVisitor<Void> implements ParsedQueryIntrospector<QueryInformation> {
private final JpqlQueryRenderer renderer = new JpqlQueryRenderer();
@@ -39,17 +39,10 @@ class JpqlQueryIntrospector extends JpqlBaseVisitor<Void> implements ParsedQuery
private boolean projectionProcessed;
private boolean hasConstructorExpression = false;
@Nullable
public String getAlias() {
return primaryFromAlias;
}
public List<QueryToken> getProjection() {
return projection == null ? Collections.emptyList() : projection;
}
public boolean hasConstructorExpression() {
return hasConstructorExpression;
@Override
public QueryInformation getParsedQueryInformation() {
return new QueryInformation(primaryFromAlias, projection == null ? Collections.emptyList() : projection,
hasConstructorExpression);
}
@Override

View File

@@ -41,21 +41,13 @@ class JpqlSortedQueryTransformer extends JpqlQueryRenderer {
private final @Nullable String primaryFromAlias;
private final @Nullable DtoProjectionTransformerDelegate dtoDelegate;
JpqlSortedQueryTransformer(Sort sort, @Nullable String primaryFromAlias) {
JpqlSortedQueryTransformer(Sort sort, QueryInformation queryInformation, @Nullable ReturnedType returnedType) {
Assert.notNull(sort, "Sort must not be null");
Assert.notNull(queryInformation, "ParsedHqlQueryInformation must not be null");
this.sort = sort;
this.primaryFromAlias = primaryFromAlias;
this.dtoDelegate = null;
}
JpqlSortedQueryTransformer(Sort sort, @Nullable String primaryFromAlias, @Nullable ReturnedType returnedType) {
Assert.notNull(sort, "Sort must not be null");
this.sort = sort;
this.primaryFromAlias = primaryFromAlias;
this.primaryFromAlias = queryInformation.getAlias();
this.dtoDelegate = returnedType == null ? null : new DtoProjectionTransformerDelegate(returnedType);
}

View File

@@ -15,19 +15,16 @@
*/
package org.springframework.data.jpa.repository.query;
import java.util.List;
import org.antlr.v4.runtime.tree.ParseTree;
import org.springframework.lang.Nullable;
/**
* Interface defining an introspector for String-queries providing details about the primary table alias, the primary
* selection projection and whether the query makes use of constructor expressions.
*
* @author Mark Paluch
* @since 3.5
*/
interface ParsedQueryIntrospector {
interface ParsedQueryIntrospector<I extends QueryInformation> {
/**
* Visit the parsed tree to introspect the AST tree.
@@ -38,20 +35,10 @@ interface ParsedQueryIntrospector {
Void visit(ParseTree tree);
/**
* Primary table alias. Contains the first table name/table alias in case multiple tables are specified in the query.
* Returns the parsed query information. The information is available after the {@link #visit(ParseTree)
* introspection} has been completed.
*
* @return
* @return parsed query information.
*/
@Nullable
String getAlias();
/**
* @return the primary selection.
*/
List<QueryToken> getProjection();
/**
* @return {@code true} if the query uses a constructor expression.
*/
boolean hasConstructorExpression();
I getParsedQueryInformation();
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jpa.repository.query;
import java.util.List;
import org.springframework.lang.Nullable;
/**
* Value object capturing introspection details of a parsed query.
*
* @author Mark Paluch
* @since 3.5
*/
class QueryInformation {
private final @Nullable String alias;
private final List<QueryToken> projection;
private final boolean hasConstructorExpression;
QueryInformation(@Nullable String alias, List<QueryToken> projection, boolean hasConstructorExpression) {
this.alias = alias;
this.projection = projection;
this.hasConstructorExpression = hasConstructorExpression;
}
/**
* Primary table alias. Contains the first table name/table alias in case multiple tables are specified in the query.
*
* @return
*/
@Nullable
public String getAlias() {
return alias;
}
/**
* @return the primary selection.
*/
public List<QueryToken> getProjection() {
return projection;
}
/**
* @return {@code true} if the query uses a constructor expression.
*/
public boolean hasConstructorExpression() {
return hasConstructorExpression;
}
}

View File

@@ -35,15 +35,13 @@ import org.springframework.data.repository.core.support.DefaultRepositoryMetadat
class EqlDtoQueryTransformerUnitTests {
JpaQueryMethod method = getMethod("dtoProjection");
EqlSortedQueryTransformer transformer = new EqlSortedQueryTransformer(Sort.unsorted(), null,
method.getResultProcessor().getReturnedType());
@Test // GH-3076
void shouldTranslateSingleProjectionToDto() {
JpaQueryEnhancer.EqlQueryParser parser = JpaQueryEnhancer.EqlQueryParser.parseQuery("SELECT p from Person p");
QueryTokenStream visit = transformer.visit(parser.getContext());
QueryTokenStream visit = getTransformer(parser).visit(parser.getContext());
assertThat(QueryRenderer.TokenRenderer.render(visit)).isEqualTo(
"SELECT new org.springframework.data.jpa.repository.query.EqlDtoQueryTransformerUnitTests$MyRecord(p.foo, p.bar) from Person p");
@@ -55,7 +53,7 @@ class EqlDtoQueryTransformerUnitTests {
JpaQueryEnhancer.EqlQueryParser parser = JpaQueryEnhancer.EqlQueryParser
.parseQuery("select u from User u left outer join u.roles r where r in (select r from Role r)");
QueryTokenStream visit = transformer.visit(parser.getContext());
QueryTokenStream visit = getTransformer(parser).visit(parser.getContext());
assertThat(QueryRenderer.TokenRenderer.render(visit)).isEqualTo(
"select new org.springframework.data.jpa.repository.query.EqlDtoQueryTransformerUnitTests$MyRecord(u.foo, u.bar) from User u left outer join u.roles r where r in (select r from Role r)");
@@ -67,7 +65,7 @@ class EqlDtoQueryTransformerUnitTests {
JpaQueryEnhancer.EqlQueryParser parser = JpaQueryEnhancer.EqlQueryParser
.parseQuery("SELECT NEW String(p) from Person p");
QueryTokenStream visit = transformer.visit(parser.getContext());
QueryTokenStream visit = getTransformer(parser).visit(parser.getContext());
assertThat(QueryRenderer.TokenRenderer.render(visit)).isEqualTo("SELECT NEW String(p) from Person p");
}
@@ -78,6 +76,7 @@ class EqlDtoQueryTransformerUnitTests {
JpaQueryEnhancer.EqlQueryParser parser = JpaQueryEnhancer.EqlQueryParser
.parseQuery("SELECT p.foo, p.bar, sum(p.age) from Person p");
EqlSortedQueryTransformer transformer = getTransformer(parser);
QueryTokenStream visit = transformer.visit(parser.getContext());
assertThat(QueryRenderer.TokenRenderer.render(visit)).isEqualTo(
@@ -97,6 +96,11 @@ class EqlDtoQueryTransformerUnitTests {
}
}
private EqlSortedQueryTransformer getTransformer(JpaQueryEnhancer.EqlQueryParser parser) {
return new EqlSortedQueryTransformer(Sort.unsorted(), parser.getQueryInformation(),
method.getResultProcessor().getReturnedType());
}
interface MyRepo extends Repository<Person, String> {
MyRecord dtoProjection();

View File

@@ -35,15 +35,13 @@ import org.springframework.data.repository.core.support.DefaultRepositoryMetadat
class HqlDtoQueryTransformerUnitTests {
JpaQueryMethod method = getMethod("dtoProjection");
HqlSortedQueryTransformer transformer = new HqlSortedQueryTransformer(Sort.unsorted(), null,
method.getResultProcessor().getReturnedType());
@Test // GH-3076
void shouldTranslateSingleProjectionToDto() {
JpaQueryEnhancer.HqlQueryParser parser = JpaQueryEnhancer.HqlQueryParser.parseQuery("SELECT p from Person p");
QueryTokenStream visit = transformer.visit(parser.getContext());
QueryTokenStream visit = getTransformer(parser).visit(parser.getContext());
assertThat(QueryRenderer.TokenRenderer.render(visit)).isEqualTo(
"SELECT new org.springframework.data.jpa.repository.query.HqlDtoQueryTransformerUnitTests$MyRecord(p.foo, p.bar) from Person p");
@@ -55,7 +53,7 @@ class HqlDtoQueryTransformerUnitTests {
JpaQueryEnhancer.HqlQueryParser parser = JpaQueryEnhancer.HqlQueryParser
.parseQuery("select u from User u left outer join u.roles r where r in (select r from Role r)");
QueryTokenStream visit = transformer.visit(parser.getContext());
QueryTokenStream visit = getTransformer(parser).visit(parser.getContext());
assertThat(QueryRenderer.TokenRenderer.render(visit)).isEqualTo(
"select new org.springframework.data.jpa.repository.query.HqlDtoQueryTransformerUnitTests$MyRecord(u.foo, u.bar) from User u left outer join u.roles r where r in (select r from Role r)");
@@ -67,7 +65,7 @@ class HqlDtoQueryTransformerUnitTests {
JpaQueryEnhancer.HqlQueryParser parser = JpaQueryEnhancer.HqlQueryParser
.parseQuery("SELECT NEW String(p) from Person p");
QueryTokenStream visit = transformer.visit(parser.getContext());
QueryTokenStream visit = getTransformer(parser).visit(parser.getContext());
assertThat(QueryRenderer.TokenRenderer.render(visit)).isEqualTo("SELECT NEW String(p) from Person p");
}
@@ -78,7 +76,7 @@ class HqlDtoQueryTransformerUnitTests {
JpaQueryEnhancer.HqlQueryParser parser = JpaQueryEnhancer.HqlQueryParser
.parseQuery("SELECT p.foo, p.bar, sum(p.age) from Person p");
QueryTokenStream visit = transformer.visit(parser.getContext());
QueryTokenStream visit = getTransformer(parser).visit(parser.getContext());
assertThat(QueryRenderer.TokenRenderer.render(visit)).isEqualTo(
"SELECT new org.springframework.data.jpa.repository.query.HqlDtoQueryTransformerUnitTests$MyRecord(p.foo, p.bar, sum(p.age)) from Person p");
@@ -97,6 +95,11 @@ class HqlDtoQueryTransformerUnitTests {
}
}
private HqlSortedQueryTransformer getTransformer(JpaQueryEnhancer.HqlQueryParser parser) {
return new HqlSortedQueryTransformer(Sort.unsorted(), parser.getQueryInformation(),
method.getResultProcessor().getReturnedType());
}
interface MyRepo extends Repository<Person, String> {
MyRecord dtoProjection();

View File

@@ -35,15 +35,13 @@ import org.springframework.data.repository.core.support.DefaultRepositoryMetadat
class JpqlDtoQueryTransformerUnitTests {
JpaQueryMethod method = getMethod("dtoProjection");
JpqlSortedQueryTransformer transformer = new JpqlSortedQueryTransformer(Sort.unsorted(), null,
method.getResultProcessor().getReturnedType());
@Test // GH-3076
void shouldTranslateSingleProjectionToDto() {
JpaQueryEnhancer.JpqlQueryParser parser = JpaQueryEnhancer.JpqlQueryParser.parseQuery("SELECT p from Person p");
QueryTokenStream visit = transformer.visit(parser.getContext());
QueryTokenStream visit = getTransformer(parser).visit(parser.getContext());
assertThat(QueryRenderer.TokenRenderer.render(visit)).isEqualTo(
"SELECT new org.springframework.data.jpa.repository.query.JpqlDtoQueryTransformerUnitTests$MyRecord(p.foo, p.bar) from Person p");
@@ -55,7 +53,7 @@ class JpqlDtoQueryTransformerUnitTests {
JpaQueryEnhancer.JpqlQueryParser parser = JpaQueryEnhancer.JpqlQueryParser
.parseQuery("select u from User u left outer join u.roles r where r in (select r from Role r)");
QueryTokenStream visit = transformer.visit(parser.getContext());
QueryTokenStream visit = getTransformer(parser).visit(parser.getContext());
assertThat(QueryRenderer.TokenRenderer.render(visit)).isEqualTo(
"select new org.springframework.data.jpa.repository.query.JpqlDtoQueryTransformerUnitTests$MyRecord(u.foo, u.bar) from User u left outer join u.roles r where r in (select r from Role r)");
@@ -67,7 +65,7 @@ class JpqlDtoQueryTransformerUnitTests {
JpaQueryEnhancer.JpqlQueryParser parser = JpaQueryEnhancer.JpqlQueryParser
.parseQuery("SELECT NEW String(p) from Person p");
QueryTokenStream visit = transformer.visit(parser.getContext());
QueryTokenStream visit = getTransformer(parser).visit(parser.getContext());
assertThat(QueryRenderer.TokenRenderer.render(visit)).isEqualTo("SELECT NEW String(p) from Person p");
}
@@ -78,7 +76,7 @@ class JpqlDtoQueryTransformerUnitTests {
JpaQueryEnhancer.JpqlQueryParser parser = JpaQueryEnhancer.JpqlQueryParser
.parseQuery("SELECT p.foo, p.bar, sum(p.age) from Person p");
QueryTokenStream visit = transformer.visit(parser.getContext());
QueryTokenStream visit = getTransformer(parser).visit(parser.getContext());
assertThat(QueryRenderer.TokenRenderer.render(visit)).isEqualTo(
"SELECT new org.springframework.data.jpa.repository.query.JpqlDtoQueryTransformerUnitTests$MyRecord(p.foo, p.bar, sum(p.age)) from Person p");
@@ -97,6 +95,11 @@ class JpqlDtoQueryTransformerUnitTests {
}
}
private JpqlSortedQueryTransformer getTransformer(JpaQueryEnhancer.JpqlQueryParser parser) {
return new JpqlSortedQueryTransformer(Sort.unsorted(), parser.getQueryInformation(),
method.getResultProcessor().getReturnedType());
}
interface MyRepo extends Repository<Person, String> {
MyRecord dtoProjection();