Skip to content

Commit 31a5f2f

Browse files
some refinements.
There's a difference in what the query needs to look like using dto vs. interface projections where the former does not allow column aliases and the latter requires them.
1 parent 02f7af5 commit 31a5f2f

File tree

4 files changed

+25
-4
lines changed

4 files changed

+25
-4
lines changed

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java

+11-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,13 @@ public Query doCreateQuery(JpaParametersParameterAccessor accessor) {
119119

120120
Sort sort = accessor.getSort();
121121
ResultProcessor processor = getQueryMethod().getResultProcessor().withDynamicProjection(accessor);
122-
String sortedQueryString = getSortedQueryString(sort, processor.getReturnedType());
122+
123+
String sortedQueryString = null;
124+
if(querySortRewriter.equals(NoOpQuerySortRewriter.INSTANCE) && accessor.findDynamicProjection() != null && !accessor.findDynamicProjection().isInterface()) {
125+
sortedQueryString = getSortedQueryString(new ProjectingSortRewriter(), query, sort, processor.getReturnedType());
126+
} else {
127+
sortedQueryString = getSortedQueryString(sort, processor.getReturnedType());
128+
}
123129

124130
Query query = createJpaQuery(sortedQueryString, sort, accessor.getPageable(), processor.getReturnedType());
125131

@@ -134,6 +140,10 @@ String getSortedQueryString(Sort sort, ReturnedType returnedType) {
134140
return querySortRewriter.getSorted(query, sort, returnedType);
135141
}
136142

143+
private static String getSortedQueryString(QuerySortRewriter rewriter, DeclaredQuery query, Sort sort, ReturnedType returnedType) {
144+
return rewriter.getSorted(query, sort, returnedType);
145+
}
146+
137147
@Override
138148
protected ParameterBinder createBinder() {
139149
return createBinder(query);

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkUserRepositoryFinderTests.java

+4
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,8 @@ void executesNotInQueryCorrectly() {}
3636
@Override
3737
void executesInKeywordForPageCorrectly() {}
3838

39+
@Disabled
40+
@Override
41+
void rawMapProjectionWithEntityAndAggregatedValue() {}
42+
3943
}

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -735,10 +735,10 @@ List<String> findAllAndSortByFunctionResultNamedParameter(@Param("namedParameter
735735
@UserRoleCountProjectingQuery
736736
Page<UserRoleCountDtoProjection> dtoProjectionEntityAndAggregatedValue(PageRequest page);
737737

738-
@UserRoleCountProjectingQuery
738+
@Query("select u as user, count(r) as roleCount from User u left outer join u.roles r group by u")
739739
List<UserRoleCountInterfaceProjection> interfaceProjectionEntityAndAggregatedValue();
740740

741-
@UserRoleCountProjectingQuery
741+
@Query("select u as user, count(r) as roleCount from User u left outer join u.roles r group by u")
742742
List<Map<String, Object>> rawMapProjectionEntityAndAggregatedValue();
743743

744744
@UserRoleCountProjectingQuery
@@ -747,7 +747,7 @@ List<String> findAllAndSortByFunctionResultNamedParameter(@Param("namedParameter
747747
Window<User> findBy(OffsetScrollPosition position);
748748

749749
@Retention(RetentionPolicy.RUNTIME)
750-
@Query("select u AS user, count(r) AS roleCount from User u left outer join u.roles r group by u")
750+
@Query("select u, count(r) from User u left outer join u.roles r group by u")
751751
@interface UserRoleCountProjectingQuery {}
752752

753753
interface RolesAndFirstname {

src/main/antora/modules/ROOT/pages/repositories/projections.adoc

+7
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ This query gets rewritten to `SELECT new UserDto(u.firstname, u.lastname) FROM U
4040
This query gets rewritten to `SELECT new UserDto(u.firstname, u.lastname) FROM USER u`.
4141
====
4242

43+
[WARNING]
44+
====
45+
JPQL constructor expressions must not contain aliases for selected columns.
46+
While `SELECT u as user, count(u.roles) as roleCount FROM USER u ...` is a valid usecase for interface based projections that rely on column names from the returned `Tuple`, the same construct is invalid when requesting a DTO where it needs to be `SELECT u, count(u.roles) FROM USER u ...`. +
47+
Some persistence providers may be lenient about this, others not.
48+
====
49+
4350
Repository query methods that return a DTO projection type (a Java type outside the domain type hierarchy) are subject for query rewriting.
4451
If an `@Query`-annotated query already uses constructor expressions, then Spring Data backs off and doesn't apply DTO constructor expression rewriting.
4552

0 commit comments

Comments
 (0)