Skip to content

Fix incorrect behaviour of RepositoryItemReader#jumpToItem() on restart #443

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
@@ -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<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());
}

}
Original file line number Diff line number Diff line change
@@ -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<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);
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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<Author, Integer> {
}
Original file line number Diff line number Diff line change
@@ -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<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;
}
}
Original file line number Diff line number Diff line change
@@ -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>
Loading