diff --git a/spring-data-jpa-performance/src/main/java/org/springframework/data/jpa/repository/PersonRepository.java b/spring-data-jpa-performance/src/main/java/org/springframework/data/jpa/repository/PersonRepository.java index ace25ba62..4c1352006 100644 --- a/spring-data-jpa-performance/src/main/java/org/springframework/data/jpa/repository/PersonRepository.java +++ b/spring-data-jpa-performance/src/main/java/org/springframework/data/jpa/repository/PersonRepository.java @@ -39,4 +39,9 @@ public interface PersonRepository extends ListCrudRepository { @Query(value = "SELECT * FROM person WHERE firstname = ?1", nativeQuery = true) List findAllWithNativeQueryByFirstname(String firstname); + + Long countByFirstname(String firstname); + + @Query("SELECT COUNT(*) FROM org.springframework.data.jpa.model.Person p WHERE p.firstname = ?1") + Long countWithAnnotatedQueryByFirstname(String firstname); } diff --git a/spring-data-jpa-performance/src/test/java/org/springframework/data/jpa/repository/RepositoryFinderTests.java b/spring-data-jpa-performance/src/test/java/org/springframework/data/jpa/repository/RepositoryFinderTests.java index 322a34dfe..0fc408151 100644 --- a/spring-data-jpa-performance/src/test/java/org/springframework/data/jpa/repository/RepositoryFinderTests.java +++ b/spring-data-jpa-performance/src/test/java/org/springframework/data/jpa/repository/RepositoryFinderTests.java @@ -58,6 +58,9 @@ import org.springframework.util.ObjectUtils; @Timeout(time = 2) public class RepositoryFinderTests { + private static final String PERSON_FIRSTNAME = "first"; + private static final String COLUMN_PERSON_FIRSTNAME = "firstname"; + @State(Scope.Benchmark) public static class BenchmarkParameters { @@ -83,7 +86,7 @@ public class RepositoryFinderTests { entityManager.persist(generalProfile); entityManager.persist(sdUserProfile); - Person person = new Person("first", "last"); + Person person = new Person(PERSON_FIRSTNAME, "last"); person.setProfiles(Set.of(generalProfile, sdUserProfile)); entityManager.persist(person); entityManager.getTransaction().commit(); @@ -128,7 +131,7 @@ public class RepositoryFinderTests { CriteriaQuery query = criteriaBuilder.createQuery(Person.class); Root root = query.from(Person.class); TypedQuery typedQuery = parameters.entityManager - .createQuery(query.where(criteriaBuilder.equal(root.get("firstname"), "first"))); + .createQuery(query.where(criteriaBuilder.equal(root.get(COLUMN_PERSON_FIRSTNAME), PERSON_FIRSTNAME))); return typedQuery.getResultList(); } @@ -138,35 +141,52 @@ public class RepositoryFinderTests { Query query = parameters.entityManager .createQuery("SELECT p FROM org.springframework.data.jpa.model.Person p WHERE p.firstname = ?1"); - query.setParameter(1, "first"); + query.setParameter(1, PERSON_FIRSTNAME); return query.getResultList(); } + @Benchmark + public Long baselineEntityManagerCount(BenchmarkParameters parameters) { + + Query query = parameters.entityManager.createQuery("SELECT COUNT(*) FROM org.springframework.data.jpa.model.Person p WHERE p.firstname = ?1"); + query.setParameter(1, PERSON_FIRSTNAME); + + return (Long) query.getSingleResult(); + } + @Benchmark public List derivedFinderMethod(BenchmarkParameters parameters) { - return parameters.repositoryProxy.findAllByFirstname("first"); + return parameters.repositoryProxy.findAllByFirstname(PERSON_FIRSTNAME); } @Benchmark public List derivedFinderMethodWithInterfaceProjection(BenchmarkParameters parameters) { - return parameters.repositoryProxy.findAllAndProjectToInterfaceByFirstname("first"); + return parameters.repositoryProxy.findAllAndProjectToInterfaceByFirstname(PERSON_FIRSTNAME); } - @Benchmark public List stringBasedQuery(BenchmarkParameters parameters) { - return parameters.repositoryProxy.findAllWithAnnotatedQueryByFirstname("first"); + return parameters.repositoryProxy.findAllWithAnnotatedQueryByFirstname(PERSON_FIRSTNAME); } @Benchmark public List stringBasedQueryDynamicSort(BenchmarkParameters parameters) { - return parameters.repositoryProxy.findAllWithAnnotatedQueryByFirstname("first", Sort.by("firstname")); + return parameters.repositoryProxy.findAllWithAnnotatedQueryByFirstname(PERSON_FIRSTNAME, Sort.by(COLUMN_PERSON_FIRSTNAME)); } @Benchmark public List stringBasedNativeQuery(BenchmarkParameters parameters) { - return parameters.repositoryProxy.findAllWithNativeQueryByFirstname("first"); + return parameters.repositoryProxy.findAllWithNativeQueryByFirstname(PERSON_FIRSTNAME); } + @Benchmark + public Long derivedCount(BenchmarkParameters parameters) { + return parameters.repositoryProxy.countByFirstname(PERSON_FIRSTNAME); + } + + @Benchmark + public Long stringBasedCount(BenchmarkParameters parameters) { + return parameters.repositoryProxy.countWithAnnotatedQueryByFirstname(PERSON_FIRSTNAME); + } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlCountQueryTransformer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlCountQueryTransformer.java index 743f30347..2f14e10f7 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlCountQueryTransformer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlCountQueryTransformer.java @@ -17,9 +17,8 @@ package org.springframework.data.jpa.repository.query; import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken.*; -import java.util.List; - import org.springframework.data.jpa.repository.query.QueryRenderer.QueryRendererBuilder; +import org.springframework.data.jpa.repository.query.QueryTransformers.CountSelectionTokenStream; import org.springframework.lang.Nullable; /** @@ -87,10 +86,10 @@ class EqlCountQueryTransformer extends EqlQueryRenderer { QueryRendererBuilder selectionListbuilder = QueryRendererBuilder.concat(ctx.select_item(), this::visit, TOKEN_COMMA); - List countSelection = QueryTransformers - .filterCountSelection(selectionListbuilder.build().stream().toList()); + CountSelectionTokenStream countSelection = QueryTransformers + .filterCountSelection(selectionListbuilder); - if (countSelection.stream().anyMatch(eqlToken -> eqlToken.getToken().contains("new"))) { + if (countSelection.requiresPrimaryAlias()) { // constructor nested.append(new JpaQueryParsingToken(primaryFromAlias)); } else { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlSortedQueryTransformer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlSortedQueryTransformer.java index 5c954b5e7..cb50f8b83 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlSortedQueryTransformer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlSortedQueryTransformer.java @@ -111,8 +111,7 @@ class EqlSortedQueryTransformer extends EqlQueryRenderer { QueryRendererBuilder builder = super.visitSelect_item(ctx); if (ctx.result_variable() != null) { - List tokens = builder.build().stream().toList(); - transformerSupport.registerAlias(tokens.get(tokens.size() - 1).getToken()); + transformerSupport.registerAlias(builder.lastToken()); } return builder; @@ -122,9 +121,7 @@ class EqlSortedQueryTransformer extends EqlQueryRenderer { public QueryRendererBuilder visitJoin(EqlParser.JoinContext ctx) { QueryRendererBuilder builder = super.visitJoin(ctx); - - List tokens = builder.build().stream().toList(); - transformerSupport.registerAlias(tokens.get(tokens.size() - 1).getToken()); + transformerSupport.registerAlias(builder.lastToken()); return builder; } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlCountQueryTransformer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlCountQueryTransformer.java index 78884bb48..7582a1346 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlCountQueryTransformer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlCountQueryTransformer.java @@ -17,9 +17,8 @@ package org.springframework.data.jpa.repository.query; import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken.*; -import java.util.List; - import org.springframework.data.jpa.repository.query.QueryRenderer.QueryRendererBuilder; +import org.springframework.data.jpa.repository.query.QueryTransformers.CountSelectionTokenStream; import org.springframework.lang.Nullable; /** @@ -196,10 +195,10 @@ class HqlCountQueryTransformer extends HqlQueryRenderer { if (ctx.DISTINCT() != null) { - List countSelection = QueryTransformers - .filterCountSelection(selectionListbuilder.build().stream().toList()); + CountSelectionTokenStream countSelection = QueryTransformers + .filterCountSelection(selectionListbuilder); - if (countSelection.stream().anyMatch(hqlToken -> hqlToken.getToken().contains("new"))) { + if (countSelection.requiresPrimaryAlias()) { // constructor nested.append(new JpaQueryParsingToken(primaryFromAlias)); } else { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java index 0ecbaf61a..e27284345 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java @@ -2291,21 +2291,21 @@ class HqlQueryRenderer extends HqlBaseVisitor { if (ctx.IS() != null) { - builder.append(JpaQueryExpression.expression(ctx.IS())); + builder.append(JpaExpressionToken.expression(ctx.IS())); if (ctx.NOT() != null) { builder.append(JpaQueryParsingToken.expression(ctx.NOT())); } - builder.append(JpaQueryExpression.expression(ctx.EMPTY())); + builder.append(JpaExpressionToken.expression(ctx.EMPTY())); } else if (ctx.MEMBER() != null) { if (ctx.NOT() != null) { builder.append(JpaQueryParsingToken.expression(ctx.NOT())); } - builder.append(JpaQueryExpression.expression(ctx.MEMBER())); - builder.append(JpaQueryExpression.expression(ctx.OF())); + builder.append(JpaExpressionToken.expression(ctx.MEMBER())); + builder.append(JpaExpressionToken.expression(ctx.OF())); builder.append(visit(ctx.path())); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlSortedQueryTransformer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlSortedQueryTransformer.java index 458887fc7..42a7f43d4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlSortedQueryTransformer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlSortedQueryTransformer.java @@ -101,8 +101,7 @@ class HqlSortedQueryTransformer extends HqlQueryRenderer { QueryRendererBuilder builder = super.visitJoinPath(ctx); if (ctx.variable() != null) { - List tokens = builder.build().stream().toList(); - transformerSupport.registerAlias(tokens.get(tokens.size() - 1).getToken()); + transformerSupport.registerAlias(builder.lastToken()); } return builder; @@ -114,8 +113,7 @@ class HqlSortedQueryTransformer extends HqlQueryRenderer { QueryRendererBuilder builder = super.visitJoinSubquery(ctx); if (ctx.variable() != null) { - List tokens = builder.build().stream().toList(); - transformerSupport.registerAlias(tokens.get(tokens.size() - 1).getToken()); + transformerSupport.registerAlias(builder.lastToken()); } return builder; @@ -127,8 +125,7 @@ class HqlSortedQueryTransformer extends HqlQueryRenderer { QueryRendererBuilder builder = super.visitVariable(ctx); if (ctx.identifier() != null) { - List tokens = builder.build().stream().toList(); - transformerSupport.registerAlias(tokens.get(tokens.size() - 1).getToken()); + transformerSupport.registerAlias(builder.lastToken()); } return builder; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryEnhancer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryEnhancer.java index 321246775..2aaea143c 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryEnhancer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryEnhancer.java @@ -15,8 +15,6 @@ */ package org.springframework.data.jpa.repository.query; -import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken.*; - import java.util.List; import java.util.Set; import java.util.function.BiFunction; @@ -31,7 +29,6 @@ import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.TokenStream; import org.antlr.v4.runtime.atn.PredictionMode; import org.antlr.v4.runtime.tree.ParseTreeVisitor; - import org.springframework.data.domain.Sort; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -65,7 +62,7 @@ class JpaQueryEnhancer implements QueryEnhancer { this.introspector.visit(context); List tokens = introspector.getProjection(); - this.projection = tokens.isEmpty() ? "" : render(tokens); + this.projection = tokens.isEmpty() ? "" : QueryRenderer.TokenRenderer.render(tokens); } static

ParserRuleContext parse(String query, Function lexerFactoryFunction, @@ -194,7 +191,7 @@ class JpaQueryEnhancer implements QueryEnhancer { */ @Override public String applySorting(Sort sort) { - return render(sortFunction.apply(sort, detectAlias()).visit(context)); + return QueryRenderer.TokenRenderer.render(sortFunction.apply(sort, detectAlias()).visit(context)); } /** @@ -226,7 +223,7 @@ class JpaQueryEnhancer implements QueryEnhancer { */ @Override public String createCountQueryFor(@Nullable String countProjection) { - return render(countQueryFunction.apply(countProjection, detectAlias()).visit(context)); + return QueryRenderer.TokenRenderer.render(countQueryFunction.apply(countProjection, detectAlias()).visit(context)); } /** diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingToken.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingToken.java index c8d71e507..1c7ef0651 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingToken.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingToken.java @@ -15,7 +15,6 @@ */ package org.springframework.data.jpa.repository.query; -import java.util.Collection; import java.util.function.Supplier; import org.antlr.v4.runtime.Token; @@ -26,41 +25,42 @@ import org.antlr.v4.runtime.tree.TerminalNode; * in the parsing process, so the text itself is wrapped in a {@link Supplier}. * * @author Greg Turnquist + * @author Christoph Strobl * @since 3.1 */ -class JpaQueryParsingToken { +class JpaQueryParsingToken implements QueryToken { /** * Commonly use tokens. */ - public static final JpaQueryParsingToken TOKEN_NONE = JpaQueryParsingToken.token(""); - public static final JpaQueryParsingToken TOKEN_COMMA = JpaQueryParsingToken.token(", "); - public static final JpaQueryParsingToken TOKEN_SPACE = JpaQueryParsingToken.token(" "); - public static final JpaQueryParsingToken TOKEN_DOT = JpaQueryParsingToken.token("."); - public static final JpaQueryParsingToken TOKEN_EQUALS = JpaQueryParsingToken.token(" = "); - public static final JpaQueryParsingToken TOKEN_OPEN_PAREN = JpaQueryParsingToken.token("("); - public static final JpaQueryParsingToken TOKEN_CLOSE_PAREN = JpaQueryParsingToken.token(")"); - public static final JpaQueryParsingToken TOKEN_ORDER_BY = JpaQueryParsingToken.expression("order by"); - public static final JpaQueryParsingToken TOKEN_LOWER_FUNC = new JpaQueryParsingToken("lower("); - public static final JpaQueryParsingToken TOKEN_SELECT_COUNT = JpaQueryParsingToken.token("select count("); - public static final JpaQueryParsingToken TOKEN_COUNT_FUNC = JpaQueryParsingToken.token("count("); - public static final JpaQueryParsingToken TOKEN_DOUBLE_PIPE = JpaQueryParsingToken.token(" || "); - public static final JpaQueryParsingToken TOKEN_OPEN_SQUARE_BRACKET = JpaQueryParsingToken.token("["); - public static final JpaQueryParsingToken TOKEN_CLOSE_SQUARE_BRACKET = new JpaQueryParsingToken("]"); - public static final JpaQueryParsingToken TOKEN_COLON = JpaQueryParsingToken.token(":"); - public static final JpaQueryParsingToken TOKEN_QUESTION_MARK = JpaQueryParsingToken.token("?"); - public static final JpaQueryParsingToken TOKEN_OPEN_BRACE = JpaQueryParsingToken.token("{"); - public static final JpaQueryParsingToken TOKEN_CLOSE_BRACE = new JpaQueryParsingToken("}"); - public static final JpaQueryParsingToken TOKEN_DOUBLE_UNDERSCORE = JpaQueryParsingToken.token("__"); - public static final JpaQueryParsingToken TOKEN_AS = JpaQueryParsingToken.expression("AS"); - public static final JpaQueryParsingToken TOKEN_DESC = JpaQueryParsingToken.expression("desc"); - public static final JpaQueryParsingToken TOKEN_ASC = JpaQueryParsingToken.expression("asc"); - public static final JpaQueryParsingToken TOKEN_WITH = JpaQueryParsingToken.expression("WITH"); - public static final JpaQueryParsingToken TOKEN_NOT = JpaQueryParsingToken.expression("NOT"); - public static final JpaQueryParsingToken TOKEN_MATERIALIZED = JpaQueryParsingToken.expression("materialized"); - public static final JpaQueryParsingToken TOKEN_NULLS = JpaQueryParsingToken.expression("NULLS"); - public static final JpaQueryParsingToken TOKEN_FIRST = JpaQueryParsingToken.expression("FIRST"); - public static final JpaQueryParsingToken TOKEN_LAST = JpaQueryParsingToken.expression("LAST"); + static final JpaQueryParsingToken TOKEN_NONE = JpaQueryParsingToken.token(""); + static final JpaQueryParsingToken TOKEN_COMMA = JpaQueryParsingToken.token(", "); + static final JpaQueryParsingToken TOKEN_SPACE = JpaQueryParsingToken.token(" "); + static final JpaQueryParsingToken TOKEN_DOT = JpaQueryParsingToken.token("."); + static final JpaQueryParsingToken TOKEN_EQUALS = JpaQueryParsingToken.token(" = "); + static final JpaQueryParsingToken TOKEN_OPEN_PAREN = JpaQueryParsingToken.token("("); + static final JpaQueryParsingToken TOKEN_CLOSE_PAREN = JpaQueryParsingToken.token(")"); + static final JpaQueryParsingToken TOKEN_ORDER_BY = JpaQueryParsingToken.expression("order by"); + static final JpaQueryParsingToken TOKEN_LOWER_FUNC = JpaQueryParsingToken.token("lower("); + static final JpaQueryParsingToken TOKEN_SELECT_COUNT = JpaQueryParsingToken.token("select count("); + static final JpaQueryParsingToken TOKEN_COUNT_FUNC = JpaQueryParsingToken.token("count("); + static final JpaQueryParsingToken TOKEN_DOUBLE_PIPE = JpaQueryParsingToken.token(" || "); + static final JpaQueryParsingToken TOKEN_OPEN_SQUARE_BRACKET = JpaQueryParsingToken.token("["); + static final JpaQueryParsingToken TOKEN_CLOSE_SQUARE_BRACKET = JpaQueryParsingToken.token("]"); + static final JpaQueryParsingToken TOKEN_COLON = JpaQueryParsingToken.token(":"); + static final JpaQueryParsingToken TOKEN_QUESTION_MARK = JpaQueryParsingToken.token("?"); + static final JpaQueryParsingToken TOKEN_OPEN_BRACE = JpaQueryParsingToken.token("{"); + static final JpaQueryParsingToken TOKEN_CLOSE_BRACE = JpaQueryParsingToken.token("}"); + static final JpaQueryParsingToken TOKEN_DOUBLE_UNDERSCORE = JpaQueryParsingToken.token("__"); + static final JpaQueryParsingToken TOKEN_AS = JpaQueryParsingToken.expression("AS"); + static final JpaQueryParsingToken TOKEN_DESC = JpaQueryParsingToken.expression("desc"); + static final JpaQueryParsingToken TOKEN_ASC = JpaQueryParsingToken.expression("asc"); + static final JpaQueryParsingToken TOKEN_WITH = JpaQueryParsingToken.expression("WITH"); + static final JpaQueryParsingToken TOKEN_NOT = JpaQueryParsingToken.expression("NOT"); + static final JpaQueryParsingToken TOKEN_MATERIALIZED = JpaQueryParsingToken.expression("materialized"); + static final JpaQueryParsingToken TOKEN_NULLS = JpaQueryParsingToken.expression("NULLS"); + static final JpaQueryParsingToken TOKEN_FIRST = JpaQueryParsingToken.expression("FIRST"); + static final JpaQueryParsingToken TOKEN_LAST = JpaQueryParsingToken.expression("LAST"); /** * The text value of the token. @@ -71,11 +71,11 @@ class JpaQueryParsingToken { this.token = token; } - public static JpaQueryParsingToken token(TerminalNode node) { + static JpaQueryParsingToken token(TerminalNode node) { return token(node.getText()); } - public static JpaQueryParsingToken token(Token token) { + static JpaQueryParsingToken token(Token token) { return token(token.getText()); } @@ -84,22 +84,26 @@ class JpaQueryParsingToken { } static JpaQueryParsingToken expression(String expression) { - return new JpaQueryExpression(expression); + return new JpaExpressionToken(expression); } - public static JpaQueryParsingToken expression(Token token) { + static JpaQueryParsingToken expression(Token token) { return expression(token.getText()); } - public static JpaQueryParsingToken expression(TerminalNode node) { + static JpaQueryParsingToken expression(TerminalNode node) { return expression(node.getText()); } - public static JpaQueryParsingToken ventilated(Token op) { + static JpaQueryParsingToken ventilated(Token op) { return new JpaQueryParsingToken(" " + op.getText() + " "); } String getToken() { + return value(); + } + + public String value() { return token; } @@ -109,8 +113,8 @@ class JpaQueryParsingToken { * @param token must not be {@literal null}. * @return {@literal true} if both tokens are equals (using case-insensitive comparison). */ - boolean isA(JpaQueryParsingToken token) { - return token.getToken().equalsIgnoreCase(this.getToken()); + public boolean isA(QueryToken token) { + return token.value().equalsIgnoreCase(this.value()); } @Override @@ -118,52 +122,14 @@ class JpaQueryParsingToken { return getToken(); } - /** - * Render a list of {@link JpaQueryParsingToken}s into a string. - * - * @param tokens - * @return rendered string containing either a query or some subset of that query - */ - static String render(Object tokens) { + static class JpaExpressionToken extends JpaQueryParsingToken { - if (tokens instanceof Collection tpr) { - return render(tpr); - } - - return ((QueryRenderer.QueryRendererBuilder) tokens).build().render(); - } - - /** - * Render a list of {@link JpaQueryParsingToken}s into a string. - * - * @param tokens - * @return rendered string containing either a query or some subset of that query - */ - static String render(Collection tokens) { - - StringBuilder results = new StringBuilder(); - - boolean previousExpression = false; - - for (JpaQueryParsingToken jpaQueryParsingToken : tokens) { - - if (previousExpression) { - if (!results.isEmpty() && results.charAt(results.length() - 1) != ' ') { - results.append(' '); - } - } - - previousExpression = jpaQueryParsingToken instanceof JpaQueryExpression; - results.append(jpaQueryParsingToken.getToken()); - } - - return results.toString(); - } - - static class JpaQueryExpression extends JpaQueryParsingToken { - - JpaQueryExpression(String token) { + JpaExpressionToken(String token) { super(token); } + + public boolean isExpression() { + return true; + } } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryTransformerSupport.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryTransformerSupport.java index 215426616..3f8737424 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryTransformerSupport.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryTransformerSupport.java @@ -41,6 +41,10 @@ class JpaQueryTransformerSupport { projectionAliases.add(token); } + void registerAlias(QueryToken token) { + projectionAliases.add(token.value()); + } + /** * Using the primary {@literal FROM} clause's alias and a {@link Sort}, construct all the {@literal ORDER BY} * arguments. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlCountQueryTransformer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlCountQueryTransformer.java index c2898686c..392c2fd5f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlCountQueryTransformer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlCountQueryTransformer.java @@ -17,9 +17,8 @@ package org.springframework.data.jpa.repository.query; import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken.*; -import java.util.List; - import org.springframework.data.jpa.repository.query.QueryRenderer.QueryRendererBuilder; +import org.springframework.data.jpa.repository.query.QueryTransformers.CountSelectionTokenStream; import org.springframework.lang.Nullable; /** @@ -87,10 +86,10 @@ class JpqlCountQueryTransformer extends JpqlQueryRenderer { QueryRendererBuilder selectionListbuilder = QueryRendererBuilder.concat(ctx.select_item(), this::visit, TOKEN_COMMA); - List countSelection = QueryTransformers - .filterCountSelection(selectionListbuilder.build().stream().toList()); + CountSelectionTokenStream countSelection = QueryTransformers + .filterCountSelection(selectionListbuilder); - if (countSelection.stream().anyMatch(jpqlToken -> jpqlToken.getToken().contains("new"))) { + if (countSelection.requiresPrimaryAlias()) { // constructor nested.append(new JpaQueryParsingToken(primaryFromAlias)); } else { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlSortedQueryTransformer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlSortedQueryTransformer.java index 5b8a0aa29..4ad5bba31 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlSortedQueryTransformer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlSortedQueryTransformer.java @@ -105,8 +105,7 @@ class JpqlSortedQueryTransformer extends JpqlQueryRenderer { QueryRendererBuilder builder = super.visitSelect_item(ctx); if (ctx.result_variable() != null) { - List tokens = builder.build().stream().toList(); - transformerSupport.registerAlias(tokens.get(tokens.size() - 1).getToken()); + transformerSupport.registerAlias(builder.lastToken()); } return builder; @@ -117,8 +116,7 @@ class JpqlSortedQueryTransformer extends JpqlQueryRenderer { QueryRendererBuilder builder = super.visitJoin(ctx); - List tokens = builder.build().stream().toList(); - transformerSupport.registerAlias(tokens.get(tokens.size() - 1).getToken()); + transformerSupport.registerAlias(builder.lastToken()); return builder; } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRenderer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRenderer.java index e7b83c69b..8dd6a14ec 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRenderer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRenderer.java @@ -17,17 +17,22 @@ package org.springframework.data.jpa.repository.query; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.function.Function; import java.util.stream.Stream; +import org.antlr.v4.runtime.TokenStream; +import org.springframework.util.CompositeIterator; + /** * Abstraction to encapsulate query expressions and render a query. *

* Query rendering consists of multiple building blocks: *

    *
  • {@link JpaQueryParsingToken tokens} and - * {@link org.springframework.data.jpa.repository.query.JpaQueryParsingToken.JpaQueryExpression expression tokens}
  • + * {@link org.springframework.data.jpa.repository.query.JpaQueryParsingToken.JpaExpressionToken expression tokens} *
  • {@link QueryRenderer compositions} such as a composition of multiple tokens.
  • *
  • {@link QueryRenderer expressions} that are individual parts such as {@code SELECT} or {@code ORDER BY …}
  • *
  • {@link QueryRenderer inline expressions} such as composition of tokens and expressions such as function calls @@ -36,7 +41,7 @@ import java.util.stream.Stream; * * @author Mark Paluch */ -abstract class QueryRenderer { +abstract class QueryRenderer implements QueryTokenStream { /** * Creates a QueryRenderer from a collection of {@link JpaQueryParsingToken}. @@ -44,8 +49,8 @@ abstract class QueryRenderer { * @param tokens * @return */ - static QueryRenderer from(Collection tokens) { - List tokensToUse = new ArrayList<>(32); + static QueryRenderer from(Collection tokens) { + List tokensToUse = new ArrayList<>(Math.max(tokens.size(), 32)); tokensToUse.addAll(tokens); return new TokenRenderer(tokensToUse); } @@ -80,10 +85,7 @@ abstract class QueryRenderer { * @return */ QueryRenderer append(QueryRenderer renderer) { - List objects = new ArrayList<>(32); - objects.add(this); - objects.add(renderer); - return new CompositeRenderer(objects); + return CompositeRenderer.combine(this, renderer); } /** @@ -93,13 +95,6 @@ abstract class QueryRenderer { return false; } - /** - * @return stream of tokens. - */ - public Stream stream() { - return Stream.empty(); - } - @Override public String toString() { return render(); @@ -111,9 +106,21 @@ abstract class QueryRenderer { static class CompositeRenderer extends QueryRenderer { private final List nested; + private int size; - CompositeRenderer(List nested) { - this.nested = new ArrayList<>(nested); + static CompositeRenderer combine(QueryRenderer root, QueryRenderer nested) { + + List queryRenderers = new ArrayList<>(32); + queryRenderers.add(root); + queryRenderers.add(nested); + + return new CompositeRenderer(queryRenderers, root.estimatedSize() + nested.estimatedSize()); + } + + private CompositeRenderer(List nested, int size) { + + this.nested = nested; + this.size = size; } @Override @@ -140,6 +147,7 @@ abstract class QueryRenderer { QueryRenderer append(QueryRenderer renderer) { nested.add(renderer); + this.size += renderer.estimatedSize(); return this; } @@ -149,15 +157,18 @@ abstract class QueryRenderer { } @Override - public Stream stream() { - - Stream stream = Stream.empty(); + public Iterator iterator() { + CompositeIterator iterator = new CompositeIterator<>(); for (QueryRenderer renderer : nested) { - stream = Stream.concat(stream, renderer.stream()); + iterator.add(renderer.iterator()); } + return iterator; + } - return stream; + @Override + public int estimatedSize() { + return size; } } @@ -166,15 +177,15 @@ abstract class QueryRenderer { */ static class TokenRenderer extends QueryRenderer { - private final List tokens; + private final List tokens; - TokenRenderer(List tokens) { + TokenRenderer(List tokens) { this.tokens = tokens; } @Override String render() { - return JpaQueryParsingToken.render(tokens); + return render(tokens); } @Override @@ -190,19 +201,80 @@ abstract class QueryRenderer { @Override public boolean isExpression() { - return !tokens.isEmpty() && tokens.get(tokens.size() - 1) instanceof JpaQueryParsingToken.JpaQueryExpression; + return !tokens.isEmpty() && tokens.get(tokens.size() - 1).isExpression(); } @Override - public Stream stream() { + public Stream stream() { return tokens.stream(); } + + @Override + public Iterator iterator() { + return tokens.iterator(); + } + + @Override + public List toList() { + return tokens; + } + + @Override + public int estimatedSize() { + return tokens.size(); + } + + /** + * Render a list of {@link JpaQueryParsingToken}s into a string. + * + * @param tokens + * @return rendered string containing either a query or some subset of that query + */ + static String render(Object tokens) { + + if (tokens instanceof Collection tpr) { + return render(tpr); + } + + if(tokens instanceof QueryRendererBuilder qrb) { + return qrb.build().render(); + } + + if(tokens instanceof QueryRenderer qr) { + return qr.render(); + } + + throw new IllegalArgumentException("Unknown token type %s".formatted(tokens)); + } + + + static String render(Collection tokens) { + + StringBuilder results = new StringBuilder(); + + boolean previousExpression = false; + + for (QueryToken jpaQueryParsingToken : tokens) { + + if (previousExpression) { + if (!results.isEmpty() && results.charAt(results.length() - 1) != ' ') { + results.append(' '); + } + } + + previousExpression = jpaQueryParsingToken.isExpression(); + results.append(jpaQueryParsingToken.value()); + } + + return results.toString(); + } + } /** * Builder for {@link QueryRenderer}. */ - static class QueryRendererBuilder { + static class QueryRendererBuilder implements QueryTokenStream { protected QueryRenderer current = QueryRenderer.empty(); @@ -217,7 +289,7 @@ abstract class QueryRenderer { * @param */ public static QueryRendererBuilder concat(Collection elements, Function visitor, - JpaQueryParsingToken separator) { + QueryToken separator) { return concat(elements, visitor, QueryRendererBuilder::toInline, separator); } @@ -232,7 +304,7 @@ abstract class QueryRenderer { * @param */ public static QueryRendererBuilder concatExpressions(Collection elements, - Function visitor, JpaQueryParsingToken separator) { + Function visitor, QueryToken separator) { return concat(elements, visitor, QueryRendererBuilder::toExpression, separator); } @@ -248,7 +320,7 @@ abstract class QueryRenderer { * @param */ public static QueryRendererBuilder concat(Collection elements, Function visitor, - Function postProcess, JpaQueryParsingToken separator) { + Function postProcess, QueryToken separator) { QueryRendererBuilder builder = new QueryRendererBuilder(); for (T element : elements) { @@ -267,7 +339,7 @@ abstract class QueryRenderer { * @param token * @return */ - public static QueryRendererBuilder from(JpaQueryParsingToken token) { + public static QueryRendererBuilder from(QueryToken token) { return new QueryRendererBuilder().append(token); } @@ -277,7 +349,7 @@ abstract class QueryRenderer { * @param token * @return {@code this} builder. */ - QueryRendererBuilder append(JpaQueryParsingToken token) { + QueryRendererBuilder append(QueryToken token) { return append(List.of(token)); } @@ -287,10 +359,14 @@ abstract class QueryRenderer { * @param tokens * @return {@code this} builder. */ - QueryRendererBuilder append(Collection tokens) { + QueryRendererBuilder append(Collection tokens) { return append(QueryRenderer.from(tokens)); } + QueryRendererBuilder append(QueryTokenStream tokens) { + return append(QueryRenderer.from(tokens.toList())); + } + /** * Append a QueryRendererBuilder. * @@ -378,7 +454,7 @@ abstract class QueryRenderer { * @return */ public boolean isEmpty() { - return current instanceof EmptyQueryRenderer; + return current.isEmpty(); } public QueryRenderer build() { @@ -402,6 +478,26 @@ abstract class QueryRenderer { public QueryRenderer toInline() { return new InlineRenderer(current); } + + @Override + public List toList() { + return current.toList(); + } + + @Override + public Stream stream() { + return current.stream(); + } + + @Override + public int estimatedSize() { + return current.estimatedSize(); + } + + @Override + public Iterator iterator() { + return current.iterator(); + } } private static class InlineRenderer extends QueryRenderer { @@ -418,9 +514,24 @@ abstract class QueryRenderer { } @Override - public Stream stream() { + public Stream stream() { return delegate.stream(); } + + @Override + public List toList() { + return delegate.toList(); + } + + @Override + public Iterator iterator() { + return delegate.iterator(); + } + + @Override + public int estimatedSize() { + return delegate.estimatedSize(); + } } private static class ExpressionRenderer extends QueryRenderer { @@ -442,9 +553,24 @@ abstract class QueryRenderer { } @Override - public Stream stream() { + public Stream stream() { return delegate.stream(); } + + @Override + public List toList() { + return delegate.toList(); + } + + @Override + public Iterator iterator() { + return delegate.iterator(); + } + + @Override + public int estimatedSize() { + return delegate.estimatedSize(); + } } private static class EmptyQueryRenderer extends QueryRenderer { @@ -460,5 +586,30 @@ abstract class QueryRenderer { QueryRenderer append(QueryRenderer renderer) { return renderer; } + + @Override + public boolean isEmpty() { + return true; + } + + @Override + public List toList() { + return Collections.emptyList(); + } + + @Override + public Stream stream() { + return Stream.empty(); + } + + @Override + public Iterator iterator() { + return Collections.emptyIterator(); + } + + @Override + public int estimatedSize() { + return 0; + } } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryToken.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryToken.java new file mode 100644 index 000000000..c4b85109f --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryToken.java @@ -0,0 +1,30 @@ +/* + * Copyright 2024 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; + +/** + * @author Christoph Strobl + */ +public interface QueryToken { + + String value(); + + default boolean isExpression() { + return false; + } + + boolean isA(QueryToken queryToken); +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryTokenStream.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryTokenStream.java new file mode 100644 index 000000000..fb61a5e80 --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryTokenStream.java @@ -0,0 +1,38 @@ +/* + * Copyright 2024 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 org.springframework.data.util.Streamable; +import org.springframework.lang.Nullable; +import org.springframework.util.CollectionUtils; + +/** + * @author Christoph Strobl + */ +public interface QueryTokenStream extends Streamable { + + @Nullable + default T firstToken() { + return CollectionUtils.firstElement(toList()); + } + + @Nullable + default T lastToken() { + return CollectionUtils.lastElement(toList()); + } + + int estimatedSize(); +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryTransformers.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryTransformers.java index a6f167cea..32240a807 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryTransformers.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryTransformers.java @@ -18,6 +18,7 @@ package org.springframework.data.jpa.repository.query; import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken.*; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; /** @@ -28,19 +29,14 @@ import java.util.List; */ class QueryTransformers { - /** - * Filter a token list from a {@code SELECT} clause to be used within a count query. That is, filter any {@code AS …} - * aliases. - * - * @param selection the input selection. - * @return filtered selection to be used with count queries. - */ - static List filterCountSelection(List selection) { + static CountSelectionTokenStream filterCountSelection(QueryTokenStream selection) { - List target = new ArrayList<>(selection.size()); + List target = new ArrayList<>(selection.estimatedSize()); boolean skipNext = false; + boolean containsNew = false; + + for (QueryToken token : selection) { - for (JpaQueryParsingToken token : selection) { if (skipNext) { skipNext = false; @@ -52,14 +48,48 @@ class QueryTransformers { continue; } - if (!token.isA(TOKEN_COMMA) && token instanceof JpaQueryExpression) { - token = JpaQueryParsingToken.token(token.getToken()); + if (!token.isA(TOKEN_COMMA) && token.isExpression()) { + token = JpaQueryParsingToken.token(token.value()); + } + + if(!containsNew && token.value().contains("new")) { + containsNew = true; } target.add(token); } - return target; + return new CountSelectionTokenStream(target, containsNew); + } + + static class CountSelectionTokenStream implements QueryTokenStream { + + private final List tokens; + private final boolean requiresPrimaryAlias; + + public CountSelectionTokenStream(List tokens, boolean containsNew) { + this.tokens = tokens; + this.requiresPrimaryAlias = containsNew; + } + + @Override + public int estimatedSize() { + return tokens.size(); + } + + @Override + public Iterator iterator() { + return tokens.iterator(); + } + + @Override + public List toList() { + return tokens; + } + + public boolean requiresPrimaryAlias() { + return requiresPrimaryAlias; + } } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlComplianceTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlComplianceTests.java index 606434093..f8b0fe0e7 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlComplianceTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlComplianceTests.java @@ -21,6 +21,7 @@ import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; import org.junit.jupiter.api.Test; +import org.springframework.data.jpa.repository.query.QueryRenderer.TokenRenderer; /** * Tests built around examples of EQL found in the EclipseLink's docs at @@ -47,7 +48,7 @@ class EqlComplianceTests { EqlParser.StartContext parsedQuery = parser.start(); - return render(new EqlQueryRenderer().visit(parsedQuery)); + return TokenRenderer.render(new EqlQueryRenderer().visit(parsedQuery)); } private void assertQuery(String query) { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlQueryRendererTests.java index 76dd2f01a..c80185ce7 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlQueryRendererTests.java @@ -28,6 +28,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.data.jpa.repository.query.QueryRenderer.TokenRenderer; /** * Tests built around examples of EQL found in the JPA spec @@ -54,7 +55,7 @@ class EqlQueryRendererTests { EqlParser.StartContext parsedQuery = parser.start(); - return render(new EqlQueryRenderer().visit(parsedQuery)); + return TokenRenderer.render(new EqlQueryRenderer().visit(parsedQuery)); } static Stream reservedWords() { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlSpecificationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlSpecificationTests.java index 1dda209b7..b5c6aabe4 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlSpecificationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlSpecificationTests.java @@ -22,6 +22,7 @@ import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.springframework.data.jpa.repository.query.QueryRenderer.TokenRenderer; /** * Tests built around examples of EQL found in the JPA spec @@ -44,7 +45,7 @@ class EqlSpecificationTests { EqlParser.StartContext parsedQuery = parser.start(); - return render(new EqlQueryRenderer().visit(parsedQuery)); + return TokenRenderer.render(new EqlQueryRenderer().visit(parsedQuery)); } private void assertQuery(String query) { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlSpecificationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlSpecificationTests.java index 65f87fd85..74e474f7c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlSpecificationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlSpecificationTests.java @@ -22,6 +22,7 @@ import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.springframework.data.jpa.repository.query.QueryRenderer.TokenRenderer; /** * Tests built around examples of HQL found in @@ -46,7 +47,7 @@ class HqlSpecificationTests { HqlParser.StartContext parsedQuery = parser.start(); - return render(new HqlQueryRenderer().visit(parsedQuery)); + return TokenRenderer.render(new HqlQueryRenderer().visit(parsedQuery)); } private void assertQuery(String query) { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlComplianceTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlComplianceTests.java index f85859497..8dc22aa44 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlComplianceTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlComplianceTests.java @@ -21,6 +21,7 @@ import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; import org.junit.jupiter.api.Test; +import org.springframework.data.jpa.repository.query.QueryRenderer.TokenRenderer; /** * Test to verify compliance of {@link JpqlParser} with standard SQL. Other than {@link JpqlSpecificationTests} tests in @@ -40,7 +41,7 @@ class JpqlComplianceTests { JpqlParser.StartContext parsedQuery = parser.start(); - return render(new JpqlQueryRenderer().visit(parsedQuery)); + return TokenRenderer.render(new JpqlQueryRenderer().visit(parsedQuery)); } private void assertQuery(String query) { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java index bf9d1074b..9bd2d8bfa 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java @@ -28,6 +28,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.data.jpa.repository.query.QueryRenderer.TokenRenderer; /** * Tests built around examples of JPQL found in the JPA spec @@ -55,7 +56,7 @@ class JpqlQueryRendererTests { JpqlParser.StartContext parsedQuery = parser.start(); - return render(new JpqlQueryRenderer().visit(parsedQuery)); + return TokenRenderer.render(new JpqlQueryRenderer().visit(parsedQuery)); } static Stream reservedWords() { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlSpecificationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlSpecificationTests.java index 02493d7f9..3b8d9ac37 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlSpecificationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlSpecificationTests.java @@ -22,6 +22,7 @@ import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.springframework.data.jpa.repository.query.QueryRenderer.TokenRenderer; /** * Tests built around examples of JPQL found in the JPA spec @@ -48,7 +49,7 @@ class JpqlSpecificationTests { JpqlParser.StartContext parsedQuery = parser.start(); - return render(new JpqlQueryRenderer().visit(parsedQuery)); + return TokenRenderer.render(new JpqlQueryRenderer().visit(parsedQuery)); } private void assertQuery(String query) {