Spring Batch ItemProcessor Example

Spring Batch ItemProcessor is mainly used for writing business logic. We can add Spring Batch ItemProcess component between the reading and writing components. The ItemProcess can be used for validating items, transforming items, and filtering items.

The ItemProcess modify read items before sending them to the writer. It can change the state of the read item or it can create a new object. So the written items may not be of the same type as read items. For example – we have some student records in the Database and we have to read from the database and write it to excel. Before writing, we want to modify all student’s names in uppercase. Here we can define ItemProcess and add some business logic to the item processor.

Another use case – Suppose we want to write only those record that belongs to a specific university. We can define custom ItemProcess and add business logic for filtering.

Spring Batch ItemProcesor interface contains process() method. We can define our custom ItemProcessor by implementing the ItemProcessor interface and we need to override the process() method.

public class StudentItemProcessor implements ItemProcessor<Student, Student> {
    @Override
    public Student process(Student student) throws Exception {
        //some business logic
    }
}

Spring Batch provides different types of predefined item processes for different purposes.

In this post, we are going to see ValidatingItemProcessor and ItemProcessorAadapter examples. We have separate tutorials for CompositeItemProcessor and AsyncItemProcessor here.

Spring Batch ValidatingItemProcessor example

Use case – Consider we are reading thousands of records from the database and writing those in CSV files. We want to perform some validation for items before writing those to the CSV file.

Spring batch provides ValidatingItemProcessor class that can be used to perform validation on items.

The ValidatingItemProcessor class implements ItemProcess which is used to validate input and returns it without any modifications.

It contains the below methods.

public T process(T item)
public void setFilter(boolean filter)
public void afterPropertiesSet()
public void setValidator(Validator<? super T> validator)

Define Validator class i.e StudentValidator.java

In this example, we are going to perform fundamental validation. If records from the database don’t contain an id or name throw a validation exception.

package com.springbatchexample.component;

import com.springbatchexample.entity.Student;
import org.springframework.batch.item.validator.ValidationException;
import org.springframework.batch.item.validator.Validator;

public class StudentValidator implements Validator<Student> {

    @Override
    public void validate(Student student) throws ValidationException {

        if (student.getId() == null) {

            throw new ValidationException("id must not be null");
        }
        if (student.getName() == null) {
            throw new ValidationException("name must not be null");
        }
    }
}

Configure ValidatingItemProcessor class

    @Bean
    public ValidatingItemProcessor<Student> itemProcessor() {
        ValidatingItemProcessor<Student> validatingItemProcessor = new ValidatingItemProcessor<>();
        validatingItemProcessor.setValidator(new StudentValidator());
        validatingItemProcessor.setFilter(true);
        return validatingItemProcessor;
    }

Call this ValidatingItemProcessor.

    @Bean
    public Step getDbToCsvStep() {
        StepBuilder stepBuilder = stepBuilderFactory.get("getDbToCsvStep");
        SimpleStepBuilder<Student, Student> simpleStepBuilder = stepBuilder.chunk(1);
        return simpleStepBuilder.reader(reader()).processor(itemProcessor()).writer(writer()).build();
    }

We need to call ValidatingItemProcessor using reader.processor() method.

Let’s see the complete Spring Batch ValidatingItemProcessor Example using Spring boot

maven dependency

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.1.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.batch</groupId>
            <artifactId>spring-batch-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-batch</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
    </dependencies>

package structure

Spring Batch ValidatingItemProcessor example

Model class

package com.springbatchexample.entity;

public class Student {

    private Long id;
    private String name;
    private String rollNumber;
    public Student() {

    }
    public Student(Long id, String name, String rollNumber) {
        this.id = id;
        this.name = name;
        this.rollNumber = rollNumber;
    }
    //getter & setter
}

Define RowMapper

package com.springbatchexample.component;

import com.springbatchexample.entity.Student;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Component;

import java.sql.ResultSet;
import java.sql.SQLException;

@Component
public class StudentResultRowMapper implements RowMapper<Student> {
    @Override
    public Student mapRow(ResultSet rs, int i) throws SQLException {
        Student student = new Student();
        student.setId(rs.getLong("id"));
        student.setRollNumber(rs.getString("roll_number"));
        student.setName(rs.getString("name"));
        return student;
    }
}

This StudentResultRowMapper will get used while creating reader using JdbcCursorItemReader.

Define SpringBatchConfig file

package com.springbatchexample.config;

import com.springbatchexample.component.StudentResultRowMapper;
import com.springbatchexample.component.StudentValidator;
import com.springbatchexample.entity.Student;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.job.builder.FlowJobBuilder;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.core.step.builder.SimpleStepBuilder;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.item.database.JdbcCursorItemReader;
import org.springframework.batch.item.file.FlatFileItemWriter;
import org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor;
import org.springframework.batch.item.file.transform.DelimitedLineAggregator;
import org.springframework.batch.item.validator.ValidatingItemProcessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.FileSystemResource;

import javax.sql.DataSource;

@EnableBatchProcessing
@Configuration
public class SpringBatchConfig {


    @Autowired
    private DataSource dataSource;

    @Autowired
    private JobBuilderFactory jobBuilderFactory;

    @Autowired
    private StepBuilderFactory stepBuilderFactory;


    @Bean
    public JdbcCursorItemReader<Student> reader() {
        JdbcCursorItemReader<Student> reader = new JdbcCursorItemReader<>();
        reader.setDataSource(dataSource);
        reader.setSql("select id, roll_number, name from student");
        reader.setRowMapper(new StudentResultRowMapper());
        return reader;
    }

    @Bean
    public FlatFileItemWriter<Student> writer() {
        FlatFileItemWriter<Student> writer = new FlatFileItemWriter<>();
        writer.setResource(new FileSystemResource("C://data/batch/data.csv"));
        writer.setLineAggregator(getDelimitedLineAggregator());
        return writer;
    }

    private DelimitedLineAggregator<Student> getDelimitedLineAggregator() {
        BeanWrapperFieldExtractor<Student> beanWrapperFieldExtractor = new BeanWrapperFieldExtractor<Student>();
        beanWrapperFieldExtractor.setNames(new String[]{"id", "rollNumber", "name"});

        DelimitedLineAggregator<Student> aggregator = new DelimitedLineAggregator<Student>();
        aggregator.setDelimiter(",");
        aggregator.setFieldExtractor(beanWrapperFieldExtractor);
        return aggregator;

    }

    @Bean
    public Step getDbToCsvStep() {
        StepBuilder stepBuilder = stepBuilderFactory.get("getDbToCsvStep");
        SimpleStepBuilder<Student, Student> simpleStepBuilder = stepBuilder.chunk(1);
        return simpleStepBuilder.reader(reader()).processor(itemProcessor()).writer(writer()).build();
    }

    @Bean
    public Job dbToCsvJob() {
        JobBuilder jobBuilder = jobBuilderFactory.get("dbToCsvJob");
        jobBuilder.incrementer(new RunIdIncrementer());
        FlowJobBuilder flowJobBuilder = jobBuilder.flow(getDbToCsvStep()).end();
        Job job = flowJobBuilder.build();
        return job;
    }

    @Bean
    public ValidatingItemProcessor<Student> itemProcessor() {
        ValidatingItemProcessor<Student> validatingItemProcessor = new ValidatingItemProcessor<>();
        validatingItemProcessor.setValidator(new StudentValidator());
        validatingItemProcessor.setFilter(true);
        return validatingItemProcessor;
    }
}

Define SpringMain class

package com.springbatchexample.main;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan(basePackages = "com.springbatchexample.*")
public class SpringMain {
    public static void main(String[] args) {

        SpringApplication.run(SpringMain.class, args);
    }
}

application.properties

spring.datasource.url=jdbc:mysql://localhost:3306/springbootcrudexample
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.hibernate.ddl-auto=create
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
server.port = 9091
spring.batch.initialize-schema=always	

Note – For more details about ValidatingItemProcessor spring batch see docs.

Spring Batch ItemProcessorAadapter Example

While using ItemProcessorAdapter we no need to define a custom ItemProcessor implementing ItemProcessor interface. The ItemProcessorAdapter used to invoke a custom method on a delegate class or POJO.

For example we defined StudentNameInUpperCase class and method call nameInUpperCase(). The ItemProcessorAdapter extends AbstractMethodInvokingDelegator class that provides method to set target class and methods.

Before writing, we will change Student’s name in uppercase.

Directory structure.

spring batch itemprocessor adapter example
spring batch itemprocessor adapter example

Create a target class.

public class StudentNameInUpperCase {
    public Student nameInUpperCase(Object item) throws Exception {
        Student student = (Student) item;
        student.setName(student.getName().toUpperCase());
        return student;
    }
}

Configure ItemProcessorAdapter.

    @Bean
    public ItemProcessorAdapter itemProcessorAdapter() {
        ItemProcessorAdapter itemProcessorAdapter = new ItemProcessorAdapter();
        itemProcessorAdapter.setTargetObject(studentNameInUpperCase());
        itemProcessorAdapter.setTargetMethod("nameInUpperCase");
        return itemProcessorAdapter;
    }

    @Bean
    public StudentNameInUpperCase studentNameInUpperCase(){
        return new StudentNameInUpperCase();
    }

The rest of the code would be the same. After running the main method our csv will have name in uppercase.

spring batch itemprocessor adapter example

See spring batch ItemProcessorAdapter docs.

That’s all about Spring Batch ItemProcessor Example.

Other Spring Batch Tutorials.