Fix incorrect behaviour of RepositoryItemReader#jumpToItem() on restart
* Corrects unit tests with expected behavior. * Creates integration tests highlighting Hibernate lazy-loading failure if the reader has requested the page on open(). Issue #1074
This commit is contained in:
committed by
Mahmoud Ben Hassine
parent
613f425336
commit
95f11facf4
@@ -478,6 +478,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"
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2020 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<Author> reader;
|
||||
|
||||
@After
|
||||
public void reinitializeReader() {
|
||||
reader.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadFromFirstPos() throws Exception {
|
||||
service.openReader(new ExecutionContext());
|
||||
|
||||
final List<Book> 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<Book> 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<Book> 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<Book> 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<Book> 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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2020 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<Book> books;
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public List<Book> 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2020 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2020 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<Author, Integer> {
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2020 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<Author> itemReader;
|
||||
|
||||
public SimpleService(RepositoryItemReader<Author> 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<Book> nextAuthorBooks() throws Exception {
|
||||
List<Book> result = new ArrayList<>();
|
||||
|
||||
final Author nextAuthor = itemReader.read();
|
||||
if (nextAuthor != null) {
|
||||
result.addAll(nextAuthor.getBooks());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
|
||||
xmlns:tx="http://www.springframework.org/schema/tx"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
|
||||
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
|
||||
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
|
||||
|
||||
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
|
||||
<property name="driverClassName" value="org.hsqldb.jdbcDriver" />
|
||||
<property name="url" value="jdbc:hsqldb:mem:testjpamydb" />
|
||||
</bean>
|
||||
|
||||
<!-- Initialise the database -->
|
||||
<bean id="dataSourceInitializer" class="test.jdbc.datasource.DataSourceInitializer">
|
||||
<property name="dataSource" ref="dataSource"/>
|
||||
<property name="initialize" value="true"/>
|
||||
<property name="initScripts">
|
||||
<list>
|
||||
<value>classpath:org/springframework/batch/item/database/init-books-schema.sql</value>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
|
||||
<property name="entityManagerFactory" ref="entityManagerFactory" />
|
||||
</bean>
|
||||
<tx:annotation-driven transaction-manager="transactionManager"/>
|
||||
|
||||
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
|
||||
<property name="dataSource" ref="dataSource"/>
|
||||
<property name="jpaVendorAdapter">
|
||||
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
|
||||
<property name="showSql" value="false"/>
|
||||
</bean>
|
||||
</property>
|
||||
<property name="packagesToScan" value="org.springframework.batch.item.sample.books"/>
|
||||
</bean>
|
||||
|
||||
<jpa:repositories base-package="org.springframework.batch.item.sample.books.data"/>
|
||||
|
||||
<bean id="authorRepositoryItemReader" class="org.springframework.batch.item.data.RepositoryItemReader">
|
||||
<property name="repository" ref="authorRepository"/>
|
||||
<property name="methodName" value="findAll"/>
|
||||
<property name="sort">
|
||||
<map>
|
||||
<entry key="id" value="ASC"/>
|
||||
</map>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="myService" class="org.springframework.batch.item.sample.books.data.SimpleService">
|
||||
<constructor-arg ref="authorRepositoryItemReader"/>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
@@ -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);
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* Copyright 2012-2020 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.
|
||||
@@ -71,6 +71,7 @@ import org.springframework.util.MethodInvoker;
|
||||
* </p>
|
||||
*
|
||||
* @author Michael Minella
|
||||
* @author Antoine Kapps
|
||||
* @since 2.2
|
||||
*/
|
||||
public class RepositoryItemReader<T> extends AbstractItemCountingItemStreamItemReader<T> implements InitializingBean {
|
||||
@@ -91,7 +92,7 @@ public class RepositoryItemReader<T> extends AbstractItemCountingItemStreamItemR
|
||||
|
||||
private volatile List<T> results;
|
||||
|
||||
private Object lock = new Object();
|
||||
private final Object lock = new Object();
|
||||
|
||||
private String methodName;
|
||||
|
||||
@@ -156,20 +157,24 @@ public class RepositoryItemReader<T> extends AbstractItemCountingItemStreamItemR
|
||||
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()) {
|
||||
@@ -186,11 +191,8 @@ public class RepositoryItemReader<T> extends AbstractItemCountingItemStreamItemR
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
* Copyright 2013-2020 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.
|
||||
@@ -16,7 +16,9 @@
|
||||
package org.springframework.batch.item.data;
|
||||
|
||||
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;
|
||||
@@ -35,12 +37,17 @@ import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
|
||||
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;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@@ -54,8 +61,7 @@ public class RepositoryItemReaderTests {
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
sorts = new HashMap<>();
|
||||
sorts.put("id", Direction.ASC);
|
||||
sorts = Collections.singletonMap("id", Direction.ASC);
|
||||
reader = new RepositoryItemReader<>();
|
||||
reader.setRepository(repository);
|
||||
reader.setPageSize(1);
|
||||
@@ -66,9 +72,10 @@ public class RepositoryItemReaderTests {
|
||||
@Test
|
||||
public void testAfterPropertiesSet() throws Exception {
|
||||
try {
|
||||
new RepositoryItemReader<>().afterPropertiesSet();
|
||||
new RepositoryItemReader<>().afterPropertiesSet();
|
||||
fail();
|
||||
} catch (IllegalStateException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -77,6 +84,7 @@ public class RepositoryItemReaderTests {
|
||||
reader.afterPropertiesSet();
|
||||
fail();
|
||||
} catch (IllegalStateException iae) {
|
||||
// expected
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -86,6 +94,7 @@ public class RepositoryItemReaderTests {
|
||||
reader.afterPropertiesSet();
|
||||
fail();
|
||||
} catch (IllegalStateException iae) {
|
||||
// expected
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -95,6 +104,7 @@ public class RepositoryItemReaderTests {
|
||||
reader.afterPropertiesSet();
|
||||
fail();
|
||||
} catch (IllegalStateException iae) {
|
||||
// expected
|
||||
}
|
||||
|
||||
reader = new RepositoryItemReader<>();
|
||||
@@ -120,14 +130,13 @@ public class RepositoryItemReaderTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("serial")
|
||||
public void testDoReadFirstReadResults() throws Exception {
|
||||
ArgumentCaptor<PageRequest> pageRequestContainer = ArgumentCaptor.forClass(PageRequest.class);
|
||||
final Object result = new Object();
|
||||
|
||||
when(repository.findAll(pageRequestContainer.capture())).thenReturn(new PageImpl<>(new ArrayList<Object>(){{
|
||||
add(result);
|
||||
}}));
|
||||
when(repository.findAll(pageRequestContainer.capture())).thenReturn(new PageImpl<>(singletonList(
|
||||
result
|
||||
)));
|
||||
|
||||
assertEquals(result, reader.doRead());
|
||||
|
||||
@@ -139,15 +148,14 @@ public class RepositoryItemReaderTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("serial")
|
||||
public void testDoReadFirstReadSecondPage() throws Exception {
|
||||
ArgumentCaptor<PageRequest> pageRequestContainer = ArgumentCaptor.forClass(PageRequest.class);
|
||||
final Object result = new Object();
|
||||
when(repository.findAll(pageRequestContainer.capture())).thenReturn(new PageImpl<>(new ArrayList<Object>() {{
|
||||
add(new Object());
|
||||
}})).thenReturn(new PageImpl<>(new ArrayList<Object>(){{
|
||||
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());
|
||||
@@ -160,15 +168,14 @@ public class RepositoryItemReaderTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("serial")
|
||||
public void testDoReadFirstReadExhausted() throws Exception {
|
||||
ArgumentCaptor<PageRequest> pageRequestContainer = ArgumentCaptor.forClass(PageRequest.class);
|
||||
final Object result = new Object();
|
||||
when(repository.findAll(pageRequestContainer.capture())).thenReturn(new PageImpl<>(new ArrayList<Object>() {{
|
||||
add(new Object());
|
||||
}})).thenReturn(new PageImpl<>(new ArrayList<Object>(){{
|
||||
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());
|
||||
@@ -182,15 +189,21 @@ public class RepositoryItemReaderTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("serial")
|
||||
public void testJumpToItem() throws Exception {
|
||||
reader.setPageSize(100);
|
||||
final List<Object> objectList = fillWithNewObjects(100);
|
||||
ArgumentCaptor<PageRequest> pageRequestContainer = ArgumentCaptor.forClass(PageRequest.class);
|
||||
when(repository.findAll(pageRequestContainer.capture())).thenReturn(new PageImpl<>(new ArrayList<Object>() {{
|
||||
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());
|
||||
@@ -199,6 +212,34 @@ public class RepositoryItemReaderTests {
|
||||
assertEquals("id: ASC", pageRequest.getSort().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJumpToItemFirstItemOnPage() throws Exception {
|
||||
reader.setPageSize(50);
|
||||
final List<Object> objectList = fillWithNewObjects(50);
|
||||
ArgumentCaptor<PageRequest> 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<Object> fillWithNewObjects(int nb) {
|
||||
List<Object> 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");
|
||||
@@ -215,17 +256,16 @@ public class RepositoryItemReaderTests {
|
||||
public void testDifferentTypes() throws Exception {
|
||||
TestRepository differentRepository = mock(TestRepository.class);
|
||||
RepositoryItemReader<String> reader = new RepositoryItemReader<>();
|
||||
sorts = new HashMap<>();
|
||||
sorts.put("id", Direction.ASC);
|
||||
sorts = Collections.singletonMap("id", Direction.ASC);
|
||||
reader.setRepository(differentRepository);
|
||||
reader.setPageSize(1);
|
||||
reader.setSort(sorts);
|
||||
reader.setMethodName("findFirstNames");
|
||||
|
||||
ArgumentCaptor<PageRequest> pageRequestContainer = ArgumentCaptor.forClass(PageRequest.class);
|
||||
when(differentRepository.findFirstNames(pageRequestContainer.capture())).thenReturn(new PageImpl<>(new ArrayList<String>(){{
|
||||
add("result");
|
||||
}}));
|
||||
when(differentRepository.findFirstNames(pageRequestContainer.capture())).thenReturn(new PageImpl<>(singletonList(
|
||||
"result"
|
||||
)));
|
||||
|
||||
assertEquals("result", reader.doRead());
|
||||
|
||||
@@ -238,47 +278,47 @@ public class RepositoryItemReaderTests {
|
||||
|
||||
@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 = PageRequest.of(1, 2, Sort.by(Direction.ASC, "id"));
|
||||
when(repository.findAll(request)).thenReturn(new PageImpl<>(new ArrayList<Object>() {{
|
||||
add("3");
|
||||
add("4");
|
||||
}}));
|
||||
when(repository.findAll(request)).thenReturn(new PageImpl<>(Arrays.<Object> asList(
|
||||
"3",
|
||||
"4"
|
||||
)));
|
||||
|
||||
request = PageRequest.of(2, 2, Sort.by(Direction.ASC, "id"));
|
||||
when(repository.findAll(request)).thenReturn(new PageImpl<>(new ArrayList<Object>(){{
|
||||
add("5");
|
||||
add("6");
|
||||
}}));
|
||||
when(repository.findAll(request)).thenReturn(new PageImpl<>(Arrays.<Object> 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 = PageRequest.of(1, 2, Sort.by(Direction.ASC, "id"));
|
||||
when(repository.findAll(request)).thenReturn(new PageImpl<>(new ArrayList<Object>(){{
|
||||
add("3");
|
||||
add("4");
|
||||
}}));
|
||||
when(repository.findAll(request)).thenReturn(new PageImpl<>(Arrays.<Object> asList(
|
||||
"3",
|
||||
"4"
|
||||
)));
|
||||
|
||||
request = PageRequest.of(2, 2, Sort.by(Direction.ASC, "id"));
|
||||
when(repository.findAll(request)).thenReturn(new PageImpl<>(new ArrayList<Object>() {{
|
||||
add("5");
|
||||
add("6");
|
||||
}}));
|
||||
when(repository.findAll(request)).thenReturn(new PageImpl<>(Arrays.<Object> asList(
|
||||
"5",
|
||||
"6"
|
||||
)));
|
||||
|
||||
ExecutionContext executionContext = new ExecutionContext();
|
||||
reader.open(executionContext);
|
||||
@@ -287,10 +327,9 @@ public class RepositoryItemReaderTests {
|
||||
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());
|
||||
}
|
||||
@@ -300,16 +339,16 @@ public class RepositoryItemReaderTests {
|
||||
reader.setPageSize(2);
|
||||
|
||||
PageRequest request = PageRequest.of(0, 2, Sort.by(Direction.ASC, "id"));
|
||||
when(repository.findAll(request)).thenReturn(new PageImpl<>(new ArrayList<Object>(){{
|
||||
add("1");
|
||||
add("2");
|
||||
}}));
|
||||
when(repository.findAll(request)).thenReturn(new PageImpl<>(Arrays.<Object> asList(
|
||||
"1",
|
||||
"2"
|
||||
)));
|
||||
|
||||
request = PageRequest.of(1, 2, Sort.by(Direction.ASC, "id"));
|
||||
when(repository.findAll(request)).thenReturn(new PageImpl<>(new ArrayList<Object>() {{
|
||||
add("3");
|
||||
add("4");
|
||||
}}));
|
||||
when(repository.findAll(request)).thenReturn(new PageImpl<>(Arrays.<Object> asList(
|
||||
"3",
|
||||
"4"
|
||||
)));
|
||||
|
||||
ExecutionContext executionContext = new ExecutionContext();
|
||||
reader.open(executionContext);
|
||||
@@ -325,7 +364,21 @@ public class RepositoryItemReaderTests {
|
||||
assertEquals("3", reader.read());
|
||||
}
|
||||
|
||||
public interface TestRepository extends PagingAndSortingRepository<Map<String, String>, Long> {
|
||||
public interface TestRepository extends PagingAndSortingRepository<Map, Long> {
|
||||
Page<String> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user