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.
This commit is contained in:
Oliver Gierke
2014-02-28 14:44:15 +01:00
parent 28dac21931
commit 1805aad718
9 changed files with 549 additions and 221 deletions

View File

@@ -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<T> implements Slice<T>, Serializable {
private static final long serialVersionUID = 867755909294344406L;
private final List<T> content = new ArrayList<T>();
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<T> 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<T> 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<T> 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;
}
}

View File

@@ -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 <T>
* @author Oliver Gierke
*/
public interface Page<T> extends Iterable<T> {
/**
* 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<T> extends Slice<T> {
/**
* Returns the number of total pages.
@@ -48,13 +31,6 @@ public interface Page<T> extends Iterable<T> {
*/
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<T> extends Iterable<T> {
/**
* 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<T> iterator();
/**
* Returns the page content as {@link List}.
*
* @return
*/
List<T> getContent();
/**
* Returns whether the {@link Page} has content at all.
*
* @return
*/
boolean hasContent();
/**
* Returns the sorting parameters for the page.
*
* @return
*/
Sort getSort();
}

View File

@@ -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 <T> the type of which the page consists.
* @author Oliver Gierke
*/
public class PageImpl<T> implements Page<T>, Serializable {
public class PageImpl<T> extends Chunk<T> implements Page<T> {
private static final long serialVersionUID = 867755909294344406L;
private final List<T> content = new ArrayList<T>();
private final Pageable pageable;
private final long total;
/**
@@ -44,13 +38,8 @@ public class PageImpl<T> implements Page<T>, Serializable {
*/
public PageImpl(List<T> 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<T> implements Page<T>, 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<T> implements Page<T>, 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<T> implements Page<T>, Serializable {
* @see org.springframework.data.domain.Page#hasPreviousPage()
*/
public boolean hasPreviousPage() {
return getNumber() > 0;
return hasPrevious();
}
/*
@@ -116,7 +81,16 @@ public class PageImpl<T> implements Page<T>, 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<T> implements Page<T>, 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<T> implements Page<T>, 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<T> iterator() {
return content.iterator();
}
/*
* (non-Javadoc)
* @see org.springframework.data.domain.Page#getContent()
*/
public List<T> 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<T> implements Page<T>, Serializable {
public String toString() {
String contentType = "UNKNOWN";
List<T> content = getContent();
if (content.size() > 0) {
contentType = content.get(0).getClass().getName();
@@ -221,11 +152,7 @@ public class PageImpl<T> implements Page<T>, 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<T> implements Page<T>, 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;
}

View File

@@ -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<T> extends Iterable<T> {
/**
* 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<T> 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();
}

View File

@@ -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<T> extends Chunk<T> {
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<T> 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<T> 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<T> 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;
}
}