Skip to content

Commit c4528e2

Browse files
christophstroblmp911de
authored andcommitted
Do not use tuple-typed queries for derived count queries.
Prior to this change interface projections used to set useTupleQuery() to true on JpaQueryCreator, which was later also reused to build the count query. This caused trouble when reading results expecting a numeric value. Closes #4230 Original pull request: #4235
1 parent 5697b96 commit c4528e2

3 files changed

Lines changed: 38 additions & 6 deletions

File tree

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ public JpaCountQueryCreator(PartTree tree, ReturnedType returnedType, ParameterM
7777
this.distinct = tree.isDistinct();
7878
}
7979

80+
@Override
81+
public boolean useTupleQuery() {
82+
return false;
83+
}
84+
8085
@Override
8186
protected JpqlQueryBuilder.Select buildQuery(Sort sort) {
8287

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
6868
private final JpaParameters parameters;
6969

7070
private final QueryPreparer queryPreparer;
71-
private final QueryPreparer countQuery;
71+
private final CountQueryPreparer countQuery;
7272
private final EntityManager em;
7373
private final EscapeCharacter escape;
7474
private final Lazy<JpaEntityInformation<?, ?>> entityInformation;

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

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
/*
22
* Copyright 2011-present the original author or authors.
33
*
4-
* Licensed under the Apache License, Version 2.0 (the "License
5-
import org.springframework.aop.framework.Advised;
6-
");
4+
* Licensed under the Apache License, Version 2.0 (the "License");
75
* you may not use this file except in compliance with the License.
86
* You may obtain a copy of the License at
97
*
@@ -22,6 +20,7 @@
2220
import jakarta.persistence.EntityManager;
2321
import jakarta.persistence.PersistenceContext;
2422
import jakarta.persistence.Query;
23+
import jakarta.persistence.Tuple;
2524
import jakarta.persistence.TemporalType;
2625

2726
import java.lang.reflect.Method;
@@ -247,6 +246,20 @@ void allowsCollectionArgForCollectionProperty() throws Exception {
247246
new PartTreeJpaQuery(getQueryMethod("findByAttributes", String[].class), entityManager);
248247
}
249248

249+
@Test // GH-4230
250+
void countQueryForPagedInterfaceProjectionReturnsScalarAggregate() throws Exception {
251+
252+
JpaQueryMethod queryMethod = getQueryMethod(ProjectionPagingRepository.class, "findByFirstname", String.class,
253+
Pageable.class);
254+
PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager);
255+
256+
Query countQuery = jpaQuery
257+
.createCountQuery(getAccessor(queryMethod, new Object[] { "Matthews", PageRequest.of(0, 2) }));
258+
259+
Object total = countQuery.getSingleResult();
260+
assertThat(total).isInstanceOf(Number.class).isNotInstanceOf(Tuple.class);
261+
}
262+
250263
private void testIgnoreCase(String methodName, Object... values) throws Exception {
251264

252265
Class<?>[] parameterTypes = new Class[values.length];
@@ -262,8 +275,14 @@ private void testIgnoreCase(String methodName, Object... values) throws Exceptio
262275

263276
private JpaQueryMethod getQueryMethod(String methodName, Class<?>... parameterTypes) throws Exception {
264277

265-
Method method = UserRepository.class.getMethod(methodName, parameterTypes);
266-
return new JpaQueryMethod(method, new DefaultRepositoryMetadata(UserRepository.class),
278+
return getQueryMethod(UserRepository.class, methodName, parameterTypes);
279+
}
280+
281+
private JpaQueryMethod getQueryMethod(Class<?> repositoryType, String methodName, Class<?>... parameterTypes)
282+
throws Exception {
283+
284+
Method method = repositoryType.getMethod(methodName, parameterTypes);
285+
return new JpaQueryMethod(method, new DefaultRepositoryMetadata(repositoryType),
267286
new SpelAwareProxyProjectionFactory(), PersistenceProvider.fromEntityManager(entityManager));
268287
}
269288

@@ -326,4 +345,12 @@ interface UserRepository extends Repository<User, Integer> {
326345
List<User> findByAttributes(String... attributes);
327346
}
328347

348+
interface UserNameProjection {
349+
String getLastname();
350+
}
351+
352+
interface ProjectionPagingRepository extends Repository<User, Integer> {
353+
Page<UserNameProjection> findByFirstname(String firstname, Pageable pageable);
354+
}
355+
329356
}

0 commit comments

Comments
 (0)