Skip to content

Commit b21a664

Browse files
committed
Polishing.
Add WindowIterator example. See #662
1 parent 1b8872a commit b21a664

File tree

4 files changed

+68
-54
lines changed

4 files changed

+68
-54
lines changed

jpa/example/src/main/java/example/springdata/jpa/pagination/Book.java

+4-6
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@
1515
*/
1616
package example.springdata.jpa.pagination;
1717

18-
import java.util.Date;
19-
2018
import jakarta.persistence.Entity;
2119
import jakarta.persistence.Id;
2220
import jakarta.persistence.ManyToOne;
2321
import jakarta.persistence.Table;
2422
import lombok.Data;
2523

24+
import java.util.Date;
25+
2626
/**
2727
* @author Christoph Strobl
2828
*/
@@ -31,12 +31,10 @@
3131
@Table(name = "books")
3232
public class Book {
3333

34-
@Id
35-
private String id;
34+
@Id private String id;
3635
private String title;
3736
private String isbn10;
3837
private Date publicationDate;
3938

40-
@ManyToOne
41-
Author author;
39+
@ManyToOne Author author;
4240
}

jpa/example/src/main/java/example/springdata/jpa/pagination/BookRepository.java

+11-10
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@
2828
public interface BookRepository extends ListCrudRepository<Book, String> {
2929

3030
/**
31-
* Uses an {@literal offset} based pagination that first sorts the entries by their {@link Book#getPublicationDate() publication_date}
32-
* and then limits the result by dropping the number of rows specified in the {@link Pageable#getOffset() offset} clause.
33-
* To retrieve {@link Page#getTotalElements()} an additional count query is executed.
31+
* Uses an {@literal offset} based pagination that first sorts the entries by their {@link Book#getPublicationDate()
32+
* publication_date} and then limits the result by dropping the number of rows specified in the
33+
* {@link Pageable#getOffset() offset} clause. To retrieve {@link Page#getTotalElements()} an additional count query
34+
* is executed.
3435
*
3536
* @param title
3637
* @param pageable
@@ -39,8 +40,9 @@ public interface BookRepository extends ListCrudRepository<Book, String> {
3940
Page<Book> findByTitleContainsOrderByPublicationDate(String title, Pageable pageable);
4041

4142
/**
42-
* Uses an {@literal offset} based slicing that first sorts the entries by their {@link Book#getPublicationDate() publication_date}
43-
* and then limits the result by dropping the number of rows specified in the {@link Pageable#getOffset() offset} clause.
43+
* Uses an {@literal offset} based slicing that first sorts the entries by their {@link Book#getPublicationDate()
44+
* publication_date} and then limits the result by dropping the number of rows specified in the
45+
* {@link Pageable#getOffset() offset} clause.
4446
*
4547
* @param title
4648
* @param pageable
@@ -49,11 +51,10 @@ public interface BookRepository extends ListCrudRepository<Book, String> {
4951
Slice<Book> findBooksByTitleContainsOrderByPublicationDate(String title, Pageable pageable);
5052

5153
/**
52-
* Depending on the provided {@link ScrollPosition} either {@link org.springframework.data.domain.OffsetScrollPosition offset}
53-
* or {@link org.springframework.data.domain.KeysetScrollPosition keyset} scrolling is possible.
54-
* Scrolling through results requires a stable {@link org.springframework.data.domain.Sort} which is different from
55-
* what {@link Pageable#getSort()} offers.
56-
* The {@literal limit} is defined via the {@literal Top} keyword.
54+
* Depending on the provided {@link ScrollPosition} either {@link org.springframework.data.domain.OffsetScrollPosition
55+
* offset} or {@link org.springframework.data.domain.KeysetScrollPosition keyset} scrolling is possible. Scrolling
56+
* through results requires a stable {@link org.springframework.data.domain.Sort} which is different from what
57+
* {@link Pageable#getSort()} offers. The {@literal limit} is defined via the {@literal Top} keyword.
5758
*
5859
* @param title
5960
* @param scrollPosition

jpa/example/src/main/java/example/springdata/jpa/pagination/PagingRepoConfig.java

+2-4
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,5 @@
2222
* @author Christoph Strobl
2323
*/
2424
@SpringBootApplication
25-
@EnableJpaRepositories(repositoryBaseClass = BookRepository.class)
26-
class PagingRepoConfig {
27-
28-
}
25+
@EnableJpaRepositories(basePackageClasses = BookRepository.class)
26+
class PagingRepoConfig {}

jpa/example/src/test/java/example/springdata/jpa/pagination/PaginationTests.java

+51-34
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
*/
1616
package example.springdata.jpa.pagination;
1717

18-
import static org.assertj.core.api.AssertionsForClassTypes.*;
18+
import static org.assertj.core.api.Assertions.*;
19+
20+
import jakarta.persistence.EntityManager;
1921

2022
import java.util.List;
2123
import java.util.Random;
@@ -24,13 +26,12 @@
2426
import java.util.stream.Collectors;
2527
import java.util.stream.IntStream;
2628

27-
import com.github.javafaker.Faker;
28-
import jakarta.persistence.EntityManager;
2929
import org.junit.jupiter.api.BeforeEach;
3030
import org.junit.jupiter.api.Test;
31+
3132
import org.springframework.beans.factory.annotation.Autowired;
3233
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
33-
import org.springframework.boot.test.context.SpringBootTest;
34+
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
3435
import org.springframework.context.annotation.Configuration;
3536
import org.springframework.data.domain.KeysetScrollPosition;
3637
import org.springframework.data.domain.OffsetScrollPosition;
@@ -40,15 +41,18 @@
4041
import org.springframework.data.domain.ScrollPosition;
4142
import org.springframework.data.domain.Slice;
4243
import org.springframework.data.domain.Window;
43-
import org.springframework.transaction.annotation.Transactional;
44+
import org.springframework.data.domain.WindowIterator;
45+
import org.springframework.data.util.Streamable;
46+
47+
import com.github.javafaker.Faker;
4448

4549
/**
46-
* Show different types of paging styles using {@link Page}, {@link org.springframework.data.domain.Slice} and {@link Window}
50+
* Show different types of paging styles using {@link Page}, {@link Slice} and {@link Window}.
4751
*
4852
* @author Christoph Strobl
53+
* @author Mark Paluch
4954
*/
50-
@SpringBootTest
51-
@Transactional
55+
@DataJpaTest
5256
class PaginationTests {
5357

5458
@Configuration
@@ -57,8 +61,7 @@ static class Config {
5761

5862
}
5963

60-
@Autowired
61-
BookRepository books;
64+
@Autowired BookRepository books;
6265

6366
@BeforeEach
6467
void setUp() {
@@ -71,10 +74,9 @@ void setUp() {
7174
}
7275

7376
/**
74-
* Page through the results using an offset/limit approach where the server skips over the number of results
75-
* specified via {@link Pageable#getOffset()}.
76-
* The {@link Page} return type will run an additional {@literal count} query to read the total number of matching rows
77-
* on each request.
77+
* Page through the results using an offset/limit approach where the server skips over the number of results specified
78+
* via {@link Pageable#getOffset()}. The {@link Page} return type will run an additional {@literal count} query to
79+
* read the total number of matching rows on each request.
7880
*/
7981
@Test
8082
void pageThroughResultsWithSkipAndLimit() {
@@ -93,9 +95,9 @@ void pageThroughResultsWithSkipAndLimit() {
9395

9496
/**
9597
* Run through the results using an offset/limit approach where the server skips over the number of results specified
96-
* via {@link Pageable#getOffset()}.
97-
* No additional {@literal count} query to read the total number of matching rows is issued. Still {@link Slice} requests,
98-
* but does not emit, one row more than specified via {@link Page#getSize()} to feed {@link Slice#hasNext()}
98+
* via {@link Pageable#getOffset()}. No additional {@literal count} query to read the total number of matching rows is
99+
* issued. Still {@link Slice} requests, but does not emit, one row more than specified via {@link Page#getSize()} to
100+
* feed {@link Slice#hasNext()}
99101
*/
100102
@Test
101103
void sliceThroughResultsWithSkipAndLimit() {
@@ -133,11 +135,28 @@ void scrollThroughResultsWithSkipAndLimit() {
133135
} while (window.hasNext());
134136
}
135137

138+
/**
139+
* Scroll through the results using an offset/limit approach where the server skips over the number of results
140+
* specified via {@link OffsetScrollPosition#getOffset()} using {@link WindowIterator}.
141+
* <p>
142+
* This approach is similar to the {@link #sliceThroughResultsWithSkipAndLimit() slicing one}.
143+
*/
144+
@Test
145+
void scrollThroughResultsUsingWindowIteratorWithSkipAndLimit() {
146+
147+
WindowIterator<Book> iterator = WindowIterator
148+
.of(scrollPosition -> books.findTop2ByTitleContainsOrderByPublicationDate("the-crazy-book-", scrollPosition))
149+
.startingAt(OffsetScrollPosition.initial());
150+
151+
List<Book> allBooks = Streamable.of(() -> iterator).stream().toList();
152+
assertThat(allBooks).hasSize(50);
153+
}
154+
136155
/**
137156
* Scroll through the results using an index based approach where the {@link KeysetScrollPosition#getKeys() keyset}
138157
* keeps track of already seen values to resume scrolling by altering the where clause to only return rows after the
139-
* values contained in the keyset.
140-
* Set {@literal logging.level.org.hibernate.SQL=debug} to show the modified query in the log.
158+
* values contained in the keyset. Set {@literal logging.level.org.hibernate.SQL=debug} to show the modified query in
159+
* the log.
141160
*/
142161
@Test
143162
void scrollThroughResultsWithKeyset() {
@@ -155,8 +174,7 @@ void scrollThroughResultsWithKeyset() {
155174

156175
// --> Test Data
157176

158-
@Autowired
159-
EntityManager em;
177+
@Autowired EntityManager em;
160178

161179
private List<Author> createAuthors(Faker faker) {
162180

@@ -176,18 +194,17 @@ private List<Author> createAuthors(Faker faker) {
176194
private List<Book> createBooks(Faker faker, List<Author> authors) {
177195

178196
Random rand = new Random();
179-
return IntStream.range(0, 100)
180-
.mapToObj(id -> {
181-
182-
Book book = new Book();
183-
book.setId("book-%03d".formatted(id));
184-
book.setTitle(faker.book().title());
185-
book.setIsbn10(UUID.randomUUID().toString().substring(0, 10));
186-
book.setPublicationDate(faker.date().past(5000, TimeUnit.DAYS));
187-
book.setAuthor(authors.get(rand.nextInt(authors.size())));
188-
189-
em.persist(book);
190-
return book;
191-
}).collect(Collectors.toList());
197+
return IntStream.range(0, 100).mapToObj(id -> {
198+
199+
Book book = new Book();
200+
book.setId("book-%03d".formatted(id));
201+
book.setTitle((id % 2 == 0 ? "the-crazy-book-" : "") + faker.book().title());
202+
book.setIsbn10(UUID.randomUUID().toString().substring(0, 10));
203+
book.setPublicationDate(faker.date().past(5000, TimeUnit.DAYS));
204+
book.setAuthor(authors.get(rand.nextInt(authors.size())));
205+
206+
em.persist(book);
207+
return book;
208+
}).collect(Collectors.toList());
192209
}
193210
}

0 commit comments

Comments
 (0)