Spring Top – Temp

Get started with Spring 5 and Spring Boot 2, through the reference Learn Spring course:

Lightrun – Third Party Code
announcement - icon

Flakiness in REST requests is a common issue. A request can get a 200 OK in one scenario and a 409 next time. Sometimes a request can even succeed and fail intermittently on the same exact request. In short, working over HTTP can be a bit of a mess without solid tooling.

Also, while it’s easy enough to debug these issues locally when developing the application, we’re talking about production here - we can’t afford the downtime while you’re stepping in and out of code. Uptime is kind of the whole point.

With Lightrun, you can get the same level of access you get with a local debugger or profiler - no downtime required. You can add logs, metrics, and snapshots (think breakpoints, but without stopping the running service), in a safe and read-only manner - without redeploying, restarting, or even stopping the running service. Performance and security are maintained throughout the process.

Learn how to debug a live REST API (built with Spring, of course), using Lightrun, in this 5-minute tutorial:

>> Debugging REST Requests in Spring-Based applications using the Lightrun Platform

1. Overview

In this tutorial, we're going to look at a practical, code-focused intro to Spring Batch. Spring Batch is a processing framework designed for robust execution of jobs.

It's current version 4.3 supports Spring 5 and Java 8. It also accommodates JSR-352, which is the new java specification for batch processing.

Here are a few interesting and practical use cases of the framework.

2. Workflow Basics

Spring Batch follows the traditional batch architecture where a job repository does the work of scheduling and interacting with the job.

A job can have more than one step. And every step typically follows the sequence of reading data, processing it and writing it.

And of course the framework will do most of the heavy lifting for us here — especially when it comes to the low-level persistence work of dealing with the jobs — using sqlite for the job repository.

2.1. Example Use Case

The simple use case we're going to tackle here is migrating some financial transaction data from CSV to XML.

The input file has a very simple structure.

It contains a transaction per line, made up of a username, the user id, the date of the transaction and the amount:

username, userid, transaction_date, transaction_amount
devendra, 1234, 31/10/2015, 10000
john, 2134, 3/12/2015, 12321
robin, 2134, 2/02/2015, 23411

3. The Maven POM

Dependencies required for this project are spring core, spring batch and sqlite jdbc connector:

        <!-- SQLite database driver -->

4. Spring Batch Config

The first thing we'll do is configure Spring Batch with XML:

    <!-- connect to SQLite database -->
    <bean id="dataSource"
        <property name="driverClassName" value="org.sqlite.JDBC" />
        <property name="url" value="jdbc:sqlite:repository.sqlite" />
        <property name="username" value="" />
        <property name="password" value="" />

    <!-- create job-meta tables automatically -->
    <jdbc:initialize-database data-source="dataSource">
          location="org/springframework/batch/core/schema-drop-sqlite.sql" />
        <jdbc:script location="org/springframework/batch/core/schema-sqlite.sql" />

    <!-- stored job-meta in memory -->
    <bean id="jobRepository" 
        <property name="transactionManager" ref="transactionManager" />

    <!-- stored job-meta in database -->
    <bean id="jobRepository"
        <property name="dataSource" ref="dataSource" />
        <property name="transactionManager" ref="transactionManager" />
        <property name="databaseType" value="sqlite" />

    <bean id="transactionManager" class=
      "org.springframework.batch.support.transaction.ResourcelessTransactionManager" />

    <bean id="jobLauncher"
        <property name="jobRepository" ref="jobRepository" />

Of course, a Java configuration is also available:

public class SpringConfig {

    private Resource dropReopsitoryTables;

    private Resource dataReopsitorySchema;

    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        return dataSource;

    public DataSourceInitializer dataSourceInitializer(DataSource dataSource)
      throws MalformedURLException {
        ResourceDatabasePopulator databasePopulator = 
          new ResourceDatabasePopulator();


        DataSourceInitializer initializer = new DataSourceInitializer();

        return initializer;

    private JobRepository getJobRepository() throws Exception {
        JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
        return (JobRepository) factory.getObject();

    private PlatformTransactionManager getTransactionManager() {
        return new ResourcelessTransactionManager();

    public JobLauncher getJobLauncher() throws Exception {
        SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
        return jobLauncher;

5. Spring Batch Job Config

Let's now write our job description for the CSV to XML work:

    <import resource="spring.xml" />

    <bean id="record" class="com.baeldung.spring_batch_intro.model.Transaction"></bean>
    <bean id="itemReader"

        <property name="resource" value="input/record.csv" />

        <property name="lineMapper">
            <bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
                <property name="lineTokenizer">
                    <bean class=
                        <property name="names" value="username,userid,transactiondate,amount" />
                <property name="fieldSetMapper">
                    <bean class="com.baeldung.spring_batch_intro.service.RecordFieldSetMapper" />

    <bean id="itemProcessor"
      class="com.baeldung.spring_batch_intro.service.CustomItemProcessor" />

    <bean id="itemWriter"
        <property name="resource" value="file:xml/output.xml" />
        <property name="marshaller" ref="recordMarshaller" />
        <property name="rootTagName" value="transactionRecord" />

    <bean id="recordMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
        <property name="classesToBeBound">
    <batch:job id="firstBatchJob">
        <batch:step id="step1">
                <batch:chunk reader="itemReader" writer="itemWriter"
                  processor="itemProcessor" commit-interval="10">

And here's the similar Java-based job config:

public class SpringBatchConfig {
    private JobBuilderFactory jobs;

    private StepBuilderFactory steps;

    private Resource inputCsv;

    private Resource outputXml;

    public ItemReader<Transaction> itemReader()
      throws UnexpectedInputException, ParseException {
        FlatFileItemReader<Transaction> reader = new FlatFileItemReader<Transaction>();
        DelimitedLineTokenizer tokenizer = new DelimitedLineTokenizer();
        String[] tokens = { "username", "userid", "transactiondate", "amount" };
        DefaultLineMapper<Transaction> lineMapper = 
          new DefaultLineMapper<Transaction>();
        lineMapper.setFieldSetMapper(new RecordFieldSetMapper());
        return reader;

    public ItemProcessor<Transaction, Transaction> itemProcessor() {
        return new CustomItemProcessor();

    public ItemWriter<Transaction> itemWriter(Marshaller marshaller)
      throws MalformedURLException {
        StaxEventItemWriter<Transaction> itemWriter = 
          new StaxEventItemWriter<Transaction>();
        return itemWriter;

    public Marshaller marshaller() {
        Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
        marshaller.setClassesToBeBound(new Class[] { Transaction.class });
        return marshaller;

    protected Step step1(ItemReader<Transaction> reader,
      ItemProcessor<Transaction, Transaction> processor,
      ItemWriter<Transaction> writer) {
        return steps.get("step1").<Transaction, Transaction> chunk(10)

    @Bean(name = "firstBatchJob")
    public Job job(@Qualifier("step1") Step step1) {
        return jobs.get("firstBatchJob").start(step1).build();

Now that we have the whole config, let's break it down and start discussing it.

5.1. Read Data and Create Objects With ItemReader

First, we configured the cvsFileItemReader that will read the data from the record.csv and convert it into the Transaction object:

@XmlRootElement(name = "transactionRecord")
public class Transaction {
    private String username;
    private int userId;
    private LocalDateTime transactionDate;
    private double amount;

    /* getters and setters for the attributes */

    public String toString() {
        return "Transaction [username=" + username + ", userId=" + userId
          + ", transactionDate=" + transactionDate + ", amount=" + amount
          + "]";

To do so, it uses a custom mapper:

public class RecordFieldSetMapper implements FieldSetMapper<Transaction> {
    public Transaction mapFieldSet(FieldSet fieldSet) throws BindException {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d/M/yyy");
        Transaction transaction = new Transaction();
        String dateString = fieldSet.readString(2);
        transaction.setTransactionDate(LocalDate.parse(dateString, formatter).atStartOfDay());
        return transaction;

5.2. Processing Data With ItemProcessor

We have created our own item processor, CustomItemProcessor. This doesn't process anything related to the transaction object.

All it does is pass the original object coming from reader to the writer:

public class CustomItemProcessor implements ItemProcessor<Transaction, Transaction> {

    public Transaction process(Transaction item) {
        return item;

5.3. Writing Objects to the FS With ItemWriter

Finally, we are going to store this transaction into an XML file located at xml/output.xml:

<bean id="itemWriter"
    <property name="resource" value="file:xml/output.xml" />
    <property name="marshaller" ref="recordMarshaller" />
    <property name="rootTagName" value="transactionRecord" />

5.4. Configuring the Batch Job

So, all we have to do is connect the dots with a job using the batch:job syntax.

Note the commit-interval. That's the number of transactions to be kept in memory before committing the batch to the itemWriter.

It will hold the transactions in memory until that point (or until the end of the input data is encountered):

<batch:job id="firstBatchJob">
    <batch:step id="step1">
            <batch:chunk reader="itemReader" writer="itemWriter"
              processor="itemProcessor" commit-interval="10">

5.5. Running the Batch Job

Now let's set up and run everything:

public class App {
    public static void main(String[] args) {
        // Spring Java config
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        JobLauncher jobLauncher = (JobLauncher) context.getBean("jobLauncher");
        Job job = (Job) context.getBean("firstBatchJob");
        System.out.println("Starting the batch job");
        try {
            JobExecution execution = jobLauncher.run(job, new JobParameters());
            System.out.println("Job Status : " + execution.getStatus());
            System.out.println("Job completed");
        } catch (Exception e) {
            System.out.println("Job failed");

6. Conclusion

This article illustrated how to work with Spring Batch and how to use it in a simple use case.

It shows how we can easily develop our batch processing pipeline and how we can customize different stages in reading, processing and writing.

The full implementation of this article can be found in the GitHub project. This is an Eclipse-based project, so it should be easy to import and run as it is.

Spring bottom

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

Generic footer banner
Comments are closed on this article!