From c8ca0f80c490e0b4686284989824a2d52fd3e3ba Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Tue, 4 Mar 2014 17:37:08 +0100 Subject: [PATCH] DATAJPA-486 - Added support for sliced query execution. Added support for Slice as return type for query methods. The execution will expand the requested page size by one to read one more element than actually requested. If that additional element is returned, it will considered to be an indicator for whether a next slice is available. Related issues: DATACMNS-397. --- .../repository/query/AbstractJpaQuery.java | 3 ++ .../repository/query/JpaQueryExecution.java | 47 ++++++++++++++++++- .../repository/UserRepositoryFinderTests.java | 39 +++++++++++---- .../jpa/repository/sample/UserRepository.java | 6 +++ 4 files changed, 85 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java index 15f0f2e52..5426f47bc 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java @@ -25,6 +25,7 @@ import org.springframework.data.jpa.repository.query.JpaQueryExecution.Collectio 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.SingleEntityExecution; +import org.springframework.data.jpa.repository.query.JpaQueryExecution.SlicedExecution; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.util.Assert; @@ -100,6 +101,8 @@ public abstract class AbstractJpaQuery implements RepositoryQuery { if (method.isCollectionQuery()) { return new CollectionExecution(); + } else if (method.isSliceQuery()) { + return new SlicedExecution(method.getParameters()); } else if (method.isPageQuery()) { return new PagedExecution(method.getParameters()); } else if (method.isModifyingQuery()) { diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java index 643f8bf51..a18bcc076 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2013 the original author or authors. + * Copyright 2008-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,6 +27,8 @@ import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.data.domain.SliceImpl; import org.springframework.data.repository.query.ParameterAccessor; import org.springframework.data.repository.query.Parameters; import org.springframework.data.repository.query.ParametersParameterAccessor; @@ -87,7 +89,7 @@ public abstract class JpaQueryExecution { protected abstract Object doExecute(AbstractJpaQuery query, Object[] values); /** - * Executes the {@link AbstractStringBasedJpaQuery} to return a simple collection of entities. + * Executes the query to return a simple collection of entities. */ static class CollectionExecution extends JpaQueryExecution { @@ -97,6 +99,47 @@ public abstract class JpaQueryExecution { } } + /** + * Executes the query to return a {@link Slice} of entities. + * + * @author Oliver Gierke + * @since 1.6 + */ + static class SlicedExecution extends JpaQueryExecution { + + private final Parameters parameters; + + /** + * Creates a new {@link SlicedExecution} using the given {@link Parameters}. + * + * @param parameters must not be {@literal null}. + */ + public SlicedExecution(Parameters parameters) { + this.parameters = parameters; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.jpa.repository.query.JpaQueryExecution#doExecute(org.springframework.data.jpa.repository.query.AbstractJpaQuery, java.lang.Object[]) + */ + @Override + @SuppressWarnings("unchecked") + protected Object doExecute(AbstractJpaQuery query, Object[] values) { + + ParametersParameterAccessor accessor = new ParametersParameterAccessor(parameters, values); + Pageable pageable = accessor.getPageable(); + + Query createQuery = query.createQuery(values); + int pageSize = pageable.getPageSize(); + createQuery.setMaxResults(pageSize + 1); + + List resultList = createQuery.getResultList(); + boolean hasNext = resultList.size() > pageSize; + + return new SliceImpl(hasNext ? resultList.subList(0, pageSize) : resultList, pageable, hasNext); + } + } + /** * Executes the {@link AbstractStringBasedJpaQuery} to return a {@link org.springframework.data.domain.Page} of * entities. diff --git a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java index 448aba7df..e5f73e188 100644 --- a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2011 the original author or authors. + * Copyright 2008-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package org.springframework.data.jpa.repository; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; +import static org.springframework.data.domain.Sort.Direction.*; import java.util.Arrays; import java.util.List; @@ -27,8 +28,8 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Slice; import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Direction; import org.springframework.data.jpa.domain.sample.User; import org.springframework.data.jpa.repository.sample.UserRepository; import org.springframework.data.repository.query.QueryLookupStrategy; @@ -47,8 +48,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional public class UserRepositoryFinderTests { - @Autowired - UserRepository userRepository; + @Autowired UserRepository userRepository; User dave, carter, oliver; @@ -136,6 +136,9 @@ public class UserRepositoryFinderTests { assertThat(result.get(0), is(oliver)); } + /** + * @see DATAJPA-92 + */ @Test public void findsByLastnameIgnoringCase() throws Exception { List result = userRepository.findByLastnameIgnoringCase("BeAUfoRd"); @@ -143,6 +146,9 @@ public class UserRepositoryFinderTests { assertThat(result.get(0), is(carter)); } + /** + * @see DATAJPA-92 + */ @Test public void findsByLastnameIgnoringCaseLike() throws Exception { List result = userRepository.findByLastnameIgnoringCaseLike("BeAUfo%"); @@ -150,6 +156,9 @@ public class UserRepositoryFinderTests { assertThat(result.get(0), is(carter)); } + /** + * @see DATAJPA-92 + */ @Test public void findByLastnameAndFirstnameAllIgnoringCase() throws Exception { List result = userRepository.findByLastnameAndFirstnameAllIgnoringCase("MaTTheWs", "DaVe"); @@ -157,12 +166,15 @@ public class UserRepositoryFinderTests { assertThat(result.get(0), is(dave)); } + /** + * @see DATAJPA-94 + */ @Test public void respectsPageableOrderOnQueryGenerateFromMethodName() throws Exception { - Page ascending = userRepository.findByLastnameIgnoringCase(new PageRequest(0, 10, new Sort(Direction.ASC, - "firstname")), "Matthews"); - Page descending = userRepository.findByLastnameIgnoringCase(new PageRequest(0, 10, new Sort(Direction.DESC, - "firstname")), "Matthews"); + Page ascending = userRepository.findByLastnameIgnoringCase( + new PageRequest(0, 10, new Sort(ASC, "firstname")), "Matthews"); + Page descending = userRepository.findByLastnameIgnoringCase(new PageRequest(0, 10, + new Sort(DESC, "firstname")), "Matthews"); assertThat(ascending.getTotalElements(), is(2L)); assertThat(descending.getTotalElements(), is(2L)); assertThat(ascending.getContent().get(0).getFirstname(), is(not(equalTo(descending.getContent().get(0) @@ -171,4 +183,15 @@ public class UserRepositoryFinderTests { assertThat(ascending.getContent().get(1).getFirstname(), is(equalTo(descending.getContent().get(0).getFirstname()))); } + /** + * @see DATAJPA-486 + */ + @Test + public void executesQueryToSlice() { + + Slice slice = userRepository.findSliceByLastname("Matthews", new PageRequest(0, 1, ASC, "firstname")); + + assertThat(slice.getContent(), hasItem(dave)); + assertThat(slice.hasNext(), is(true)); + } } diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java b/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java index 5510f80f8..da2225cc8 100644 --- a/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java +++ b/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java @@ -23,6 +23,7 @@ import javax.persistence.QueryHint; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.sample.SpecialUser; import org.springframework.data.jpa.domain.sample.User; @@ -305,4 +306,9 @@ public interface UserRepository extends JpaRepository, JpaSpecifi * @see DATAJPA-454 */ List findByBinaryData(byte[] data); + + /** + * @see DATAJPA-486 + */ + Slice findSliceByLastname(String lastname, Pageable pageable); }