DATAJPA-86 - Pagination over queries using group-by works correctly.
Added handling of count methods for queries using group by. In case the count query returns multiple results we use the number of results instead of failing. If the result contains one result we use this one. Moved hint handling to AbstractJpaQuery.
This commit is contained in:
@@ -17,12 +17,13 @@ package org.springframework.data.jpa.repository.query;
|
|||||||
|
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
import javax.persistence.Query;
|
import javax.persistence.Query;
|
||||||
|
import javax.persistence.QueryHint;
|
||||||
|
import javax.persistence.TypedQuery;
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.query.JpaQueryExecution.CollectionExecution;
|
import org.springframework.data.jpa.repository.query.JpaQueryExecution.CollectionExecution;
|
||||||
import org.springframework.data.jpa.repository.query.JpaQueryExecution.ModifyingExecution;
|
import org.springframework.data.jpa.repository.query.JpaQueryExecution.ModifyingExecution;
|
||||||
import org.springframework.data.jpa.repository.query.JpaQueryExecution.PagedExecution;
|
import org.springframework.data.jpa.repository.query.JpaQueryExecution.PagedExecution;
|
||||||
import org.springframework.data.jpa.repository.query.JpaQueryExecution.SingleEntityExecution;
|
import org.springframework.data.jpa.repository.query.JpaQueryExecution.SingleEntityExecution;
|
||||||
import org.springframework.data.repository.query.QueryMethod;
|
|
||||||
import org.springframework.data.repository.query.RepositoryQuery;
|
import org.springframework.data.repository.query.RepositoryQuery;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
@@ -58,7 +59,7 @@ public abstract class AbstractJpaQuery implements RepositoryQuery {
|
|||||||
* org.springframework.data.repository.query.RepositoryQuery#getQueryMethod
|
* org.springframework.data.repository.query.RepositoryQuery#getQueryMethod
|
||||||
* ()
|
* ()
|
||||||
*/
|
*/
|
||||||
public QueryMethod getQueryMethod() {
|
public JpaQueryMethod getQueryMethod() {
|
||||||
|
|
||||||
return method;
|
return method;
|
||||||
}
|
}
|
||||||
@@ -106,12 +107,46 @@ public abstract class AbstractJpaQuery implements RepositoryQuery {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ParameterBinder createBinder(Object[] values) {
|
/**
|
||||||
|
* Applies the declared query hints to the given query.
|
||||||
|
*
|
||||||
|
* @param query
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private <T extends Query> T applyHints(T query, JpaQueryMethod method) {
|
||||||
|
|
||||||
|
for (QueryHint hint : method.getHints()) {
|
||||||
|
query.setHint(hint.name(), hint.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ParameterBinder createBinder(Object[] values) {
|
||||||
return new ParameterBinder(getQueryMethod().getParameters(), values);
|
return new ParameterBinder(getQueryMethod().getParameters(), values);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract Query createQuery(Object[] values);
|
protected Query createQuery(Object[] values) {
|
||||||
|
return applyHints(doCreateQuery(values), method);
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract Query createCountQuery(Object[] values);
|
protected TypedQuery<Long> createCountQuery(Object[] values) {
|
||||||
|
return applyHints(doCreateCountQuery(values), method);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link Query} instance for the given values.
|
||||||
|
*
|
||||||
|
* @param values must not be {@literal null}.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected abstract Query doCreateQuery(Object[] values);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link TypedQuery} for counting using the given values.
|
||||||
|
*
|
||||||
|
* @param values must not be {@literal null}.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected abstract TypedQuery<Long> doCreateCountQuery(Object[] values);
|
||||||
}
|
}
|
||||||
@@ -15,9 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package org.springframework.data.jpa.repository.query;
|
package org.springframework.data.jpa.repository.query;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
import javax.persistence.NoResultException;
|
import javax.persistence.NoResultException;
|
||||||
import javax.persistence.Query;
|
import javax.persistence.Query;
|
||||||
|
import javax.persistence.TypedQuery;
|
||||||
|
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.PageImpl;
|
import org.springframework.data.domain.PageImpl;
|
||||||
@@ -42,7 +45,6 @@ public abstract class JpaQueryExecution {
|
|||||||
* @param binder
|
* @param binder
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public Object execute(AbstractJpaQuery query, Object[] values) {
|
public Object execute(AbstractJpaQuery query, Object[] values) {
|
||||||
|
|
||||||
Assert.notNull(query);
|
Assert.notNull(query);
|
||||||
@@ -71,7 +73,6 @@ public abstract class JpaQueryExecution {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object doExecute(AbstractJpaQuery query, Object[] values) {
|
protected Object doExecute(AbstractJpaQuery query, Object[] values) {
|
||||||
|
|
||||||
return query.createQuery(values).getResultList();
|
return query.createQuery(values).getResultList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,11 +94,11 @@ public abstract class JpaQueryExecution {
|
|||||||
protected Object doExecute(AbstractJpaQuery repositoryQuery, Object[] values) {
|
protected Object doExecute(AbstractJpaQuery repositoryQuery, Object[] values) {
|
||||||
|
|
||||||
// Execute query to compute total
|
// Execute query to compute total
|
||||||
Query projection = repositoryQuery.createCountQuery(values);
|
TypedQuery<Long> projection = repositoryQuery.createCountQuery(values);
|
||||||
Long total = (Long) projection.getSingleResult();
|
List<Long> totals = projection.getResultList();
|
||||||
|
Long total = totals.size() == 1 ? totals.get(0) : totals.size();
|
||||||
|
|
||||||
Query query = repositoryQuery.createQuery(values);
|
Query query = repositoryQuery.createQuery(values);
|
||||||
|
|
||||||
ParameterAccessor accessor = new ParametersParameterAccessor(parameters, values);
|
ParameterAccessor accessor = new ParametersParameterAccessor(parameters, values);
|
||||||
|
|
||||||
return new PageImpl<Object>(query.getResultList(), accessor.getPageable(), total);
|
return new PageImpl<Object>(query.getResultList(), accessor.getPageable(), total);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ package org.springframework.data.jpa.repository.query;
|
|||||||
|
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
import javax.persistence.Query;
|
import javax.persistence.Query;
|
||||||
|
import javax.persistence.TypedQuery;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -101,15 +102,10 @@ final class NamedQuery extends AbstractJpaQuery {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
* @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#doCreateQuery(java.lang.Object[])
|
||||||
* @see
|
|
||||||
* org.springframework.data.jpa.repository.query.AbstractStringBasedJpaQuery
|
|
||||||
* #
|
|
||||||
* createQuery(org.springframework.data.jpa.repository.query.ParameterBinder
|
|
||||||
* )
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected Query createQuery(Object[] values) {
|
protected Query doCreateQuery(Object[] values) {
|
||||||
|
|
||||||
Query query = getEntityManager().createNamedQuery(queryName);
|
Query query = getEntityManager().createNamedQuery(queryName);
|
||||||
return createBinder(values).bindAndPrepare(query);
|
return createBinder(values).bindAndPrepare(query);
|
||||||
@@ -117,18 +113,15 @@ final class NamedQuery extends AbstractJpaQuery {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
* @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#doCreateCountQuery(java.lang.Object[])
|
||||||
* @see
|
|
||||||
* org.springframework.data.jpa.repository.query.AbstractStringBasedJpaQuery
|
|
||||||
* #createCountQuery(org.springframework.data.jpa.repository.query.
|
|
||||||
* ParameterBinder)
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected Query createCountQuery(Object[] values) {
|
protected TypedQuery<Long> doCreateCountQuery(Object[] values) {
|
||||||
|
|
||||||
Query query = createQuery(values);
|
Query query = createQuery(values);
|
||||||
String queryString = extractor.extractQueryString(query);
|
String queryString = extractor.extractQueryString(query);
|
||||||
|
|
||||||
return createBinder(values).bind(getEntityManager().createQuery(QueryUtils.createCountQueryFor(queryString)));
|
return createBinder(values).bind(
|
||||||
|
getEntityManager().createQuery(QueryUtils.createCountQueryFor(queryString), Long.class));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -95,7 +95,7 @@ public class ParameterBinder {
|
|||||||
* @param query
|
* @param query
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public Query bind(Query query) {
|
public <T extends Query> T bind(T query) {
|
||||||
|
|
||||||
int methodParameterPosition = 0;
|
int methodParameterPosition = 0;
|
||||||
int queryParameterPosition = 1;
|
int queryParameterPosition = 1;
|
||||||
|
|||||||
@@ -62,28 +62,23 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
* @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#doCreateQuery(java.lang.Object[])
|
||||||
* @see
|
|
||||||
* org.springframework.data.jpa.repository.query.AbstractJpaQuery#createQuery
|
|
||||||
* (javax.persistence.EntityManager,
|
|
||||||
* org.springframework.data.jpa.repository.query.ParameterBinder)
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Query createQuery(Object[] values) {
|
public Query doCreateQuery(Object[] values) {
|
||||||
|
|
||||||
return query.createQuery(values);
|
return query.createQuery(values);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
* @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#doCreateCountQuery(java.lang.Object[])
|
||||||
* @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#
|
|
||||||
* createCountQuery(javax.persistence.EntityManager)
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Query createCountQuery(Object[] values) {
|
@SuppressWarnings("unchecked")
|
||||||
|
public TypedQuery<Long> doCreateCountQuery(Object[] values) {
|
||||||
|
|
||||||
return countQuery.createQuery(values);
|
return (TypedQuery<Long>) countQuery.createQuery(values);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -15,11 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.springframework.data.jpa.repository.query;
|
package org.springframework.data.jpa.repository.query;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
import javax.persistence.Query;
|
import javax.persistence.Query;
|
||||||
import javax.persistence.QueryHint;
|
import javax.persistence.TypedQuery;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -42,7 +40,6 @@ final class SimpleJpaQuery extends AbstractJpaQuery {
|
|||||||
private final String queryString;
|
private final String queryString;
|
||||||
private final String countQuery;
|
private final String countQuery;
|
||||||
private final String alias;
|
private final String alias;
|
||||||
private final List<QueryHint> hints;
|
|
||||||
private final Parameters parameters;
|
private final Parameters parameters;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -53,7 +50,6 @@ final class SimpleJpaQuery extends AbstractJpaQuery {
|
|||||||
super(method, em);
|
super(method, em);
|
||||||
this.queryString = queryString;
|
this.queryString = queryString;
|
||||||
this.alias = QueryUtils.detectAlias(queryString);
|
this.alias = QueryUtils.detectAlias(queryString);
|
||||||
this.hints = method.getHints();
|
|
||||||
this.parameters = method.getParameters();
|
this.parameters = method.getParameters();
|
||||||
this.countQuery = method.getCountQuery() == null ? QueryUtils.createCountQueryFor(queryString) : method
|
this.countQuery = method.getCountQuery() == null ? QueryUtils.createCountQueryFor(queryString) : method
|
||||||
.getCountQuery();
|
.getCountQuery();
|
||||||
@@ -64,48 +60,26 @@ final class SimpleJpaQuery extends AbstractJpaQuery {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
* @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#createQuery(java.lang.Object[])
|
||||||
* @see
|
|
||||||
* org.springframework.data.jpa.repository.query.AbstractStringBasedJpaQuery
|
|
||||||
* #
|
|
||||||
* createQuery(org.springframework.data.jpa.repository.query.ParameterBinder
|
|
||||||
* )
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Query createQuery(Object[] values) {
|
public Query doCreateQuery(Object[] values) {
|
||||||
|
|
||||||
ParameterAccessor accessor = new ParametersParameterAccessor(parameters, values);
|
ParameterAccessor accessor = new ParametersParameterAccessor(parameters, values);
|
||||||
String sortedQueryString = QueryUtils.applySorting(queryString, accessor.getSort(), alias);
|
String sortedQueryString = QueryUtils.applySorting(queryString, accessor.getSort(), alias);
|
||||||
|
|
||||||
Query query = getEntityManager().createQuery(sortedQueryString);
|
Query query = getEntityManager().createQuery(sortedQueryString);
|
||||||
return createBinder(values).bindAndPrepare(applyHints(query));
|
return createBinder(values).bindAndPrepare(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
* @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#doCreateCountQuery(java.lang.Object[])
|
||||||
* @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#
|
|
||||||
* createCountQuery(java.lang.Object[])
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected Query createCountQuery(Object[] values) {
|
protected TypedQuery<Long> doCreateCountQuery(Object[] values) {
|
||||||
|
|
||||||
return createBinder(values).bind(applyHints(getEntityManager().createQuery(countQuery)));
|
return createBinder(values).bind(getEntityManager().createQuery(countQuery, Long.class));
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Applies the declared query hints to the given query.
|
|
||||||
*
|
|
||||||
* @param query
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private Query applyHints(Query query) {
|
|
||||||
|
|
||||||
for (QueryHint hint : hints) {
|
|
||||||
query.setHint(hint.name(), hint.value());
|
|
||||||
}
|
|
||||||
|
|
||||||
return query;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -725,6 +725,18 @@ public class UserRepositoryTests {
|
|||||||
assertThat(result, hasItem(firstUser));
|
assertThat(result, hasItem(firstUser));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see DATADOC-86
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void readsPageWithGroupByClauseCorrectly() {
|
||||||
|
|
||||||
|
flushTestUsers();
|
||||||
|
|
||||||
|
Page<String> result = repository.findByLastnameGrouped(new PageRequest(0, 10));
|
||||||
|
assertThat(result.getTotalPages(), is(1));
|
||||||
|
}
|
||||||
|
|
||||||
private Page<User> executeSpecWithSort(Sort sort) {
|
private Page<User> executeSpecWithSort(Sort sort) {
|
||||||
|
|
||||||
flushTestUsers();
|
flushTestUsers();
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import org.mockito.runners.MockitoJUnitRunner;
|
|||||||
import org.springframework.data.jpa.repository.query.JpaQueryExecution.ModifyingExecution;
|
import org.springframework.data.jpa.repository.query.JpaQueryExecution.ModifyingExecution;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unit test for {@link QueryExecution}.
|
* Unit test for {@link JpaQueryExecution}.
|
||||||
*
|
*
|
||||||
* @author Oliver Gierke
|
* @author Oliver Gierke
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -23,8 +23,7 @@ import static org.mockito.Mockito.*;
|
|||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
import javax.persistence.Query;
|
import javax.persistence.TypedQuery;
|
||||||
import javax.persistence.QueryHint;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -34,7 +33,6 @@ import org.mockito.Mockito;
|
|||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
import org.springframework.data.domain.PageRequest;
|
import org.springframework.data.domain.PageRequest;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.data.jpa.repository.QueryHints;
|
|
||||||
import org.springframework.data.jpa.repository.sample.UserRepository;
|
import org.springframework.data.jpa.repository.sample.UserRepository;
|
||||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||||
import org.springframework.data.repository.query.Parameters;
|
import org.springframework.data.repository.query.Parameters;
|
||||||
@@ -54,31 +52,22 @@ public class SimpleJpaQueryUnitTests {
|
|||||||
@Mock
|
@Mock
|
||||||
QueryExtractor extractor;
|
QueryExtractor extractor;
|
||||||
@Mock
|
@Mock
|
||||||
Query query;
|
TypedQuery<Long> query;
|
||||||
@Mock
|
@Mock
|
||||||
RepositoryMetadata metadata;
|
RepositoryMetadata metadata;
|
||||||
@Mock
|
@Mock
|
||||||
ParameterBinder binder;
|
ParameterBinder binder;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@QueryHints(@QueryHint(name = "foo", value = "bar"))
|
|
||||||
public void setUp() throws SecurityException, NoSuchMethodException {
|
public void setUp() throws SecurityException, NoSuchMethodException {
|
||||||
|
|
||||||
when(em.createQuery(anyString())).thenReturn(query);
|
when(em.createQuery(anyString())).thenReturn(query);
|
||||||
|
when(em.createQuery(anyString(), eq(Long.class))).thenReturn(query);
|
||||||
|
|
||||||
Method setUp = UserRepository.class.getMethod("findByLastname", String.class);
|
Method setUp = UserRepository.class.getMethod("findByLastname", String.class);
|
||||||
method = new JpaQueryMethod(setUp, metadata, extractor);
|
method = new JpaQueryMethod(setUp, metadata, extractor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void appliesHintsCorrectly() throws Exception {
|
|
||||||
|
|
||||||
SimpleJpaQuery jpaQuery = new SimpleJpaQuery(method, em, "foobar");
|
|
||||||
jpaQuery.createQuery(new Object[] { "gierke" });
|
|
||||||
|
|
||||||
verify(query).setHint("foo", "bar");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void prefersDeclaredCountQueryOverCreatingOne() throws Exception {
|
public void prefersDeclaredCountQueryOverCreatingOne() throws Exception {
|
||||||
|
|
||||||
@@ -86,7 +75,7 @@ public class SimpleJpaQueryUnitTests {
|
|||||||
when(method.getCountQuery()).thenReturn("foo");
|
when(method.getCountQuery()).thenReturn("foo");
|
||||||
when(method.getParameters()).thenReturn(
|
when(method.getParameters()).thenReturn(
|
||||||
new Parameters(SimpleJpaQueryUnitTests.class.getMethod("prefersDeclaredCountQueryOverCreatingOne")));
|
new Parameters(SimpleJpaQueryUnitTests.class.getMethod("prefersDeclaredCountQueryOverCreatingOne")));
|
||||||
when(em.createQuery("foo")).thenReturn(query);
|
when(em.createQuery("foo", Long.class)).thenReturn(query);
|
||||||
|
|
||||||
SimpleJpaQuery jpaQuery = new SimpleJpaQuery(method, em, "select u from User u");
|
SimpleJpaQuery jpaQuery = new SimpleJpaQuery(method, em, "select u from User u");
|
||||||
|
|
||||||
|
|||||||
@@ -185,4 +185,9 @@ public interface UserRepository extends JpaRepository<User, Integer>, JpaSpecifi
|
|||||||
|
|
||||||
List<User> findByLastnameAndFirstnameAllIgnoringCase(String lastname, String firstname);
|
List<User> findByLastnameAndFirstnameAllIgnoringCase(String lastname, String firstname);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see DATADOC-86 - Count execution for group by
|
||||||
|
*/
|
||||||
|
@Query("select u.lastname from User u group by u.lastname")
|
||||||
|
Page<String> findByLastnameGrouped(Pageable pageable);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user