diff --git a/build.gradle b/build.gradle index fc1cc9f80c..e75fcecfd9 100644 --- a/build.gradle +++ b/build.gradle @@ -455,6 +455,7 @@ project('spring-batch-infrastructure-tests') { testCompile("org.springframework:spring-oxm:$springVersion") { exclude group: 'commons-lang', module: 'commons-lang' } + testCompile "org.springframework.data:spring-data-jpa:$springDataJpaVersion" testCompile "org.springframework:spring-jdbc:$springVersion" testCompile "org.springframework:spring-test:$springVersion" testCompile "org.mockito:mockito-core:$mockitoVersion" diff --git a/spring-batch-infrastructure-tests/src/test/java/org/springframework/batch/item/database/RepositoryItemReaderIntegrationTests.java b/spring-batch-infrastructure-tests/src/test/java/org/springframework/batch/item/database/RepositoryItemReaderIntegrationTests.java new file mode 100644 index 0000000000..4f2280f53d --- /dev/null +++ b/spring-batch-infrastructure-tests/src/test/java/org/springframework/batch/item/database/RepositoryItemReaderIntegrationTests.java @@ -0,0 +1,116 @@ +/* + * Copyright 2016 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.batch.item.database; + +import static org.junit.Assert.assertEquals; + +import java.util.List; + +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.batch.item.ExecutionContext; +import org.springframework.batch.item.data.RepositoryItemReader; +import org.springframework.batch.item.sample.books.Author; +import org.springframework.batch.item.sample.books.Book; +import org.springframework.batch.item.sample.books.data.SimpleService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = "RepositoryItemReaderCommonTests-context.xml") +public class RepositoryItemReaderIntegrationTests { + + private static final String CONTEXT_KEY = "RepositoryItemReader.read.count"; + + @Autowired + private SimpleService service; + + @Autowired + private RepositoryItemReader reader; + + @After + public void reinitializeReader() { + reader.close(); + } + + @Test + public void testReadFromFirstPos() throws Exception { + service.openReader(new ExecutionContext()); + + final List books = service.nextAuthorBooks(); + + assertEquals("Books list size", 2, books.size()); + assertEquals("First book", "author 1 - book 1", books.get(0).getName()); + assertEquals("Second book", "author 1 - book 2", books.get(1).getName()); + } + + @Test + public void testReadFromWithinPage() throws Exception { + reader.setCurrentItemCount(1); + service.openReader(new ExecutionContext()); + + final List books = service.nextAuthorBooks(); + + assertEquals("Books list size", 2, books.size()); + assertEquals("First book", "author 2 - book 1", books.get(0).getName()); + assertEquals("Second book", "author 2 - book 2", books.get(1).getName()); + } + + @Test + public void testReadFromNewPage() throws Exception { + reader.setPageSize(2); + reader.setCurrentItemCount(2); // 3rd item = 1rst of page 2 + service.openReader(new ExecutionContext()); + + final List books = service.nextAuthorBooks(); + + assertEquals("Books list size", 2, books.size()); + assertEquals("First book", "author 3 - book 1", books.get(0).getName()); + assertEquals("Second book", "author 3 - book 2", books.get(1).getName()); + } + + @Test + public void testReadFromWithinPage_Restart() throws Exception { + final ExecutionContext executionContext = new ExecutionContext(); + executionContext.putInt(CONTEXT_KEY, 1); + service.openReader(executionContext); + + final List books = service.nextAuthorBooks(); + + assertEquals("Books list size", 2, books.size()); + assertEquals("First book", "author 2 - book 1", books.get(0).getName()); + assertEquals("Second book", "author 2 - book 2", books.get(1).getName()); + } + + @Test + public void testReadFromNewPage_Restart() throws Exception { + reader.setPageSize(2); + final ExecutionContext executionContext = new ExecutionContext(); + executionContext.putInt(CONTEXT_KEY, 2); + service.openReader(executionContext); + + final List books = service.nextAuthorBooks(); + + assertEquals("Books list size", 2, books.size()); + assertEquals("First book", "author 3 - book 1", books.get(0).getName()); + assertEquals("Second book", "author 3 - book 2", books.get(1).getName()); + } + +} diff --git a/spring-batch-infrastructure-tests/src/test/java/org/springframework/batch/item/sample/books/Author.java b/spring-batch-infrastructure-tests/src/test/java/org/springframework/batch/item/sample/books/Author.java new file mode 100644 index 0000000000..0672f5760f --- /dev/null +++ b/spring-batch-infrastructure-tests/src/test/java/org/springframework/batch/item/sample/books/Author.java @@ -0,0 +1,70 @@ +/* + * Copyright 2016 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.batch.item.sample.books; + +import javax.persistence.Basic; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.OneToMany; +import javax.persistence.Table; +import java.util.List; +import java.util.Objects; + +/** + * Basic domain object with a lazy one-to-many association. + * + * @author Antoine Kapps + */ +@Entity +@Table(name = "T_AUTHORS") +public class Author { + + @Id + private int id; + + @Basic + private String name; + + @OneToMany + @JoinColumn(name = "AUTHOR_ID") + private List books; + + public int getId() { + return id; + } + public String getName() { + return name; + } + public List getBooks() { + return books; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Author author = (Author) o; + return id == author.id && + Objects.equals(name, author.name) && + Objects.equals(books, author.books); + } + + @Override + public int hashCode() { + return Objects.hash(id, name, books); + } +} diff --git a/spring-batch-infrastructure-tests/src/test/java/org/springframework/batch/item/sample/books/Book.java b/spring-batch-infrastructure-tests/src/test/java/org/springframework/batch/item/sample/books/Book.java new file mode 100644 index 0000000000..c7e9dd17d4 --- /dev/null +++ b/spring-batch-infrastructure-tests/src/test/java/org/springframework/batch/item/sample/books/Book.java @@ -0,0 +1,56 @@ +/* + * Copyright 2016 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.batch.item.sample.books; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Objects; + +/** + * Simple domain object implied in an association with {@link Author}. + * + * @author Antoine Kapps + */ +@Entity +@Table(name = "T_BOOKS") +public class Book { + + @Id + private int id; + private String name; + + public int getId() { + return id; + } + public String getName() { + return name; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Book book = (Book) o; + return id == book.id && + Objects.equals(name, book.name); + } + + @Override + public int hashCode() { + return Objects.hash(id, name); + } +} diff --git a/spring-batch-infrastructure-tests/src/test/java/org/springframework/batch/item/sample/books/data/AuthorRepository.java b/spring-batch-infrastructure-tests/src/test/java/org/springframework/batch/item/sample/books/data/AuthorRepository.java new file mode 100644 index 0000000000..a1a8dd5706 --- /dev/null +++ b/spring-batch-infrastructure-tests/src/test/java/org/springframework/batch/item/sample/books/data/AuthorRepository.java @@ -0,0 +1,25 @@ +/* + * Copyright 2016 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.batch.item.sample.books.data; + +import org.springframework.batch.item.sample.books.Author; +import org.springframework.data.repository.PagingAndSortingRepository; +import org.springframework.stereotype.Repository; + + +@Repository +public interface AuthorRepository extends PagingAndSortingRepository { +} diff --git a/spring-batch-infrastructure-tests/src/test/java/org/springframework/batch/item/sample/books/data/SimpleService.java b/spring-batch-infrastructure-tests/src/test/java/org/springframework/batch/item/sample/books/data/SimpleService.java new file mode 100644 index 0000000000..7b5c51d58e --- /dev/null +++ b/spring-batch-infrastructure-tests/src/test/java/org/springframework/batch/item/sample/books/data/SimpleService.java @@ -0,0 +1,55 @@ +/* + * Copyright 2016 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.batch.item.sample.books.data; + +import javax.transaction.Transactional; +import java.util.ArrayList; +import java.util.List; + +import org.springframework.batch.item.ExecutionContext; +import org.springframework.batch.item.data.RepositoryItemReader; +import org.springframework.batch.item.sample.books.Author; +import org.springframework.batch.item.sample.books.Book; + +/** + * A simple service based upon a {@link RepositoryItemReader} + */ +public class SimpleService { + + private final RepositoryItemReader itemReader; + + public SimpleService(RepositoryItemReader itemReader) { + this.itemReader = itemReader; + } + + // Prepare the reader + public void openReader(ExecutionContext executionContext) throws Exception { + itemReader.open(executionContext); + } + + // Reads next Author and returns his (lazy-loaded) books, inside a transaction (simulates the chunk's transaction) + @Transactional + public List nextAuthorBooks() throws Exception { + List result = new ArrayList<>(); + + final Author nextAuthor = itemReader.read(); + if (nextAuthor != null) { + result.addAll(nextAuthor.getBooks()); + } + + return result; + } +} diff --git a/spring-batch-infrastructure-tests/src/test/resources/org/springframework/batch/item/database/RepositoryItemReaderCommonTests-context.xml b/spring-batch-infrastructure-tests/src/test/resources/org/springframework/batch/item/database/RepositoryItemReaderCommonTests-context.xml new file mode 100644 index 0000000000..3c4afdb79b --- /dev/null +++ b/spring-batch-infrastructure-tests/src/test/resources/org/springframework/batch/item/database/RepositoryItemReaderCommonTests-context.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + classpath:org/springframework/batch/item/database/init-books-schema.sql + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-batch-infrastructure-tests/src/test/resources/org/springframework/batch/item/database/init-books-schema.sql b/spring-batch-infrastructure-tests/src/test/resources/org/springframework/batch/item/database/init-books-schema.sql new file mode 100644 index 0000000000..2dc44ce4a8 --- /dev/null +++ b/spring-batch-infrastructure-tests/src/test/resources/org/springframework/batch/item/database/init-books-schema.sql @@ -0,0 +1,24 @@ +DROP TABLE T_BOOKS if exists; +DROP TABLE T_AUTHORS if exists; + +CREATE TABLE T_AUTHORS ( + ID BIGINT NOT NULL PRIMARY KEY, + NAME VARCHAR(45) +); + +CREATE TABLE T_BOOKS( + ID BIGINT NOT NULL PRIMARY KEY, + NAME VARCHAR(45), + AUTHOR_ID BIGINT NOT NULL +); + +INSERT INTO T_AUTHORS (id, name) VALUES (1, 'author 1'); +INSERT INTO T_AUTHORS (id, name) VALUES (2, 'author 2'); +INSERT INTO T_AUTHORS (id, name) VALUES (3, 'author 3'); + +INSERT INTO T_BOOKS (id, name, author_id) VALUES (1, 'author 1 - book 1', 1); +INSERT INTO T_BOOKS (id, name, author_id) VALUES (2, 'author 1 - book 2', 1); +INSERT INTO T_BOOKS (id, name, author_id) VALUES (3, 'author 2 - book 1', 2); +INSERT INTO T_BOOKS (id, name, author_id) VALUES (4, 'author 2 - book 2', 2); +INSERT INTO T_BOOKS (id, name, author_id) VALUES (5, 'author 3 - book 1', 3); +INSERT INTO T_BOOKS (id, name, author_id) VALUES (6, 'author 3 - book 2', 3); diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/RepositoryItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/RepositoryItemReader.java index ea59a4b298..9e4be936f5 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/RepositoryItemReader.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/RepositoryItemReader.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 the original author or authors. + * Copyright 2012-2016 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. @@ -60,6 +60,7 @@ *

* * @author Michael Minella + * @author Antoine Kapps * @since 2.2 */ public class RepositoryItemReader extends AbstractItemCountingItemStreamItemReader implements InitializingBean { @@ -80,7 +81,7 @@ public class RepositoryItemReader extends AbstractItemCountingItemStreamItemR private volatile List results; - private Object lock = new Object(); + private final Object lock = new Object(); private String methodName; @@ -144,20 +145,24 @@ public void afterPropertiesSet() throws Exception { protected T doRead() throws Exception { synchronized (lock) { - if(results == null || current >= results.size()) { + boolean nextPageNeeded = (results != null && current >= results.size()); + + if (results == null || nextPageNeeded) { if (logger.isDebugEnabled()) { logger.debug("Reading page " + page); } results = doPageRead(); - - current = 0; page ++; if(results.size() <= 0) { return null; } + + if (nextPageNeeded) { + current = 0; + } } if(current < results.size()) { @@ -174,11 +179,8 @@ protected T doRead() throws Exception { @Override protected void jumpToItem(int itemLastIndex) throws Exception { synchronized (lock) { - page = (itemLastIndex - 1) / pageSize; - current = (itemLastIndex - 1) % pageSize; - - results = doPageRead(); - page++; + page = itemLastIndex / pageSize; + current = itemLastIndex % pageSize; } } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/data/RepositoryItemReaderTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/data/RepositoryItemReaderTests.java index 83031a8523..556fe05064 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/data/RepositoryItemReaderTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/data/RepositoryItemReaderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2014 the original author or authors. + * Copyright 2013-2016 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,16 +15,23 @@ */ package org.springframework.batch.item.data; +import static java.util.Collections.singletonList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.ArrayList; -import java.util.HashMap; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Map; import org.junit.Before; @@ -53,9 +60,8 @@ public class RepositoryItemReaderTests { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - sorts = new HashMap(); - sorts.put("id", Direction.ASC); - reader = new RepositoryItemReader(); + sorts = Collections.singletonMap("id", Direction.ASC); + reader = new RepositoryItemReader<>(); reader.setRepository(repository); reader.setPageSize(1); reader.setSort(sorts); @@ -65,38 +71,42 @@ public void setUp() throws Exception { @Test public void testAfterPropertiesSet() throws Exception { try { - new RepositoryItemReader().afterPropertiesSet(); + new RepositoryItemReader<>().afterPropertiesSet(); fail(); } catch (IllegalStateException e) { + // expected } try { - reader = new RepositoryItemReader(); + reader = new RepositoryItemReader<>(); reader.setRepository(repository); reader.afterPropertiesSet(); fail(); } catch (IllegalStateException iae) { + // expected } try { - reader = new RepositoryItemReader(); + reader = new RepositoryItemReader<>(); reader.setRepository(repository); reader.setPageSize(-1); reader.afterPropertiesSet(); fail(); } catch (IllegalStateException iae) { + // expected } try { - reader = new RepositoryItemReader(); + reader = new RepositoryItemReader<>(); reader.setRepository(repository); reader.setPageSize(1); reader.afterPropertiesSet(); fail(); } catch (IllegalStateException iae) { + // expected } - reader = new RepositoryItemReader(); + reader = new RepositoryItemReader<>(); reader.setRepository(repository); reader.setPageSize(1); reader.setSort(sorts); @@ -107,7 +117,7 @@ public void testAfterPropertiesSet() throws Exception { public void testDoReadFirstReadNoResults() throws Exception { ArgumentCaptor pageRequestContainer = ArgumentCaptor.forClass(PageRequest.class); - when(repository.findAll(pageRequestContainer.capture())).thenReturn(new PageImpl(new ArrayList())); + when(repository.findAll(pageRequestContainer.capture())).thenReturn(new PageImpl<>(new ArrayList<>())); assertNull(reader.doRead()); @@ -119,14 +129,13 @@ public void testDoReadFirstReadNoResults() throws Exception { } @Test - @SuppressWarnings("serial") public void testDoReadFirstReadResults() throws Exception { ArgumentCaptor pageRequestContainer = ArgumentCaptor.forClass(PageRequest.class); final Object result = new Object(); - when(repository.findAll(pageRequestContainer.capture())).thenReturn(new PageImpl(new ArrayList(){{ - add(result); - }})); + when(repository.findAll(pageRequestContainer.capture())).thenReturn(new PageImpl<>(singletonList( + result + ))); assertEquals(result, reader.doRead()); @@ -138,15 +147,14 @@ public void testDoReadFirstReadResults() throws Exception { } @Test - @SuppressWarnings("serial") public void testDoReadFirstReadSecondPage() throws Exception { ArgumentCaptor pageRequestContainer = ArgumentCaptor.forClass(PageRequest.class); final Object result = new Object(); - when(repository.findAll(pageRequestContainer.capture())).thenReturn(new PageImpl(new ArrayList() {{ - add(new Object()); - }})).thenReturn(new PageImpl(new ArrayList(){{ - add(result); - }})); + when(repository.findAll(pageRequestContainer.capture())).thenReturn(new PageImpl<>(singletonList( + new Object() + ))).thenReturn(new PageImpl<>(singletonList( + result + ))); assertFalse(reader.doRead() == result); assertEquals(result, reader.doRead()); @@ -159,15 +167,14 @@ public void testDoReadFirstReadSecondPage() throws Exception { } @Test - @SuppressWarnings("serial") public void testDoReadFirstReadExhausted() throws Exception { ArgumentCaptor pageRequestContainer = ArgumentCaptor.forClass(PageRequest.class); final Object result = new Object(); - when(repository.findAll(pageRequestContainer.capture())).thenReturn(new PageImpl(new ArrayList() {{ - add(new Object()); - }})).thenReturn(new PageImpl(new ArrayList(){{ - add(result); - }})).thenReturn(new PageImpl(new ArrayList())); + when(repository.findAll(pageRequestContainer.capture())).thenReturn(new PageImpl<>(singletonList( + new Object() + ))).thenReturn(new PageImpl<>(singletonList( + result + ))).thenReturn(new PageImpl<>(new ArrayList<>())); assertFalse(reader.doRead() == result); assertEquals(result, reader.doRead()); @@ -181,15 +188,21 @@ public void testDoReadFirstReadExhausted() throws Exception { } @Test - @SuppressWarnings("serial") public void testJumpToItem() throws Exception { reader.setPageSize(100); + final List objectList = fillWithNewObjects(100); ArgumentCaptor pageRequestContainer = ArgumentCaptor.forClass(PageRequest.class); - when(repository.findAll(pageRequestContainer.capture())).thenReturn(new PageImpl(new ArrayList() {{ - add(new Object()); - }})); + when(repository.findAll(pageRequestContainer.capture())).thenReturn(new PageImpl<>( + objectList + )); reader.jumpToItem(485); + // no page requested at this stage + verify(repository, never()).findAll(any(Pageable.class)); + + // the page must only actually be fetched on the next "doRead()" call + final Object o = reader.doRead(); + assertSame("Fetched object should be at index 85 in the current page", o, objectList.get(85)); Pageable pageRequest = pageRequestContainer.getValue(); assertEquals(400, pageRequest.getOffset()); @@ -198,6 +211,34 @@ public void testJumpToItem() throws Exception { assertEquals("id: ASC", pageRequest.getSort().toString()); } + @Test + public void testJumpToItemFirstItemOnPage() throws Exception { + reader.setPageSize(50); + final List objectList = fillWithNewObjects(50); + ArgumentCaptor pageRequestContainer = ArgumentCaptor.forClass(PageRequest.class); + when(repository.findAll(pageRequestContainer.capture())).thenReturn(new PageImpl<>( + objectList + )); + + reader.jumpToItem(150); + verify(repository, never()).findAll(any(Pageable.class)); + + assertSame("Fetched object should be the first one in the current page", objectList.get(0), reader.doRead()); + + Pageable pageRequest = pageRequestContainer.getValue(); + assertEquals(150, pageRequest.getOffset()); + assertEquals(3, pageRequest.getPageNumber()); + assertEquals(50, pageRequest.getPageSize()); + } + + private static List fillWithNewObjects(int nb) { + List result = new ArrayList<>(); + for (int i = 0; i < nb; i++) { + result.add(new TestItem(i)); + } + return result; + } + @Test public void testInvalidMethodName() throws Exception { reader.setMethodName("thisMethodDoesNotExist"); @@ -213,18 +254,17 @@ public void testInvalidMethodName() throws Exception { @Test public void testDifferentTypes() throws Exception { TestRepository differentRepository = mock(TestRepository.class); - RepositoryItemReader reader = new RepositoryItemReader(); - sorts = new HashMap(); - sorts.put("id", Direction.ASC); + RepositoryItemReader reader = new RepositoryItemReader<>(); + sorts = Collections.singletonMap("id", Direction.ASC); reader.setRepository(differentRepository); reader.setPageSize(1); reader.setSort(sorts); reader.setMethodName("findFirstNames"); ArgumentCaptor pageRequestContainer = ArgumentCaptor.forClass(PageRequest.class); - when(differentRepository.findFirstNames(pageRequestContainer.capture())).thenReturn(new PageImpl(new ArrayList(){{ - add("result"); - }})); + when(differentRepository.findFirstNames(pageRequestContainer.capture())).thenReturn(new PageImpl<>(singletonList( + "result" + ))); assertEquals("result", reader.doRead()); @@ -237,47 +277,47 @@ public void testDifferentTypes() throws Exception { @Test public void testSettingCurrentItemCountExplicitly() throws Exception { - reader.setCurrentItemCount(3); + // Dataset : ("1" "2") | "3" "4" | "5" "6" + reader.setCurrentItemCount(3); // item as index 3 is : "4" reader.setPageSize(2); PageRequest request = new PageRequest(1, 2, new Sort(Direction.ASC, "id")); - when(repository.findAll(request)).thenReturn(new PageImpl(new ArrayList() {{ - add("3"); - add("4"); - }})); + when(repository.findAll(request)).thenReturn(new PageImpl<>(Arrays. asList( + "3", + "4" + ))); request = new PageRequest(2, 2, new Sort(Direction.ASC, "id")); - when(repository.findAll(request)).thenReturn(new PageImpl(new ArrayList(){{ - add("5"); - add("6"); - }})); + when(repository.findAll(request)).thenReturn(new PageImpl<>(Arrays. asList( + "5", + "6" + ))); reader.open(new ExecutionContext()); Object result = reader.read(); - assertEquals("3", result); - assertEquals("4", reader.read()); + assertEquals("4", result); assertEquals("5", reader.read()); assertEquals("6", reader.read()); } @Test public void testSettingCurrentItemCountRestart() throws Exception { - reader.setCurrentItemCount(3); + reader.setCurrentItemCount(3); // item as index 3 is : "4" reader.setPageSize(2); PageRequest request = new PageRequest(1, 2, new Sort(Direction.ASC, "id")); - when(repository.findAll(request)).thenReturn(new PageImpl(new ArrayList(){{ - add("3"); - add("4"); - }})); + when(repository.findAll(request)).thenReturn(new PageImpl<>(Arrays. asList( + "3", + "4" + ))); request = new PageRequest(2, 2, new Sort(Direction.ASC, "id")); - when(repository.findAll(request)).thenReturn(new PageImpl(new ArrayList() {{ - add("5"); - add("6"); - }})); + when(repository.findAll(request)).thenReturn(new PageImpl<>(Arrays. asList( + "5", + "6" + ))); ExecutionContext executionContext = new ExecutionContext(); reader.open(executionContext); @@ -286,10 +326,9 @@ public void testSettingCurrentItemCountRestart() throws Exception { reader.update(executionContext); reader.close(); - assertEquals("3", result); + assertEquals("4", result); reader.open(executionContext); - assertEquals("4", reader.read()); assertEquals("5", reader.read()); assertEquals("6", reader.read()); } @@ -299,16 +338,16 @@ public void testResetOfPage() throws Exception { reader.setPageSize(2); PageRequest request = new PageRequest(0, 2, new Sort(Direction.ASC, "id")); - when(repository.findAll(request)).thenReturn(new PageImpl(new ArrayList(){{ - add("1"); - add("2"); - }})); + when(repository.findAll(request)).thenReturn(new PageImpl<>(Arrays. asList( + "1", + "2" + ))); request = new PageRequest(1, 2, new Sort(Direction.ASC, "id")); - when(repository.findAll(request)).thenReturn(new PageImpl(new ArrayList() {{ - add("3"); - add("4"); - }})); + when(repository.findAll(request)).thenReturn(new PageImpl<>(Arrays. asList( + "3", + "4" + ))); ExecutionContext executionContext = new ExecutionContext(); reader.open(executionContext); @@ -324,7 +363,21 @@ public void testResetOfPage() throws Exception { assertEquals("3", reader.read()); } - public static interface TestRepository extends PagingAndSortingRepository { + public interface TestRepository extends PagingAndSortingRepository { Page findFirstNames(Pageable pageable); } + + // Simple object for readability + private static class TestItem { + private final int myIndex; + + TestItem(int myIndex) { + this.myIndex = myIndex; + } + + @Override + public String toString() { + return "TestItem at index " + myIndex; + } + } }