Project Structure
BatchConfig.java
package com.example.batch.config;
import java.io.File;
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.item.data.RepositoryItemWriter;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.LineMapper;
import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
import org.springframework.batch.item.file.mapping.DefaultLineMapper;
import org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.FileSystemResource;
import com.example.batch.entity.Employee;
import com.example.batch.listener.JobListener;
import com.example.batch.listener.StepListener;
import com.example.batch.processor.EmployeeProcessor;
import com.example.batch.repository.EmployeeRepository;
@Configuration
@EnableBatchProcessing
public class BatchConfig {
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Autowired
private EmployeeRepository employeeRepository;
@Value("${location}")
private String location;
private String uploadFile = "";
@Autowired
private StepListener stepListener;
@Autowired
private JobListener jobListener;
public FlatFileItemReader<Employee> readDataFromFile() {
FlatFileItemReader<Employee> itemReader = new FlatFileItemReader();
FileSystemResource fileResource=new
FileSystemResource(location + File.separator + uploadFile);
itemReader.setResource(fileResource);
itemReader.setName("csvReader");
itemReader.setLinesToSkip(1);
itemReader.setLineMapper(lineMapper());
return itemReader;
}
private LineMapper<Employee> lineMapper() {
DefaultLineMapper<Employee> lineMapper = new DefaultLineMapper();
DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer();
lineTokenizer.setDelimiter(",");
lineTokenizer.setStrict(false);
lineTokenizer.setNames("First Name",
"Last Name", "Email", "Phone", "Gender", "Age", "Job Title",
"Years Of Experience", "Salary", "Department");
BeanWrapperFieldSetMapper<Employee> fieldSetMapper = new
BeanWrapperFieldSetMapper();
fieldSetMapper.setTargetType(Employee.class);
lineMapper.setLineTokenizer(lineTokenizer);
lineMapper.setFieldSetMapper(fieldSetMapper);
return lineMapper;
}
@Bean
public EmployeeProcessor employeeProcessor() {
return new EmployeeProcessor();
}
public RepositoryItemWriter<Employee> repositoryItemWriter() {
RepositoryItemWriter<Employee> repositoryItemWriter = new
RepositoryItemWriter();
repositoryItemWriter.setRepository(employeeRepository);
repositoryItemWriter.setMethodName("save");
return repositoryItemWriter;
}
public Step step1() {
return stepBuilderFactory.get("step1").
<Employee, Employee>chunk(10).reader(readDataFromFile())
.processor(employeeProcessor())
.writer(repositoryItemWriter()).
listener(stepListener).build();
}
public Job startJob() {
return jobBuilderFactory.get("saveEmployee").
listener(jobListener).flow(step1()).end().build();
}
public String getUploadFile() {
return uploadFile;
}
public void setUploadFile(String uploadFile) {
this.uploadFile = uploadFile;
}
}
AppController.java
package com.example.batch.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.example.batch.service.FileDetailService;
@RestController
@RequestMapping("/api/v1")
public class AppController {
@Autowired
private FileDetailService fileDetailService;
@PostMapping("/fileUpload")
public ResponseEntity<Object> uploadFile(@RequestParam("file")
MultipartFile file) {
return fileDetailService.uploadFileDetail(file);
}
}
Employee.java
package com.example.batch.entity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.Getter;
import lombok.Setter;
@Entity
@Table(name = "employee_records")
@Setter
@Getter
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String firstName;
private String lastName;
private String email;
private String phone;
private String gender;
private int age;
private String jobTitle;
private String yearsOfExperience;
private double salary;
private String department;
}
FileDetailEntity.java
package com.example.batch.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.Getter;
import lombok.Setter;
@Entity
@Table(name = "file_details")
@Setter
@Getter
public class FileDetailEntity {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
@Column(name = "file_name",unique = true)
private String fileName;
private String uploadDate;
}
JobListener.java
package com.example.batch.listener;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.stereotype.Component;
@Component
public class JobListener implements JobExecutionListener {
@Override
public void beforeJob(JobExecution jobExecution) {
System.out.println("Before Job....");
}
@Override
public void afterJob(JobExecution jobExecution) {
System.out.println("After Job....");
}
}
StepListener.java
package com.example.batch.listener;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.StepExecutionListener;
import org.springframework.stereotype.Component;
@Component
public class StepListener implements StepExecutionListener {
@Override
public void beforeStep(StepExecution stepExecution) {
System.out.println("Before Step....");
}
@Override
public ExitStatus afterStep(StepExecution stepExecution) {
System.out.println("After Step....");
return ExitStatus.COMPLETED;
}
}
WatcherServiceEvent.java
package com.example.batch.listener;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobExecution;
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.beans.factory.annotation.Value;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import com.example.batch.config.BatchConfig;
import lombok.extern.slf4j.Slf4j;
@Component
@Slf4j
public class WatcherServiceEvent implements
ApplicationListener<ApplicationReadyEvent> {
@Value("${location}")
private String location;
@Autowired
private BatchConfig batchConfig;
@Autowired
private JobLauncher jobLauncher;
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
log.info("Watcher service enable....");
watcherService();
}
private void watcherService() {
Path directory = Paths.get(location);
try (WatchService watchService = FileSystems.
getDefault().newWatchService()) {
directory.
register(watchService, StandardWatchEventKinds.ENTRY_CREATE);
while (true) {
WatchKey key = watchService.take();
for (WatchEvent event : key.pollEvents()) {
if (event.kind() == StandardWatchEventKinds.ENTRY_CREATE) {
Path filePath = directory.resolve((Path) event.context());
log.info("file " + filePath.getFileName());
if (filePath.getFileName().toString().
equals(event.context().toString())) {
startJob(event.context().toString());
}
}
}
key.reset();
}
} catch (Exception e) {
e.printStackTrace();
}
}
private BatchStatus startJob(String uploadFileDetail) {
JobParameters parameters = new
JobParametersBuilder().
addLong("StartAt", System.currentTimeMillis())
.toJobParameters();
JobExecution jobExecution = null;
try {
batchConfig.setUploadFile(uploadFileDetail);
jobExecution = jobLauncher.
run(batchConfig.startJob(), parameters);
if (jobExecution.getStatus().name().
equalsIgnoreCase("COMPLETED")) {
log.info("Completed.....");
}
} catch (Exception e) {
log.info("Failed...");
e.printStackTrace();
}
return jobExecution.getStatus();
}
}
EmployeeProcessor.java
package com.example.batch.processor;
import org.springframework.batch.item.ItemProcessor;
import com.example.batch.entity.Employee;
public class EmployeeProcessor implements
ItemProcessor<Employee, Employee>{
@Override
public Employee process(Employee employee)
throws Exception {
return employee;
}
}
EmployeeRepository.java
package com.example.batch.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.example.batch.entity.Employee;
@Repository
public interface EmployeeRepository extends
JpaRepository<Employee, Integer> {
}
FileDetailRepository.java
package com.example.batch.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.example.batch.entity.FileDetailEntity;
@Repository
public interface FileDetailRepository extends
JpaRepository<FileDetailEntity, Integer> {
}
FileDetailService.java
package com.example.batch.service;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import com.example.batch.entity.FileDetailEntity;
import com.example.batch.repository.FileDetailRepository;
import com.example.batch.utility.ResponseHandler;
@Service
public class FileDetailService {
@Value("${location}")
private String location;
@Autowired
private FileDetailRepository fileDetailRepository;
private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-mm-dd hh:mm:ss");
public ResponseEntity<Object> uploadFileDetail(MultipartFile file) {
try {
FileDetailEntity fileDetailEntity = new FileDetailEntity();
fileDetailEntity.setFileName(file.getOriginalFilename());
fileDetailEntity.setUploadDate(simpleDateFormat.format(new Date()));
fileDetailRepository.save(fileDetailEntity);
file.
transferTo(new File(location + File.separator + file.getOriginalFilename()));
return ResponseHandler.
generateResponse("Successfully File upload", HttpStatus.OK, null);
} catch (Exception e) {
e.printStackTrace();
return ResponseHandler.
generateResponse(e.getMessage(), HttpStatus.MULTI_STATUS, null);
}
}
}
ResponseHandler.java
package com.example.batch.utility;
import java.util.HashMap;
import java.util.Map;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
public class ResponseHandler {
public static ResponseEntity<Object>
generateResponse(String message, HttpStatus status, Object responseObj) {
Map<String, Object> map = new HashMap();
map.put("message", message);
map.put("status", status.value());
map.put("data", responseObj);
return new ResponseEntity<Object>(map, status);
}
}
SpringBatchExampleApplication.java
package com.example.batch;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBatchExampleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBatchExampleApplication.class, args);
}
}
application.properties
server.port=9090
#DATABSE CONFIGURATION HERE
spring.datasource.url=jdbc:mysql://localhost:3306/batchtutorial
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update
#BATCH CONFIGURATION HERE
spring.batch.initialize-schema=always
spring.batch.job.enabled=false
#SFTP SERVER INFO
location=F://upload
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>Spring-Batch-Example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Spring-Batch-Example</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Postman Request
Database Tables