Hace unos días escribí un post spring batch primeros pasos, que exponía el funcionamiento de un batch, bastante simple. Estaría bien leerlo y ver su funcionamiento en el caso que no lo sepáis
Por un lado, mi objetivo es crear una hilo de post, explicando cada parte o funcionalidad que nos ofrece este framework, dentro del ecosistema de Spring. Realmente si se profundiza en Spring Batch, veremos que nos provee con una lista de herramientas de soporte para nuestro procesamiento de datos bastante interesante.
Por otro lado, el objetivo de hoy, es crear un ejemplo de como implementar un ItemReader<> y como funciona, dentro de nuestro proceso.
STEP
En Spring batch, tenemos jobs y dentro de estos jobs, tenemos una lista de «steps» y los steps están constituidos, como mínimo, de un reader y un writer, el processor es opcional. Más adelante veremos los tipos de ejecución de steps, nos ofrece la configuración de un job.
ItemReader<T>
Como he dicho antes, el reader es una parte indispensable de los step de Spring Batch. Desde la configuración de nuestro ejemplo:
@Override
public Step step(StepBuilderFactory stepBuilderFactory) {
return stepBuilderFactory.get("itemReaderToDatabase-step")
.allowStartIfComplete(true)
.<Person, Person>chunk(5)
.reader(reader)
.processor(processor)
.writer(writer)
.build();
}
Podemos navegar hasta la firma del método reader, la cual tiene este aspecto:
/**
* An item reader that provides a stream of items. Will be automatically registered as a {@link #stream(ItemStream)}
* or listener if it implements the corresponding interface. By default assumed to be non-transactional.
*
* @see #readerTransactionalQueue
* @param reader an item reader
* @return this for fluent chaining
*/
public SimpleStepBuilder<I, O> reader(ItemReader<? extends I> reader) {
this.reader = reader;
return this;
}
Vemos que requiere una implementación de ItemReader. podemos deducir qué implementando y paseándosela al método reader, la usara para leer. Con la ayuda de nuestro IDE, podemos ver todas las implementaciones que podríamos usar, en esta parte de la configuración.
Seguramente hablaré de estas implementaciones a lo largo de este grupo de post.
CÓMO LEE SPRING BATCH
La interfaz de ItemReader, es bastante simple:
public interface ItemReader<T> {
T read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException;
}
Aunque se puede devolver una lista, ya que puedes definir ItemReader<List<Object>>, la lista será el objeto de entrada del processor / writer. Nosotros usaremos un objeto, en el ejemplo ya que es mas ilustrativo del funcionamiento.
En el ejemplo, podéis ejecutar el test y poner vuestros breakpoints, para ver como funciona.
De cualquier forma, si vemos la configuración del step <Person,Person>.chunk(5), esto nos obliga a que el reader devuelva Person y el Writer acepte Person como parámetro de entrada, sino el IDE se quejará.
También hemos establecido, que el chunk es de 5, por lo tanto nuestro método «reader«, será llamado 5 veces, hasta completar el chunk. Seguido, lanzará el proceso el writer, al terminar, volverá hacer otras 5 llamadas al método read, hasta que el reader devuelva null.
UNA CLASE DE AYUDA IteratorItemReader<T>
Para ir explicando las clases de soporte a la lectura que nos ofrece Spring Batch, he decidido usar IteratorItemReader<T>, que nos ayuda a usar una lista, como entrada de datos, en nuestro reader.
Para crear nuestro IteratorItemReader, podemos utilizar su constructor, pasándole una collection, que implemente Iterator<T> o Iterable<T>: new IteratorItemReader<>(personList);
Como podéis deducir, o ver en el siguiente bloque de código, simplemente itera la lista, manteniendo el estado del «cursor«.
/**
* Implementation of {@link ItemReader#read()} that just iterates over the
* iterator provided.
*/
@Nullable
@Override
public T read() {
if (iterator.hasNext())
return iterator.next();
else
return null; // end of data
}
Cómo veréis en el ejemplo de github, nuestro reader, tan sólo, usará el reader:
@Override
public Person read() {
return iteratorItemReader.read();
}
Llegados a este punto, podéis preguntaros, si podemos pasarle el IteratorItemReader, directamente a la configuración de nuestro step, la respuesta es sé. No es necesario hacer el ItemReader, es meramente explicativo.
IMPLEMENTACIÓN