Transforming items using ItemProcessor

In this post under Spring Batch, I will explain how to achieve Item Transformation using ItemProcessor interface.

Item Transformation process transforms the items read by reader before sending them to the writer. The transformation involves changing the state of the read item
or creating a completely new object. In the second case, the written item type might not be same as type of read item.

For our example we will be reading records as shown below. The records are string format of OldEmployee class instances, which doesn’t has the concept of first and last name it just has name field. The first name and last name is separated by space.

EMP_ID0,EMP_FNAME0 EMP_LNAME0,EMP_STATUS0,0
EMP_ID1,EMP_FNAME1 EMP_LNAME1,EMP_STATUS1,1
EMP_ID2,EMP_FNAME2 EMP_LNAME2,EMP_STATUS2,2
EMP_ID3,EMP_FNAME3 EMP_LNAME3,EMP_STATUS3,3
EMP_ID4,EMP_FNAME4 EMP_LNAME4,EMP_STATUS4,4

The class structure of OldEmployee class

OldEmployee


package package16;

import java.math.BigDecimal;

public class OldEmployee {
    private String id;
    private String name;
    private String status;
    private BigDecimal salary;
    
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getStatus() {
        return status;
    }
    public void setStatus(String status) {
        this.status = status;
    }
    public BigDecimal getSalary() {
        return salary;
    }
    public void setSalary(BigDecimal salary) {
        this.salary = salary;
    }
}

and written as records shown above.

The below records are of string format of NewEmployee class instances, which has the concept of first and last name

EMP_ID0,EMP_FNAME0,EMP_LNAME0,EMP_STATUS0,0
EMP_ID1,EMP_FNAME1,EMP_LNAME1,EMP_STATUS1,1
EMP_ID2,EMP_FNAME2,EMP_LNAME2,EMP_STATUS2,2
EMP_ID3,EMP_FNAME3,EMP_LNAME3,EMP_STATUS3,3
EMP_ID4,EMP_FNAME4,EMP_LNAME4,EMP_STATUS4,4

The class structure of NewEmployee class

NewEmployee


package package16;

import java.math.BigDecimal;

public class NewEmployee {
    private String id;
    private String fname;
    private String lname;
    private String status;
    private BigDecimal salary;
    
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getFname() {
        return fname;
    }
    public void setFname(String fname) {
        this.fname = fname;
    }
    public String getLname() {
        return lname;
    }
    public void setLname(String lname) {
        this.lname = lname;
    }
    public String getStatus() {
        return status;
    }
    public void setStatus(String status) {
        this.status = status;
    }
    public BigDecimal getSalary() {
        return salary;
    }
    public void setSalary(BigDecimal salary) {
        this.salary = salary;
    }
}

The ItemProcessor for our example which is EmployeeItemProcessor will
1) read the input record converted to OldEmployee instances
2) split the name field into first name and last name
3) create a NewEmployee instance, sets the fields
4) return the NewEmployee instance.

Below is the complete code of EmployeeItemProcessor

EmployeeItemProcessor


package package16;

import org.springframework.batch.item.ItemProcessor;

public class EmployeeItemProcessor implements ItemProcessor {
    @Override
    public NewEmployee process(OldEmployee oldEmployee) throws Exception {
        NewEmployee newEmployee = new NewEmployee();
        newEmployee.setId(oldEmployee.getId());
        
        String fName = oldEmployee.getName().split(" ")[0].trim();
        String lName = oldEmployee.getName().split(" ")[1].trim();
        newEmployee.setFname(fName);
        newEmployee.setLname(lName);
        
        newEmployee.setSalary(oldEmployee.getSalary());
        newEmployee.setStatus(oldEmployee.getStatus());
        return newEmployee;
    }
}

In this way each input record of type OldEmployee is transformed into records of type NewEmployee.

Below is the main code

Main Code


package package16;

import java.util.Date;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.batch.core.repository.JobRestartException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Example16 {
    public static void main(String[] args) throws JobParametersInvalidException, JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobRestartException {
        ApplicationContext context = new ClassPathXmlApplicationContext("package16\\job.xml");
        JobLauncher jobLauncher = (JobLauncher)context.getBean("jobLauncher");
        JobParameters jobParameters = new JobParametersBuilder().addDate("date", new Date()).toJobParameters();
        Job job = (Job)context.getBean("importProductsJob");
        
        jobLauncher.run(job, jobParameters);
    }
}

Below is the complete xml configuration


<?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:batch="http://www.springframework.org/schema/batch"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch.xsd">
    
    <bean id="employee" class="package16.OldEmployee" scope="prototype"/>
    
    <bean id="reader" class="org.springframework.batch.item.file.FlatFileItemReader">
        <property name="resource" value="file:FileInput3.txt"/>
        <property name="lineMapper">
            <bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
                <property name="lineTokenizer">
                    <bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
                        <property name="names" value="id,name,status,salary"/>
                    </bean>
                </property>
                <property name="fieldSetMapper">
                    <bean class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
                        <property name="prototypeBeanName" value="employee"/>
                    </bean>
                </property>
            </bean>
        </property> 
    </bean>
    
    <bean id="writer" class="org.springframework.batch.item.file.FlatFileItemWriter">
        <property name="resource" value="file:FileOutput1.txt"/>
        <property name="lineAggregator">
            <bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
                <property name="fieldExtractor">
                    <bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
                        <property name="names" value="id,fname,lname,status,salary"/>
                    </bean>
                </property>
            </bean>
        </property>
    </bean>
    
    <bean id="employeeItemProcessor" class="package16.EmployeeItemProcessor" />
    
    <batch:job id="importProductsJob">
        <batch:step id="readWriteProducts">
            <batch:tasklet>
                <batch:chunk reader="reader" writer="writer" processor="employeeItemProcessor" commit-interval="50"/>
            </batch:tasklet>
        </batch:step>
    </batch:job>
    
    <bean id="transactionManager" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
    
    <bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
        <property name="transactionManager" ref="transactionManager"/>
    </bean>
    
    <bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
        <property name="jobRepository" ref="jobRepository"/>
    </bean>
</beans>

Check these products on Amazon

Leave a Reply