Я хотел бы знать, рекомендуется ли этот способ реализовать ридер весеннего пакета с jpa или лучше поискать другое решение, и если этот способ не рекомендуется, где я могу искать информацию о лучшем варианте
public class CreditCardItemReader implements ItemReader<CreditCard> {
@Autowired
private CreditCardRepository respository;
private Iterator<CreditCard> usersIterator;
@BeforeStep
public void before(StepExecution stepExecution) {
usersIterator = respository.someQuery().iterator();
}
@Override
public CreditCard read() {
if (usersIterator != null && usersIterator.hasNext()) {
return usersIterator.next();
} else {
return null;
}
}
}




Эта реализация приемлема только для небольшого набора данных, поскольку данные считываются одним пакетным запросом и сохраняются в памяти весь список результатов. Кроме того, он не является потокобезопасным.
В случае загрузки больших объемов:
Решение 1, org.springframework.batch.item.database.JpaCursorItemReader
Похожая реализация определена из коробки в Spring Batch: JpaCursorItemReader
Основное отличие состоит в том, что эта реализация работает только с конкретным запросом JPQL, а не с репозиторием, и использует метод JPA Запрос.getResultStream() для получения результатов запроса.
Реализация JpaCursorItemReader:
protected void doOpen() throws Exception {
...
Query query = createQuery();
if (this.parameterValues != null) {
this.parameterValues.forEach(query::setParameter);
}
this.iterator = query.getResultStream().iterator();
}
Hibernate, например, представил метод Query.getResultStream() в версии 5.2.
Он использует реализацию Hibernate ScrollableResult для перемещения по набору результатов и выборки записей в пакетах. Это предотвращает загрузку сразу всех записей результирующего набора и позволяет более эффективно их обрабатывать.
Пример создания:
protected ItemReader<Foo> getItemReader() throws Exception {
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
String jpqlQuery = "from Foo";
JpaCursorItemReader<Foo> itemReader = new JpaCursorItemReader<>();
itemReader.setQueryString(jpqlQuery);
itemReader.setEntityManagerFactory(factoryBean.getObject());
itemReader.afterPropertiesSet();
itemReader.setSaveState(true);
return itemReader;
}
Решение 2, org.springframework.batch.item.database.JpaPagingItemReader
Это более гибкое решение для запроса JPQL, чем JpaCursorItemReader. ItemReader загружает и сохраняет данные по страницам и является потокобезопасным.
Согласно документации:
ItemReader for reading database records built on top of JPA.
It executes the JPQL setQueryString(String) to retrieve requested data. The query is executed using paged requests of a size specified in AbstractPagingItemReader.setPageSize(int). Additional pages are requested when needed as AbstractItemCountingItemStreamItemReader.read() method is called, returning an object corresponding to current position.
The performance of the paging depends on the JPA implementation and its use of database specific features to limit the number of returned rows.
Setting a fairly large page size and using a commit interval that matches the page size should provide better performance.
In order to reduce the memory usage for large results the persistence context is flushed and cleared after each page is read. This causes any entities read to be detached. If you make changes to the entities and want the changes persisted then you must explicitly merge the entities.
The implementation is thread-safe in between calls
Решение 3, org.springframework.batch.item.data.RepositoryItemReader
Это более эффективное решение. Он работает с репозиторием, загружает и хранит данные порциями и является потокобезопасным.
Согласно документации:
A ItemReader that reads records utilizing a PagingAndSortingRepository.
Performance of the reader is dependent on the repository implementation, however setting a reasonably large page size and matching that to the commit interval should yield better performance.
The reader must be configured with a PagingAndSortingRepository, a Sort, and a pageSize greater than 0.
This implementation is thread-safe between calls to AbstractItemCountingItemStreamItemReader.open(ExecutionContext), but remember to use saveState=false if used in a multi-threaded client (no restart available).
Пример создания:
PagingAndSortingRepository<Foo, Long> repository = FooRepository<>();
RepositoryItemReader<Foo> reader = new RepositoryItemReader<>();
reader.setRepository(repository ); //The PagingAndSortingRepository implementation used to read input from.
reader.setMethodName("findByName"); //Specifies what method on the repository to call.
reader.setArguments(arguments); // Arguments to be passed to the data providing method.
Создание через билдер:
PagingAndSortingRepository<Foo, Long> repository = new FooRepository<>();
new RepositoryItemReaderBuilder<>().repository(repository)
.methodName("findByName")
.arguments(new ArrayList<>())
.build()
Дополнительные примеры использования: RepositoryItemReaderTests и RepositoryItemReaderIntegrationTests.
Подведем итог:
Ваша реализация хороша только для простых случаев использования.
Рекомендую использовать готовые решения.