Dec 7, 2013

Beginner Spring Batch reader and writer tutorial

Step 1: You need the relevant jar files. Here is the maven pom.xml snippet


Step 2: Define the Spring config file spring-batch-job.xml  with repository, launcher, job, step, reader, and writer. The reader and writer are defined within chunk element with the commit-interval being the chunk size. The reader reads one item say at a time and chunk it according to the chunk size (1 in this example) and pass it to the writer as List. The concurrency limit value of 3 means 3 threads can process in parallel.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns=""
 xmlns:p="" xmlns:xsi=""

 <!-- define the job repository -->
 <bean id="jobRepository"

 <!--define the launcher and pass the jobRepository as setter injection -->
 <bean id="jobLauncher"
  <property name="jobRepository" ref="jobRepository" />

 <!-- multi-threading -->
 <bean id="taskExecutor" class="org.springframework.core.task.SimpleAsyncTaskExecutor">
  <property name="concurrencyLimit" value="3" />

 <bean id="transactionManager"
  class="" />

 <job id="simpleJob" xmlns="">
  <step id="simpleStep">
   <tasklet task-executor="taskExecutor">
    <chunk reader="simpleReader" writer="simpleWriter"



 <bean name="simpleReader" scope="step" class="com.myapp.SimpleReader" />

 <bean name="simpleWriter" scope="step" class="com.myapp.SimpleWriter" />


Step 3: Define the custom reader SimpleReader. Hard coded the list to keep it simple. In real life, read from a data source like database or file. T is String here.

package com.myapp;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.NonTransientResourceException;
import org.springframework.batch.item.ParseException;
import org.springframework.batch.item.UnexpectedInputException;

public class SimpleReader implements ItemReader<String> {

 // creates an unmodifiable list
 String[] itemArray = new String[] { "Java", "Spring", "Hibernate" };
 // creates a modifiable list
 List<String> items = new ArrayList<String>(Arrays.asList(itemArray));

 public String read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
  if (!items.isEmpty()) {
   return getNextItem();
  return null;

 // using items directly without lock is not thread safe
 private synchronized String getNextItem() {
  return items.remove(0);

Step 4: Define the custom writer SimpleWriter. List is List. Chunked and passed to the reader.
package com.myapp;

import java.util.List;

import org.springframework.batch.item.ItemWriter;

public class SimpleWriter implements ItemWriter<String> {

 public void write(List<? extends String> items) throws Exception {
  for (String item : items) {
   // prefix each tem with "my-"
   final String prefix = "My_";
   item = prefix + item;

   System.out.println(Thread.currentThread() + " writes: " +  item);

Step 5: The main class that runs as a stand-alone application.

package com.myapp;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
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;

public class SimpleBatchTest {

 public static void main(String[] args) throws JobExecutionAlreadyRunningException, JobRestartException,
   JobInstanceAlreadyCompleteException, JobParametersInvalidException {
  ApplicationContext appContext = new ClassPathXmlApplicationContext("classpath:/job-config/spring-batch-job.xml");

  //get the launcher
  JobLauncher jobLauncher = (JobLauncher) appContext.getBean("jobLauncher");
  //get the job to run
  Job job = (Job) appContext.getBean("simpleJob");
  //run, new JobParameters());


11:17:13.906 [main] INFO - Job: [FlowJob: [name=simpleJob]] launched with the following parameters: [{}]
11:17:13.925 [main] INFO  o.s.batch.core.job.SimpleStepHandler - Executing step: [simpleStep]
Thread[SimpleAsyncTaskExecutor-2,5,main] writes: My_Java
Thread[SimpleAsyncTaskExecutor-3,5,main] writes: My_Spring
Thread[SimpleAsyncTaskExecutor-1,5,main] writes: My_Hibernate
11:17:13.989 [main] INFO - Job: [FlowJob: [name=simpleJob]] completed with the following parameters: [{}] and the following status: [COMPLETED]

Running more than once can throw JobIsAlreadyRunningException. So, you need to supply different JobParameters as shown below.

JobParameters jobParameters = 
      new JobParametersBuilder()
      .addLong("time",System.currentTimeMillis()).toJobParameters();, jobParameters);

The above was run with 3 threads and commit-interval of 1. Experiment by changing the "commit-interval" (aka chunk size) within the step and the "concurrencyLimit" within the SimpleAsyncTaskExecutor in the spring config file.

More advanced blog posts on Spring batch



Post a Comment

Subscribe to Post Comments [Atom]

<< Home