15
15
*/
16
16
package example .springdata .jpa .pagination ;
17
17
18
- import static org .assertj .core .api .AssertionsForClassTypes .*;
18
+ import static org .assertj .core .api .Assertions .*;
19
+
20
+ import jakarta .persistence .EntityManager ;
19
21
20
22
import java .util .List ;
21
23
import java .util .Random ;
24
26
import java .util .stream .Collectors ;
25
27
import java .util .stream .IntStream ;
26
28
27
- import com .github .javafaker .Faker ;
28
- import jakarta .persistence .EntityManager ;
29
29
import org .junit .jupiter .api .BeforeEach ;
30
30
import org .junit .jupiter .api .Test ;
31
+
31
32
import org .springframework .beans .factory .annotation .Autowired ;
32
33
import org .springframework .boot .autoconfigure .EnableAutoConfiguration ;
33
- import org .springframework .boot .test .context . SpringBootTest ;
34
+ import org .springframework .boot .test .autoconfigure . orm . jpa . DataJpaTest ;
34
35
import org .springframework .context .annotation .Configuration ;
35
36
import org .springframework .data .domain .KeysetScrollPosition ;
36
37
import org .springframework .data .domain .OffsetScrollPosition ;
40
41
import org .springframework .data .domain .ScrollPosition ;
41
42
import org .springframework .data .domain .Slice ;
42
43
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 ;
44
48
45
49
/**
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}.
47
51
*
48
52
* @author Christoph Strobl
53
+ * @author Mark Paluch
49
54
*/
50
- @ SpringBootTest
51
- @ Transactional
55
+ @ DataJpaTest
52
56
class PaginationTests {
53
57
54
58
@ Configuration
@@ -57,8 +61,7 @@ static class Config {
57
61
58
62
}
59
63
60
- @ Autowired
61
- BookRepository books ;
64
+ @ Autowired BookRepository books ;
62
65
63
66
@ BeforeEach
64
67
void setUp () {
@@ -71,10 +74,9 @@ void setUp() {
71
74
}
72
75
73
76
/**
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.
78
80
*/
79
81
@ Test
80
82
void pageThroughResultsWithSkipAndLimit () {
@@ -93,9 +95,9 @@ void pageThroughResultsWithSkipAndLimit() {
93
95
94
96
/**
95
97
* 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()}
99
101
*/
100
102
@ Test
101
103
void sliceThroughResultsWithSkipAndLimit () {
@@ -133,11 +135,28 @@ void scrollThroughResultsWithSkipAndLimit() {
133
135
} while (window .hasNext ());
134
136
}
135
137
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
+
136
155
/**
137
156
* Scroll through the results using an index based approach where the {@link KeysetScrollPosition#getKeys() keyset}
138
157
* 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.
141
160
*/
142
161
@ Test
143
162
void scrollThroughResultsWithKeyset () {
@@ -155,8 +174,7 @@ void scrollThroughResultsWithKeyset() {
155
174
156
175
// --> Test Data
157
176
158
- @ Autowired
159
- EntityManager em ;
177
+ @ Autowired EntityManager em ;
160
178
161
179
private List <Author > createAuthors (Faker faker ) {
162
180
@@ -176,18 +194,17 @@ private List<Author> createAuthors(Faker faker) {
176
194
private List <Book > createBooks (Faker faker , List <Author > authors ) {
177
195
178
196
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 ());
192
209
}
193
210
}
0 commit comments