org.springframework.batch.item.database.JpaPagingItemReader creates its own instance of entityManager
(from org.springframework.batch.item.database.JpaPagingItemReader # doOpen):
entityManager = entityManagerFactory.createEntityManager(jpaPropertyMap);
If you are in a transaction, it seems reader objects are not detached (from org.springframework.batch.item.database.JpaPagingItemReader # doReadPage):
if (!transacted) { List<T> queryResult = query.getResultList(); for (T entity : queryResult) { entityManager.detach(entity); results.add(entity); }//end if } else { results.addAll(query.getResultList()); tx.commit(); }
For this reason, when you update an element in a processor or record, that element is still controlled by the entityManager reader.
When the element reader reads the next piece of data, it flushes the context to the database.
So, if we look at your case, after the first piece of data processes we have in the database:
|id|active |1 | false |2 | false |3 | false
org.springframework.batch.item.database.JpaPagingItemReader uses restriction and offset to retrieve paginated data. So, the following choice created by the reader looks like this:
select * from table where active = true offset 3 limits 3.
The reader will skip items with identifiers 4,5,6, since now they are the first rows retrieved from the database.
As a workaround, you can use the jdbc implementation (org.springframework.batch.item.database.JdbcPagingItemReader) as it does not use restriction and offset. It is based on a sorted column (usually the id column), so you won't miss any data. Of course, you will have to update your data in the record (using either JPA for a clean JDBC implementation)
The reader will be more detailed:
@Bean public ItemReader<? extends Entity> reader() { JdbcPagingItemReader<Entity> reader = new JdbcPagingItemReader<Entity>(); final SqlPagingQueryProviderFactoryBean sqlPagingQueryProviderFactoryBean = new SqlPagingQueryProviderFactoryBean(); sqlPagingQueryProviderFactoryBean.setDataSource(dataSource); sqlPagingQueryProviderFactoryBean.setSelectClause("select *"); sqlPagingQueryProviderFactoryBean.setFromClause("from <your table name>"); sqlPagingQueryProviderFactoryBean.setWhereClause("where active = true"); sqlPagingQueryProviderFactoryBean.setSortKey("id"); try { reader.setQueryProvider(sqlPagingQueryProviderFactoryBean.getObject()); } catch (Exception e) { e.printStackTrace(); } reader.setDataSource(dataSource); reader.setPageSize(3); reader.setRowMapper(new BeanPropertyRowMapper<Entity>(Entity.class)); return reader;