package demo;
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.core.configuration.annotation.StepScope;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.FlatFileParseException;
import org.springframework.batch.item.file.LineMapper;
import org.springframework.batch.item.file.mapping.DefaultLineMapper;
import org.springframework.batch.item.file.mapping.FieldSetMapper;
import org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
import org.springframework.batch.item.file.transform.LineTokenizer;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.core.JdbcTemplate;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
/**
* This example is based on example in https://github.com/briansjavablog/spring-batch-tutorial.git,
* but uses the Spring Batch Java Configuration API and Spring Boot and Java 8.
*
* @author Brian Hannaway <hannawaybrian@googlemail.com>
* @author Josh Long
*/
@SpringBootApplication
@EnableBatchProcessing
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
Job job(JobBuilderFactory jobBuilderFactory, @Qualifier("start") Step start) {
return jobBuilderFactory.get("importAccountData")
.start(start)
.build();
}
@Bean
Step start(StepBuilderFactory stepBuilderFactory,
FlatFileItemReader<Account> flatFileItemReader,
ItemWriter<Account> accountItemWriter) {
return stepBuilderFactory.get("parseAndLoadAccountData").
<Account, Account>chunk(3)
.faultTolerant()
.skip(FlatFileParseException.class)
.skipLimit(2)
.reader(flatFileItemReader)
.writer(accountItemWriter)
.build();
}
@Bean
@StepScope
FlatFileItemReader<Account> flatFileItemReader(
LineMapper<Account> accountDefaultLineMapper,
@Value("file:#{jobParameters['inputResource']}") Resource resource) {
FlatFileItemReader<Account> accountFlatFileItemReader = new FlatFileItemReader<>();
accountFlatFileItemReader.setLinesToSkip(1);
accountFlatFileItemReader.setLineMapper(accountDefaultLineMapper);
accountFlatFileItemReader.setResource(resource);
return accountFlatFileItemReader;
}
@Bean
DelimitedLineTokenizer delimitedLineTokenizer() {
DelimitedLineTokenizer delimitedLineTokenizer = new DelimitedLineTokenizer(",");
delimitedLineTokenizer.setNames("ACCOUNT_ID,ACCOUNT_HOLDER_NAME,ACCOUNT_CURRENCY,BALANCE".split(","));
return delimitedLineTokenizer;
}
@Bean
DefaultLineMapper<Account> accountDefaultLineMapper(LineTokenizer lt, FieldSetMapper<Account> accountFieldSetMapper) {
DefaultLineMapper<Account> accountDefaultLineMapper = new DefaultLineMapper<>();
accountDefaultLineMapper.setFieldSetMapper(accountFieldSetMapper);
accountDefaultLineMapper.setLineTokenizer(lt);
return accountDefaultLineMapper;
}
@Bean
ItemWriter<Account> accountItemWriter(JdbcTemplate jdbcTemplate) {
return (List<? extends Account> accounts) -> {
String insertAccount = "insert into account (ACCOUNT_ID, ACCOUNT_HOLDER_NAME, ACCOUNT_CURRENCY, BALANCE) values(?,?,?,?)";
String updateAccount = "update account set ACCOUNT_HOLDER_NAME=?, ACCOUNT_CURRENCY=?, BALANCE=? where id = ?";
accounts.forEach(a -> {
int updated = jdbcTemplate.update(updateAccount, a.getAccountHolderName(), a.getAccountCurrency(), a.getBalance(), a.getId());
if (updated == 0) {
jdbcTemplate.update(insertAccount, a.getId(), a.getAccountHolderName(), a.getAccountCurrency(), a.getBalance());
}
});
};
}
@Bean
FieldSetMapper<Account> accountFieldSetMapper() {
return (fieldSet) -> new Account(
fieldSet.readString("ACCOUNT_ID"),
fieldSet.readString("ACCOUNT_HOLDER_NAME"),
fieldSet.readString("ACCOUNT_CURRENCY"),
fieldSet.readBigDecimal("BALANCE"));
}
}
class Account implements Serializable {
private static final long serialVersionUID = -3166540015278455392L;
private String id;
private String accountHolderName;
private String accountCurrency;
private BigDecimal balance;
public String getId() {
return id;
}
public String getAccountHolderName() {
return accountHolderName;
}
public String getAccountCurrency() {
return accountCurrency;
}
public BigDecimal getBalance() {
return balance;
}
public Account(String id, String accountHolderName, String accountCurrency, BigDecimal balance) {
this.id = id;
this.accountHolderName = accountHolderName;
this.accountCurrency = accountCurrency;
this.balance = balance;
}
@Override
public String toString() {
return "Account [id=" + id + ", accountHolderName=" + accountHolderName + ", accountCurrency=" + accountCurrency + ", balance=" + balance + "]";
}
}