From 1805aad718ada5f0991350f38d5a8443b4a68542 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Fri, 28 Feb 2014 14:44:15 +0100 Subject: [PATCH] DATACMNS-387 - Introduce Slice abstraction. Introduced a Slice interface which is effectively a Page without the total page information. This will allow store modules to implement slice-by-slice access without having to calculate a total number of elements. Extracted content and Pageable related functionality into a common Chunk class which serves as base class for the two concrete implementations SliceImpl and PageImpl. Deprecated hasNextPage() etc. on Page in favor of the generic hasNext() methods. QueryMethod now exposes an isSliceQuery() to indicate a query method will need sliced execution. --- .../springframework/data/domain/Chunk.java | 191 ++++++++++++++++++ .../org/springframework/data/domain/Page.java | 81 +------- .../springframework/data/domain/PageImpl.java | 128 +++--------- .../springframework/data/domain/Slice.java | 116 +++++++++++ .../data/domain/SliceImpl.java | 114 +++++++++++ .../data/repository/query/QueryMethod.java | 18 +- .../data/web/PagedResourcesAssembler.java | 4 +- .../data/domain/PageImplUnitTests.java | 52 ++--- .../query/QueryMethodUnitTests.java | 66 ++++-- 9 files changed, 549 insertions(+), 221 deletions(-) create mode 100644 src/main/java/org/springframework/data/domain/Chunk.java create mode 100644 src/main/java/org/springframework/data/domain/Slice.java create mode 100644 src/main/java/org/springframework/data/domain/SliceImpl.java diff --git a/src/main/java/org/springframework/data/domain/Chunk.java b/src/main/java/org/springframework/data/domain/Chunk.java new file mode 100644 index 000000000..7a5a8d310 --- /dev/null +++ b/src/main/java/org/springframework/data/domain/Chunk.java @@ -0,0 +1,191 @@ +/* + * Copyright 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.domain; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.springframework.util.Assert; + +/** + * A chunk of data restricted by the configured {@link Pageable}. + * + * @author Oliver Gierke + * @since 1.8 + */ +abstract class Chunk implements Slice, Serializable { + + private static final long serialVersionUID = 867755909294344406L; + + private final List content = new ArrayList(); + private final Pageable pageable; + + /** + * Creates a new {@link Chunk} with the given content and the given governing {@link Pageable}. + * + * @param content must not be {@literal null}. + * @param pageable can be {@literal null}. + */ + public Chunk(List content, Pageable pageable) { + + Assert.notNull(content, "Content must not be null!"); + + this.content.addAll(content); + this.pageable = pageable; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.domain.Slice#getNumber() + */ + public int getNumber() { + return pageable == null ? 0 : pageable.getPageNumber(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.domain.Slice#getSize() + */ + public int getSize() { + return pageable == null ? 0 : pageable.getPageSize(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.domain.Slice#getNumberOfElements() + */ + public int getNumberOfElements() { + return content.size(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.domain.Slice#hasPrevious() + */ + public boolean hasPrevious() { + return getNumber() > 0; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.domain.Slice#isFirst() + */ + public boolean isFirst() { + return !hasPrevious(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.domain.Slice#isLast() + */ + public boolean isLast() { + return !hasNext(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.domain.Slice#nextPageable() + */ + public Pageable nextPageable() { + return hasNext() ? pageable.next() : null; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.domain.Slice#previousPageable() + */ + public Pageable previousPageable() { + + if (hasPrevious()) { + return pageable.previousOrFirst(); + } + + return null; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.domain.Slice#hasContent() + */ + public boolean hasContent() { + return !content.isEmpty(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.domain.Slice#getContent() + */ + public List getContent() { + return Collections.unmodifiableList(content); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.domain.Slice#getSort() + */ + public Sort getSort() { + return pageable == null ? null : pageable.getSort(); + } + + /* + * (non-Javadoc) + * @see java.lang.Iterable#iterator() + */ + public Iterator iterator() { + return content.iterator(); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + + if (this == obj) { + return true; + } + + if (!(obj instanceof Chunk)) { + return false; + } + + Chunk that = (Chunk) obj; + + boolean contentEqual = this.content.equals(that.content); + boolean pageableEqual = this.pageable == null ? that.pageable == null : this.pageable.equals(that.pageable); + + return contentEqual && pageableEqual; + } + + /* + * (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + + int result = 17; + + result += 31 * (pageable == null ? 0 : pageable.hashCode()); + result += 31 * content.hashCode(); + + return result; + } +} diff --git a/src/main/java/org/springframework/data/domain/Page.java b/src/main/java/org/springframework/data/domain/Page.java index 393913b2a..51c64e978 100644 --- a/src/main/java/org/springframework/data/domain/Page.java +++ b/src/main/java/org/springframework/data/domain/Page.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. @@ -15,9 +15,6 @@ */ package org.springframework.data.domain; -import java.util.Iterator; -import java.util.List; - /** * A page is a sublist of a list of objects. It allows gain information about the position of it in the containing * entire list. @@ -25,21 +22,7 @@ import java.util.List; * @param * @author Oliver Gierke */ -public interface Page extends Iterable { - - /** - * Returns the number of the current page. Is always non-negative and less that {@code Page#getTotalPages()}. - * - * @return the number of the current page - */ - int getNumber(); - - /** - * Returns the size of the page. - * - * @return the size of the page - */ - int getSize(); +public interface Page extends Slice { /** * Returns the number of total pages. @@ -48,13 +31,6 @@ public interface Page extends Iterable { */ int getTotalPages(); - /** - * Returns the number of elements currently on this page. - * - * @return the number of elements currently on this page - */ - int getNumberOfElements(); - /** * Returns the total amount of elements. * @@ -65,73 +41,36 @@ public interface Page extends Iterable { /** * Returns if there is a previous page. * + * @deprecated use {@link #hasPrevious()} instead. * @return if there is a previous page */ + @Deprecated boolean hasPreviousPage(); /** * Returns whether the current page is the first one. * + * @deprecated use {@link #isFirst()} instead. * @return */ + @Deprecated boolean isFirstPage(); /** * Returns if there is a next page. * + * @deprecated use {@link #hasNext()} instead. * @return if there is a next page */ + @Deprecated boolean hasNextPage(); /** * Returns whether the current page is the last one. * + * @deprecated use {@link #isLast()} instead. * @return */ + @Deprecated boolean isLastPage(); - - /** - * Returns the {@link Pageable} to request the next {@link Page}. Can be {@literal null} in case the current - * {@link Page} is already the last one. Clients should check {@link #hasNextPage()} before calling this method to - * make sure they receive a non-{@literal null} value. - * - * @return - */ - Pageable nextPageable(); - - /** - * Returns the {@link Pageable} to request the previous page. Can be {@literal null} in case the current {@link Page} - * is already the first one. Clients should check {@link #hasPreviousPage()} before calling this method make sure - * receive a non-{@literal null} value. - * - * @return - */ - Pageable previousPageable(); - - /* - * (non-Javadoc) - * @see java.lang.Iterable#iterator() - */ - Iterator iterator(); - - /** - * Returns the page content as {@link List}. - * - * @return - */ - List getContent(); - - /** - * Returns whether the {@link Page} has content at all. - * - * @return - */ - boolean hasContent(); - - /** - * Returns the sorting parameters for the page. - * - * @return - */ - Sort getSort(); } diff --git a/src/main/java/org/springframework/data/domain/PageImpl.java b/src/main/java/org/springframework/data/domain/PageImpl.java index 513248d74..9f63902d9 100644 --- a/src/main/java/org/springframework/data/domain/PageImpl.java +++ b/src/main/java/org/springframework/data/domain/PageImpl.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. @@ -15,10 +15,6 @@ */ package org.springframework.data.domain; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; import java.util.List; /** @@ -27,12 +23,10 @@ import java.util.List; * @param the type of which the page consists. * @author Oliver Gierke */ -public class PageImpl implements Page, Serializable { +public class PageImpl extends Chunk implements Page { private static final long serialVersionUID = 867755909294344406L; - private final List content = new ArrayList(); - private final Pageable pageable; private final long total; /** @@ -44,13 +38,8 @@ public class PageImpl implements Page, Serializable { */ public PageImpl(List content, Pageable pageable, long total) { - if (null == content) { - throw new IllegalArgumentException("Content must not be null!"); - } - - this.content.addAll(content); + super(content, pageable); this.total = total; - this.pageable = pageable; } /** @@ -63,22 +52,6 @@ public class PageImpl implements Page, Serializable { this(content, null, null == content ? 0 : content.size()); } - /* - * (non-Javadoc) - * @see org.springframework.data.domain.Page#getNumber() - */ - public int getNumber() { - return pageable == null ? 0 : pageable.getPageNumber(); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.domain.Page#getSize() - */ - public int getSize() { - return pageable == null ? 0 : pageable.getPageSize(); - } - /* * (non-Javadoc) * @see org.springframework.data.domain.Page#getTotalPages() @@ -87,14 +60,6 @@ public class PageImpl implements Page, Serializable { return getSize() == 0 ? 1 : (int) Math.ceil((double) total / (double) getSize()); } - /* - * (non-Javadoc) - * @see org.springframework.data.domain.Page#getNumberOfElements() - */ - public int getNumberOfElements() { - return content.size(); - } - /* * (non-Javadoc) * @see org.springframework.data.domain.Page#getTotalElements() @@ -108,7 +73,7 @@ public class PageImpl implements Page, Serializable { * @see org.springframework.data.domain.Page#hasPreviousPage() */ public boolean hasPreviousPage() { - return getNumber() > 0; + return hasPrevious(); } /* @@ -116,7 +81,16 @@ public class PageImpl implements Page, Serializable { * @see org.springframework.data.domain.Page#isFirstPage() */ public boolean isFirstPage() { - return !hasPreviousPage(); + return isFirst(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.domain.Slice#hasNext() + */ + @Override + public boolean hasNext() { + return hasNextPage(); } /* @@ -127,6 +101,15 @@ public class PageImpl implements Page, Serializable { return getNumber() + 1 < getTotalPages(); } + /* + * (non-Javadoc) + * @see org.springframework.data.domain.Slice#isLast() + */ + @Override + public boolean isLast() { + return isLastPage(); + } + /* * (non-Javadoc) * @see org.springframework.data.domain.Page#isLastPage() @@ -135,59 +118,6 @@ public class PageImpl implements Page, Serializable { return !hasNextPage(); } - /* - * (non-Javadoc) - * @see org.springframework.data.domain.Page#nextPageable() - */ - public Pageable nextPageable() { - return hasNextPage() ? pageable.next() : null; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.domain.Page#previousOrFirstPageable() - */ - public Pageable previousPageable() { - - if (hasPreviousPage()) { - return pageable.previousOrFirst(); - } - - return null; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.domain.Page#iterator() - */ - public Iterator iterator() { - return content.iterator(); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.domain.Page#getContent() - */ - public List getContent() { - return Collections.unmodifiableList(content); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.domain.Page#hasContent() - */ - public boolean hasContent() { - return !content.isEmpty(); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.domain.Page#getSort() - */ - public Sort getSort() { - return pageable == null ? null : pageable.getSort(); - } - /* * (non-Javadoc) * @see java.lang.Object#toString() @@ -196,6 +126,7 @@ public class PageImpl implements Page, Serializable { public String toString() { String contentType = "UNKNOWN"; + List content = getContent(); if (content.size() > 0) { contentType = content.get(0).getClass().getName(); @@ -221,11 +152,7 @@ public class PageImpl implements Page, Serializable { PageImpl that = (PageImpl) obj; - boolean totalEqual = this.total == that.total; - boolean contentEqual = this.content.equals(that.content); - boolean pageableEqual = this.pageable == null ? that.pageable == null : this.pageable.equals(that.pageable); - - return totalEqual && contentEqual && pageableEqual; + return this.total == that.total && super.equals(obj); } /* @@ -237,9 +164,8 @@ public class PageImpl implements Page, Serializable { int result = 17; - result = 31 * result + (int) (total ^ total >>> 32); - result = 31 * result + (pageable == null ? 0 : pageable.hashCode()); - result = 31 * result + content.hashCode(); + result += 31 * (int) (total ^ total >>> 32); + result += 31 * super.hashCode(); return result; } diff --git a/src/main/java/org/springframework/data/domain/Slice.java b/src/main/java/org/springframework/data/domain/Slice.java new file mode 100644 index 000000000..213de1956 --- /dev/null +++ b/src/main/java/org/springframework/data/domain/Slice.java @@ -0,0 +1,116 @@ +/* + * Copyright 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.domain; + +import java.util.List; + +/** + * A slice of data that indicates whether there's a next or previous slice available. Allows to obtain a + * {@link Pageable} to request a previous or next {@link Slice}. + * + * @author Oliver Gierke + * @since 1.8 + */ +public interface Slice extends Iterable { + + /** + * Returns the number of the current {@link Slice}. Is always non-negative. + * + * @return the number of the current {@link Slice}. + */ + int getNumber(); + + /** + * Returns the size of the {@link Slice}. + * + * @return the size of the {@link Slice}. + */ + int getSize(); + + /** + * Returns the number of elements currently on this {@link Slice}. + * + * @return the number of elements currently on this {@link Slice}. + */ + int getNumberOfElements(); + + /** + * Returns the page content as {@link List}. + * + * @return + */ + List getContent(); + + /** + * Returns whether the {@link Slice} has content at all. + * + * @return + */ + boolean hasContent(); + + /** + * Returns the sorting parameters for the {@link Slice}. + * + * @return + */ + Sort getSort(); + + /** + * Returns whether the current {@link Slice} is the first one. + * + * @return + */ + boolean isFirst(); + + /** + * Returns whether the current {@link Slice} is the last one. + * + * @return + */ + boolean isLast(); + + /** + * Returns if there is a next {@link Slice}. + * + * @return if there is a next {@link Slice}. + */ + boolean hasNext(); + + /** + * Returns if there is a previous {@link Slice}. + * + * @return if there is a previous {@link Slice}. + */ + boolean hasPrevious(); + + /** + * Returns the {@link Pageable} to request the next {@link Slice}. Can be {@literal null} in case the current + * {@link Slice} is already the last one. Clients should check {@link #hasNext()} before calling this method to make + * sure they receive a non-{@literal null} value. + * + * @return + */ + Pageable nextPageable(); + + /** + * Returns the {@link Pageable} to request the previous {@link Slice}. Can be {@literal null} in case the current + * {@link Slice} is already the first one. Clients should check {@link #hasPrevious()} before calling this method make + * sure receive a non-{@literal null} value. + * + * @return + */ + Pageable previousPageable(); +} diff --git a/src/main/java/org/springframework/data/domain/SliceImpl.java b/src/main/java/org/springframework/data/domain/SliceImpl.java new file mode 100644 index 000000000..d66a9229a --- /dev/null +++ b/src/main/java/org/springframework/data/domain/SliceImpl.java @@ -0,0 +1,114 @@ +/* + * Copyright 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.domain; + +import java.util.List; + +/** + * Default implementation of {@link Slice}. + * + * @author Oliver Gierke + * @since 1.8 + */ +public class SliceImpl extends Chunk { + + private static final long serialVersionUID = 867755909294344406L; + + private final boolean hasNext; + + /** + * Creates a new {@link Slice} with the given content and {@link Pageable}. + * + * @param content the content of this {@link Slice}, must not be {@literal null}. + * @param pageable the paging information, can be {@literal null}. + * @param hasNext whether there's another slice following the current one. + */ + public SliceImpl(List content, Pageable pageable, boolean hasNext) { + + super(content, pageable); + this.hasNext = hasNext; + } + + /** + * Creates a new {@link SliceImpl} with the given content. This will result in the created {@link Slice} being + * identical to the entire {@link List}. + * + * @param content must not be {@literal null}. + */ + public SliceImpl(List content) { + this(content, null, false); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.domain.Slice#hasNext() + */ + public boolean hasNext() { + return hasNext; + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + + String contentType = "UNKNOWN"; + List content = getContent(); + + if (content.size() > 0) { + contentType = content.get(0).getClass().getName(); + } + + return String.format("Slice %s of %d containing %s instances", getNumber(), contentType); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + + if (this == obj) { + return true; + } + + if (!(obj instanceof SliceImpl)) { + return false; + } + + SliceImpl that = (SliceImpl) obj; + + return this.hasNext == that.hasNext && super.equals(obj); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + + int result = 17; + + result += 31 * (hasNext ? 1 : 0); + result += 31 * super.hashCode(); + + return result; + } +} diff --git a/src/main/java/org/springframework/data/repository/query/QueryMethod.java b/src/main/java/org/springframework/data/repository/query/QueryMethod.java index 69d536eb2..9896220bd 100644 --- a/src/main/java/org/springframework/data/repository/query/QueryMethod.java +++ b/src/main/java/org/springframework/data/repository/query/QueryMethod.java @@ -22,6 +22,7 @@ import java.util.List; 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.repository.core.EntityMetadata; import org.springframework.data.repository.core.RepositoryMetadata; @@ -59,7 +60,7 @@ public class QueryMethod { } if (hasParameterOfType(method, Pageable.class)) { - assertReturnTypeAssignable(method, Page.class, List.class); + assertReturnTypeAssignable(method, Slice.class, Page.class, List.class); if (hasParameterOfType(method, Sort.class)) { throw new IllegalStateException(String.format("Method must not have Pageable *and* Sort parameter. " + "Use sorting capabilities on Pageble instead! Offending method: %s", method.toString())); @@ -152,7 +153,20 @@ public class QueryMethod { public boolean isCollectionQuery() { Class returnType = method.getReturnType(); - return !isPageQuery() && org.springframework.util.ClassUtils.isAssignable(Iterable.class, returnType); + return !(isPageQuery() || isSliceQuery()) + && org.springframework.util.ClassUtils.isAssignable(Iterable.class, returnType); + } + + /** + * Returns whether the query method will return a {@link Slice}. + * + * @return + * @since 1.8 + */ + public boolean isSliceQuery() { + + Class returnType = method.getReturnType(); + return !isPageQuery() && org.springframework.util.ClassUtils.isAssignable(Slice.class, returnType); } /** diff --git a/src/main/java/org/springframework/data/web/PagedResourcesAssembler.java b/src/main/java/org/springframework/data/web/PagedResourcesAssembler.java index cf7f3ba18..669e73cea 100644 --- a/src/main/java/org/springframework/data/web/PagedResourcesAssembler.java +++ b/src/main/java/org/springframework/data/web/PagedResourcesAssembler.java @@ -155,11 +155,11 @@ public class PagedResourcesAssembler implements ResourceAssembler, Pa private PagedResources addPaginationLinks(PagedResources resources, Page page, String uri) { - if (page.hasNextPage()) { + if (page.hasNext()) { foo(resources, page.nextPageable(), uri, Link.REL_NEXT); } - if (page.hasPreviousPage()) { + if (page.hasPrevious()) { foo(resources, page.previousPageable(), uri, Link.REL_PREVIOUS); } diff --git a/src/test/java/org/springframework/data/domain/PageImplUnitTests.java b/src/test/java/org/springframework/data/domain/PageImplUnitTests.java index 220d8065c..f754e5673 100644 --- a/src/test/java/org/springframework/data/domain/PageImplUnitTests.java +++ b/src/test/java/org/springframework/data/domain/PageImplUnitTests.java @@ -1,17 +1,17 @@ /* - * Copyright 2008-2013 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. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.data.domain; @@ -71,12 +71,12 @@ public class PageImplUnitTests { Page page = new PageImpl(Arrays.asList(new Object()), new PageRequest(0, 1), 10); - assertThat(page.isFirstPage(), is(true)); - assertThat(page.hasPreviousPage(), is(false)); + assertThat(page.isFirst(), is(true)); + assertThat(page.hasPrevious(), is(false)); assertThat(page.previousPageable(), is(nullValue())); - assertThat(page.isLastPage(), is(false)); - assertThat(page.hasNextPage(), is(true)); + assertThat(page.isLast(), is(false)); + assertThat(page.hasNext(), is(true)); assertThat(page.nextPageable(), is((Pageable) new PageRequest(1, 1))); } @@ -85,12 +85,12 @@ public class PageImplUnitTests { Page page = new PageImpl(Arrays.asList(new Object()), new PageRequest(1, 1), 2); - assertThat(page.isFirstPage(), is(false)); - assertThat(page.hasPreviousPage(), is(true)); + assertThat(page.isFirst(), is(false)); + assertThat(page.hasPrevious(), is(true)); assertThat(page.previousPageable(), is((Pageable) new PageRequest(0, 1))); - assertThat(page.isLastPage(), is(true)); - assertThat(page.hasNextPage(), is(false)); + assertThat(page.isLast(), is(true)); + assertThat(page.hasNext(), is(false)); assertThat(page.nextPageable(), is(nullValue())); } @@ -107,10 +107,10 @@ public class PageImplUnitTests { assertThat(page.getSort(), is((Sort) null)); assertThat(page.getTotalElements(), is(0L)); assertThat(page.getTotalPages(), is(1)); - assertThat(page.hasNextPage(), is(false)); - assertThat(page.hasPreviousPage(), is(false)); - assertThat(page.isFirstPage(), is(true)); - assertThat(page.isLastPage(), is(true)); + assertThat(page.hasNext(), is(false)); + assertThat(page.hasPrevious(), is(false)); + assertThat(page.isFirst(), is(true)); + assertThat(page.isLast(), is(true)); assertThat(page.hasContent(), is(false)); } @@ -123,7 +123,7 @@ public class PageImplUnitTests { Page page = new PageImpl(Arrays.asList("a")); assertThat(page.getTotalPages(), is(1)); - assertThat(page.hasNextPage(), is(false)); - assertThat(page.hasPreviousPage(), is(false)); + assertThat(page.hasNext(), is(false)); + assertThat(page.hasPrevious(), is(false)); } } diff --git a/src/test/java/org/springframework/data/repository/query/QueryMethodUnitTests.java b/src/test/java/org/springframework/data/repository/query/QueryMethodUnitTests.java index 5375477c9..64a36b31e 100644 --- a/src/test/java/org/springframework/data/repository/query/QueryMethodUnitTests.java +++ b/src/test/java/org/springframework/data/repository/query/QueryMethodUnitTests.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. @@ -15,32 +15,32 @@ */ package org.springframework.data.repository.query; -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import java.io.Serializable; import java.lang.reflect.Method; import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.runners.MockitoJUnitRunner; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.RepositoryMetadata; +import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; /** * Unit tests for {@link QueryMethod}. * * @author Oliver Gierke */ -@RunWith(MockitoJUnitRunner.class) public class QueryMethodUnitTests { - @Mock - RepositoryMetadata metadata; + RepositoryMetadata metadata = new DefaultRepositoryMetadata(SampleRepository.class); + /** + * @see DATAJPA-59 + */ @Test(expected = IllegalStateException.class) public void rejectsPagingMethodWithInvalidReturnType() throws Exception { @@ -48,18 +48,27 @@ public class QueryMethodUnitTests { new QueryMethod(method, metadata); } + /** + * @see DATAJPA-59 + */ @Test(expected = IllegalArgumentException.class) public void rejectsPagingMethodWithoutPageable() throws Exception { Method method = SampleRepository.class.getMethod("pagingMethodWithoutPageable"); new QueryMethod(method, metadata); } + /** + * @see DATACMNS-64 + */ @Test public void setsUpSimpleQueryMethodCorrectly() throws Exception { Method method = SampleRepository.class.getMethod("findByUsername", String.class); new QueryMethod(method, metadata); } + /** + * @see DATACMNS-61 + */ @Test public void considersIterableMethodForCollectionQuery() throws Exception { Method method = SampleRepository.class.getMethod("sampleMethod"); @@ -67,6 +76,9 @@ public class QueryMethodUnitTests { assertThat(queryMethod.isCollectionQuery(), is(true)); } + /** + * @see DATACMNS-67 + */ @Test public void doesNotConsiderPageMethodCollectionQuery() throws Exception { Method method = SampleRepository.class.getMethod("anotherSampleMethod", Pageable.class); @@ -75,33 +87,47 @@ public class QueryMethodUnitTests { assertThat(queryMethod.isCollectionQuery(), is(false)); } + /** + * @see DATACMNS-171 + */ @Test - @SuppressWarnings({ "unchecked", "rawtypes" }) public void detectsAnEntityBeingReturned() throws Exception { - when(metadata.getDomainType()).thenReturn((Class) User.class); - when(metadata.getReturnedDomainClass(Mockito.any(Method.class))).thenReturn((Class) SpecialUser.class); - Method method = SampleRepository.class.getMethod("returnsEntitySubclass"); QueryMethod queryMethod = new QueryMethod(method, metadata); assertThat(queryMethod.isQueryForEntity(), is(true)); } + /** + * @see DATACMNS-171 + */ @Test - @SuppressWarnings({ "unchecked", "rawtypes" }) public void detectsNonEntityBeingReturned() throws Exception { - when(metadata.getDomainType()).thenReturn((Class) User.class); - when(metadata.getReturnedDomainClass(Mockito.any(Method.class))).thenReturn((Class) Integer.class); - Method method = SampleRepository.class.getMethod("returnsProjection"); QueryMethod queryMethod = new QueryMethod(method, metadata); assertThat(queryMethod.isQueryForEntity(), is(false)); } - interface SampleRepository { + /** + * @see DATACMNS-397 + */ + @Test + public void detectsSliceMethod() throws Exception { + + RepositoryMetadata repositoryMetadata = new DefaultRepositoryMetadata(SampleRepository.class); + Method method = SampleRepository.class.getMethod("sliceOfUsers"); + QueryMethod queryMethod = new QueryMethod(method, repositoryMetadata); + + assertThat(queryMethod.isSliceQuery(), is(true)); + assertThat(queryMethod.isCollectionQuery(), is(false)); + assertThat(queryMethod.isPageQuery(), is(false)); + } + + interface SampleRepository extends Repository { + String pagingMethodWithInvalidReturnType(Pageable pageable); Page pagingMethodWithoutPageable(); @@ -115,6 +141,8 @@ public class QueryMethodUnitTests { SpecialUser returnsEntitySubclass(); Integer returnsProjection(); + + Slice sliceOfUsers(); } class User {