Fix potential class-cast exception.

See #3895
This commit is contained in:
Mark Paluch
2025-06-03 11:15:01 +02:00
parent 2f620400d5
commit 205912ccc2
3 changed files with 19 additions and 4 deletions

View File

@@ -37,7 +37,6 @@ import org.springframework.data.repository.query.ValueExpressionDelegate;
import org.springframework.data.util.Lazy;
import org.springframework.util.Assert;
import org.springframework.util.ConcurrentLruCache;
import org.springframework.util.StringUtils;
/**
* Base class for {@link String} based JPA queries.
@@ -71,7 +70,6 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery {
* @param method must not be {@literal null}.
* @param em must not be {@literal null}.
* @param queryString must not be {@literal null}.
* @param countQuery can be {@literal null} if not defined.
* @param queryConfiguration must not be {@literal null}.
*/
AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, String queryString,

View File

@@ -175,6 +175,10 @@ abstract class QueryRenderer implements QueryTokenStream {
return EmptyQueryRenderer.INSTANCE;
}
if (!(tokenStream instanceof QueryRenderer)) {
tokenStream = QueryRenderer.from(tokenStream);
}
if (tokenStream.isExpression()) {
return (QueryRenderer) tokenStream;
}
@@ -192,6 +196,10 @@ abstract class QueryRenderer implements QueryTokenStream {
return EmptyQueryRenderer.INSTANCE;
}
if (!(tokenStream instanceof QueryRenderer)) {
tokenStream = QueryRenderer.from(tokenStream);
}
if (!tokenStream.isExpression()) {
return (QueryRenderer) tokenStream;
}
@@ -323,6 +331,12 @@ abstract class QueryRenderer implements QueryTokenStream {
public boolean isExpression() {
return !nested.isEmpty() && nested.get(nested.size() - 1).isExpression();
}
public Stream<QueryRenderer> renderers() {
return nested.stream()
.flatMap(renderer -> renderer instanceof CompositeRenderer ? ((CompositeRenderer) renderer).renderers()
: Stream.of(renderer));
}
}
/**

View File

@@ -32,9 +32,12 @@ Support for string-based queries covers both, JPQL queries(`@Query`) and native
==== JPQL Queries
When using <<projections.dtos,Class-based projections>> 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 <<projections.dtos,Class-based projections>> 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