How to start with Spring Batch using Spring Boot – Java Config

Many business operations need to process with batch job for critical environment. Spring Batch is a lightweight framework to boot the batch application. The tutorial will guide you how to start with Spring Batch using Spring Boot.

Related Articles:
How to start with Spring Batch using Spring Boot – XML Config
How to use Intercepting Job Execution in Spring Batch
How to use Spring Batch Restartable Function
Spring Batch – Programmatic Flow Decision
How to import CSV data to PostgreSQL Database using Spring Batch Job
How to configure Spring Batch Step for restart
How to use Spring Batch Late Binding – Step Scope & Job Scope
Spring Batch Partition for Scaling & Parallel Processing

I. Technologies for Spring Batch tutorial

– Java 1.8
– Maven 3.3.9
– Spring Tool Suite – Version 3.8.1.RELEASE
– Spring Boot: 1.4.0.RELEASE

II. Overview

1. Structure of project

spring batch spring boot - structure of project

2. Step to do

– Create Spring Boot project
– Add needed dependencies
– Configure datasource for Batch Job Repository
– Create Step: Reader, Processor, Writer
– Configure Batch Job
– Create a Controller for launch Job
– Run & Check result

III. Practices

1. Create Spring Boot project

Open Spring Tool Suite, on main menu, choose File->New->Spring Starter Project, input project info as below picture:

spring batch - input info

Press Next -> Finish, Spring Boot project is created.

2. Add needed dependencies

– Add dependencies: Web MVC, Spring Batch, PostgreSQL

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-test</artifactId>
	<scope>test</scope>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-batch</artifactId>
</dependency>

<!-- http://mvnrepository.com/artifact/postgresql/postgresql -->
<dependency>
    <groupId>postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>9.1-901-1.jdbc4</version>
</dependency>

3. Config datasource for Batch Job repository

Open application.propeties, add datasource info:


spring.datasource.url=jdbc:postgresql://grokonez.com/testdb
spring.datasource.username=postgres
spring.datasource.password=123
spring.batch.job.enabled=false

4. Create Step: Reader, Processor, Writer

– Create a simple Reader.java class: public interface ItemReader: is used to get data by read() method. The read() method is called repeatedly then returning a difference value for each time. If all data are consumed, a null value will be returned.


package com.javasampleapproach.springbatch.step;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 Reader implements ItemReader{

	private String[] messages = {"Hello World!", "Welcome to Spring Batch!"};
	
	private int count=0;
	
	Logger logger = LoggerFactory.getLogger(this.getClass());
	
	@Override
	public String read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
		
		if(count < messages.length){
			return messages[count++];
		}else{
			count=0;
		}
		return null;
	}
	
}

- Create a simple Processor.java class: public interface ItemProcessor is used for transforming data. We can provide input and output data type for each ItemProcessor via . A null returning is a finally signal of the processing.


package com.javasampleapproach.springbatch.step;

import org.springframework.batch.item.ItemProcessor;

public class Processor implements ItemProcessor{

	@Override
	public String process(String content) throws Exception {
		return content.toUpperCase();
	}

}

- Create a simple Writer.java class: public interface ItemWriter is used to generate output of batch processing. The write() method get a list of items from batch reader (or batch processor) to process. The data can be persisted to database or other storages, or simple show on a console...


package com.javasampleapproach.springbatch.step;

import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.item.ItemWriter;

public class Writer implements ItemWriter {

	Logger logger = LoggerFactory.getLogger(this.getClass());
	
	@Override
	public void write(List messages) throws Exception {
		for(String msg : messages){
			System.out.println("#Writer Step: " + msg);
		}
	}
	
}

5. Enable Spring Batch

In main class, use @EnableBatchProcessing: Enable Spring Batch features and provide a base configuration for setting up batch jobs in an @Configuration class.


package com.javasampleapproach.springbatch;

import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableBatchProcessing
public class SprinpBatchApplication {
	public static void main(String[] args) {
		SpringApplication.run(SprinpBatchApplication.class, args);
	}
}

6. Configure Batch Job

Create a configuration file and enable Spring batch via annotations: @Configuration


package com.javasampleapproach.springbatch.config;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.javasampleapproach.springbatch.step.Processor;
import com.javasampleapproach.springbatch.step.Reader;
import com.javasampleapproach.springbatch.step.Writer;

@Configuration
public class BatchConfig {

    @Autowired
    public JobBuilderFactory jobBuilderFactory;

    @Autowired
    public StepBuilderFactory stepBuilderFactory;

    
	@Bean
    public Job job() {
        return jobBuilderFactory.get("job")
                .incrementer(new RunIdIncrementer())
                .flow(step1())
                .end()
                .build();
    }

    @Bean
    public Step step1() {
        return stepBuilderFactory.get("step1")
                . chunk(1)
                .reader(new Reader())
                .processor(new Processor())
                .writer(new Writer())
                .build();
    }

7. Create a Controller for launch Job

Create a simple controller JobLauncherController with 1 @RequestMapping("/launchjob")


package com.javasampleapproach.springbatch.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class JobLauncherController {

	@Autowired
	JobLauncher jobLauncher;

	@Autowired
	Job job;

	@RequestMapping("/launchjob")
	public String handle() throws Exception {

		Logger logger = LoggerFactory.getLogger(this.getClass());
		try {
			JobParameters jobParameters = new JobParametersBuilder().addLong("time", System.currentTimeMillis())
					.toJobParameters();
			jobLauncher.run(job, jobParameters);
		} catch (Exception e) {
			logger.info(e.getMessage());
		}

		return "Done";
	}
}

8. Run & Check result

- Maven build:
clean install

- Run project with mode: Spring Boot App
Make a request:
http://localhost:8080/launchjob

- Log:


o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=job]] launched with the following parameters: [{time=1472822896329}]
o.s.batch.core.job.SimpleStepHandler     : Executing step: [step1]
#Writer Step: HELLO WORLD!
#Writer Step: WELCOME TO SPRING BATCH!
o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=job]] completed with the following parameters:[{time=1472822896329}] and the following status: [COMPLETED]

See batch job tables in database:

spring batch - job tables

IV. Source code

SpringBatch

Related Article: How to start with Spring Batch using Spring Boot - XML Config



By grokonez | September 2, 2016.

Last updated on April 26, 2021.



Related Posts


7 thoughts on “How to start with Spring Batch using Spring Boot – Java Config”

  1. org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘jobLauncherController’: Unsatisfied dependency expressed through field ‘job’; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘job’ defined in class path resource [com/Batch/BatchConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.batch.core.Job]: Factory method ‘job’ threw exception; nested exception is org.springframework.beans.factory.

    1. @Messi – Annotate JobLauncherController the with @EnableScheduling , UnsatisfiedDependencyException: should go away .. @EnableScheduling – autowires job launcher and some other features for free. for more info you can read on that annotation

  2. Can you explain how to read XML file and print it on console. I am a beginner to this Spring and I request you to provide me step by step procedure as shown in above example . Thanks in advance! Hoping a reply.

  3. Can you explain how to insert a Date datatype into postgres? I’m running into an error I can’t seem to figure out:

    Caused by: org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
    Field error in object ‘target’ on field ‘clockInDate’: rejected value [2019-02-19]; codes [typeMismatch.target.clockInDate,typeMismatch.clockInDate,typeMismatch.java.util.Date,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [target.clockInDate,clockInDate]; arguments []; default message [clockInDate]]; default message [Failed to convert property value of type ‘java.lang.String’ to required type ‘java.util.Date’ for property ‘clockInDate’; nested exception is java.lang.IllegalStateException: Cannot convert value of type ‘java.lang.String’ to required type ‘java.util.Date’ for property ‘clockInDate’: no matching editors or conversion strategy found]

    Thanks!

    1. Let me rephrase, my csv file has a field named clockInDate that is being inserted into a postgres column of type Date

  4. 2021-01-17 09:52:07.479 INFO 33626 — [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.35]
    2021-01-17 09:52:07.528 INFO 33626 — [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
    2021-01-17 09:52:07.529 INFO 33626 — [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1011 ms
    2021-01-17 09:52:07.597 INFO 33626 — [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 – Starting…
    2021-01-17 09:52:07.626 INFO 33626 — [ main] com.zaxxer.hikari.pool.PoolBase : HikariPool-1 – Driver does not support get/set network timeout for connections. (Método org.postgresql.jdbc4.Jdbc4Connection.getNetworkTimeout() ainda não foi implementado.)
    2021-01-17 09:52:07.638 INFO 33626 — [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 – Start completed.
    2021-01-17 09:52:07.675 INFO 33626 — [ main] o.s.aop.framework.CglibAopProxy : Unable to proxy interface-implementing method [public final void org.springframework.dao.support.DaoSupport.afterPropertiesSet() throws java.lang.IllegalArgumentException,org.springframework.beans.factory.BeanInitializationException] because it is marked as final: Consider using interface-based JDK proxies instead!
    2021-01-17 09:52:07.836 INFO 33626 — [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService ‘applicationTaskExecutor’
    2021-01-17 09:52:07.934 INFO 33626 — [ main] o.s.b.c.r.s.JobRepositoryFactoryBean : No database type set, using meta data indicating: POSTGRES
    2021-01-17 09:52:07.942 INFO 33626 — [ main] o.s.b.c.l.support.SimpleJobLauncher : No TaskExecutor has been set, defaulting to synchronous executor.
    2021-01-17 09:52:07.998 INFO 33626 — [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ”
    2021-01-17 09:52:08.015 INFO 33626 — [ main] .j.b.SpringBatchCsvPostgreSqlApplication : Started SpringBatchCsvPostgreSqlApplication in 1.849 seconds (JVM running for 2.405)
    2021-01-17 09:52:18.419 INFO 33626 — [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet ‘dispatcherServlet’
    2021-01-17 09:52:18.419 INFO 33626 — [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet ‘dispatcherServlet’
    2021-01-17 09:52:18.423 INFO 33626 — [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 3 ms
    2021-01-17 09:52:18.518 INFO 33626 — [nio-8080-exec-1] c.j.b.controller.WebController : PreparedStatementCallback; bad SQL grammar [SELECT JOB_INSTANCE_ID, JOB_NAME from BATCH_JOB_INSTANCE where JOB_NAME = ? and JOB_KEY = ?]; nested exception is org.postgresql.util.PSQLException: ERROR: relation “batch_job_instance” does not exist
    Posição: 39
    2021-01-17 09:52:33.930 INFO 33626 — [nio-8080-exec-4] c.j.b.controller.WebController : PreparedStatementCallback; bad SQL grammar [SELECT JOB_INSTANCE_ID, JOB_NAME from BATCH_JOB_INSTANCE where JOB_NAME = ? and JOB_KEY = ?]; nested exception is org.postgresql.util.PSQLException: ERROR: relation “batch_job_instance” does not exist
    Posição: 39
    2021-01-17 09:52:36.388 INFO 33626 — [nio-8080-exec-7] c.j.b.controller.WebController : PreparedStatementCallback; bad SQL grammar [SELECT JOB_INSTANCE_ID, JOB_NAME from BATCH_JOB_INSTANCE where JOB_NAME = ? and JOB_KEY = ?]; nested exception is org.postgresql.util.PSQLException: ERROR: relation “batch_job_instance” does not exist
    Posição: 39
    2021-01-17 09:52:38.252 INFO 33626 — [nio-8080-exec-8] c.j.b.controller.WebController : PreparedStatementCallback; bad SQL grammar [SELECT JOB_INSTANCE_ID, JOB_NAME from BATCH_JOB_INSTANCE where JOB_NAME = ? and JOB_KEY = ?]; nested exception is org.postgresql.util.PSQLException: ERROR: relation “batch_job_instance” does not exist
    Posição: 39
    2021-01-17 09:52:54.155 INFO 33626 — [nio-8080-exec-1] c.j.b.controller.WebController : PreparedStatementCallback; bad SQL grammar [SELECT JOB_INSTANCE_ID, JOB_NAME from BATCH_JOB_INSTANCE where JOB_NAME = ? and JOB_KEY = ?]; nested exception is org.postgresql.util.PSQLException: ERROR: relation “batch_job_instance” does not exist
    Posição: 39

Got Something To Say:

Your email address will not be published. Required fields are marked *

*