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 3039ef735..c82f0519b 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 @@ -190,6 +190,10 @@ abstract class QueryRenderer implements QueryTokenStream { return EmptyQueryRenderer.INSTANCE; } + if (!(tokenStream instanceof QueryRenderer)) { + tokenStream = QueryRenderer.from(tokenStream); + } + if (tokenStream.isExpression()) { return (QueryRenderer) tokenStream; } @@ -207,6 +211,10 @@ abstract class QueryRenderer implements QueryTokenStream { return EmptyQueryRenderer.INSTANCE; } + if (!(tokenStream instanceof QueryRenderer)) { + tokenStream = QueryRenderer.from(tokenStream); + } + if (!tokenStream.isExpression()) { return (QueryRenderer) tokenStream; } @@ -341,6 +349,12 @@ abstract class QueryRenderer implements QueryTokenStream { public boolean isExpression() { return !nested.isEmpty() && nested.get(nested.size() - 1).isExpression(); } + + public Stream renderers() { + return nested.stream() + .flatMap(renderer -> renderer instanceof CompositeRenderer ? ((CompositeRenderer) renderer).renderers() + : Stream.of(renderer)); + } } /** diff --git a/src/main/antora/modules/ROOT/pages/repositories/projections.adoc b/src/main/antora/modules/ROOT/pages/repositories/projections.adoc index 0eb4682ff..a9df80376 100644 --- a/src/main/antora/modules/ROOT/pages/repositories/projections.adoc +++ b/src/main/antora/modules/ROOT/pages/repositories/projections.adoc @@ -32,9 +32,12 @@ Support for string-based queries covers both, JPQL queries(`@Query`) and native ==== JPQL Queries -When using <> with JPQL, you must use *constructor expressions* in your JPQL query, e.g. `SELECT new com.example.NamesOnly(u.firstname, u.lastname) from User u`. +JPA's mechanism to return <> using JPQL is *constructor expressions*. +Therefore, your query must define a constructor expression such as `SELECT new com.example.NamesOnly(u.firstname, u.lastname) from User u`. (Note the usage of a FQDN for the DTO type!) This JPQL expression can be used in `@Query` annotations as well where you define any named queries. -As a workaround you may use named queries with `ResultSetMapping` or the Hibernate-specific javadoc:{hibernatejavadocurl}org.hibernate.query.ResultListTransformer[] +As a workaround you may use named queries with `ResultSetMapping` or the Hibernate-specific javadoc:{hibernatejavadocurl}org.hibernate.query.ResultListTransformer[]. + +Spring Data JPA can aid with rewriting your query to a constructor expression if your query selects the primary entity or a list of select items. ===== DTO Projection JPQL Query Rewriting