diff --git a/spring-batch-intro/.classpath b/spring-batch-intro/.classpath new file mode 100644 index 0000000000..395dbde027 --- /dev/null +++ b/spring-batch-intro/.classpath @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-batch-intro/.project b/spring-batch-intro/.project new file mode 100644 index 0000000000..032f8a9541 --- /dev/null +++ b/spring-batch-intro/.project @@ -0,0 +1,23 @@ + + + spring-batch-intro + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/spring-batch-intro/pom.xml b/spring-batch-intro/pom.xml new file mode 100644 index 0000000000..28d48c594e --- /dev/null +++ b/spring-batch-intro/pom.xml @@ -0,0 +1,44 @@ + + 4.0.0 + + org.baeldung + spring-batch-intro + 0.1-SNAPSHOT + jar + + spring-batch-intro + http://maven.apache.org + + + UTF-8 + 4.2.0.RELEASE + 3.0.5.RELEASE + 3.8.11.2 + + + + + + org.xerial + sqlite-jdbc + ${sqlite.version} + + + org.springframework + spring-oxm + ${spring.version} + + + org.springframework + spring-jdbc + ${spring.version} + + + org.springframework.batch + spring-batch-core + ${spring.batch.version} + + + + diff --git a/spring-batch-intro/src/main/java/org/baeldung/spring_batch_intro/App.java b/spring-batch-intro/src/main/java/org/baeldung/spring_batch_intro/App.java new file mode 100644 index 0000000000..d6af37595c --- /dev/null +++ b/spring-batch-intro/src/main/java/org/baeldung/spring_batch_intro/App.java @@ -0,0 +1,36 @@ +package org.baeldung.spring_batch_intro; + +import org.springframework.batch.core.Job; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.JobParameters; +import org.springframework.batch.core.launch.JobLauncher; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + + +public class App { + public static void main(String[] args) { + // Spring Java config + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(SpringConfig.class); + context.register(SpringBatchConfig.class); + context.refresh(); + + // Spring xml config + // ApplicationContext context = new ClassPathXmlApplicationContext( + // "spring-batch-intro.xml"); + + 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 succeeded"); + } catch (Exception e) { + e.printStackTrace(); + System.out.println("Job failed"); + } + } +} \ No newline at end of file diff --git a/spring-batch-intro/src/main/java/org/baeldung/spring_batch_intro/SpringBatchConfig.java b/spring-batch-intro/src/main/java/org/baeldung/spring_batch_intro/SpringBatchConfig.java new file mode 100644 index 0000000000..a024cbc04e --- /dev/null +++ b/spring-batch-intro/src/main/java/org/baeldung/spring_batch_intro/SpringBatchConfig.java @@ -0,0 +1,93 @@ +package org.baeldung.spring_batch_intro; + +import java.net.MalformedURLException; +import java.text.ParseException; + +import org.baeldung.spring_batch_intro.model.Transaction; +import org.baeldung.spring_batch_intro.service.CustomItemProcessor; +import org.baeldung.spring_batch_intro.service.RecordFieldSetMapper; +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.item.ItemProcessor; +import org.springframework.batch.item.ItemReader; +import org.springframework.batch.item.ItemWriter; +import org.springframework.batch.item.UnexpectedInputException; +import org.springframework.batch.item.file.FlatFileItemReader; +import org.springframework.batch.item.file.mapping.DefaultLineMapper; +import org.springframework.batch.item.file.transform.DelimitedLineTokenizer; +import org.springframework.batch.item.xml.StaxEventItemWriter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.core.io.Resource; +import org.springframework.oxm.Marshaller; +import org.springframework.oxm.jaxb.Jaxb2Marshaller; + +public class SpringBatchConfig { + @Autowired + private JobBuilderFactory jobs; + + @Autowired + private StepBuilderFactory steps; + + @Value("input/record.csv") + private Resource inputCsv; + + @Value("file:xml/output.xml") + private Resource outputXml; + + @Bean + public ItemReader itemReader() + throws UnexpectedInputException, ParseException { + FlatFileItemReader reader = new FlatFileItemReader(); + DelimitedLineTokenizer tokenizer = new DelimitedLineTokenizer(); + String[] tokens = { "username", "userid", "transactiondate", "amount" }; + tokenizer.setNames(tokens); + reader.setResource(inputCsv); + DefaultLineMapper lineMapper = new DefaultLineMapper(); + lineMapper.setLineTokenizer(tokenizer); + lineMapper.setFieldSetMapper(new RecordFieldSetMapper()); + reader.setLinesToSkip(1); + reader.setLineMapper(lineMapper); + return reader; + } + + @Bean + public ItemProcessor itemProcessor() { + return new CustomItemProcessor(); + } + + @Bean + public ItemWriter itemWriter(Marshaller marshaller) + throws MalformedURLException { + StaxEventItemWriter itemWriter = new StaxEventItemWriter(); + itemWriter.setMarshaller(marshaller); + itemWriter.setRootTagName("transactionRecord"); + itemWriter.setResource(outputXml); + return itemWriter; + } + + @Bean + public Marshaller marshaller() { + Jaxb2Marshaller marshaller = new Jaxb2Marshaller(); + marshaller.setClassesToBeBound(new Class[] { Transaction.class }); + return marshaller; + } + + @Bean + protected Step step1(ItemReader reader, + ItemProcessor processor, + ItemWriter writer) { + return steps.get("step1"). chunk(10) + .reader(reader).processor(processor).writer(writer).build(); + } + + @Bean(name = "firstBatchJob") + public Job job(@Qualifier("step1") Step step1) { + return jobs.get("firstBatchJob").start(step1).build(); + } + +} diff --git a/spring-batch-intro/src/main/java/org/baeldung/spring_batch_intro/SpringConfig.java b/spring-batch-intro/src/main/java/org/baeldung/spring_batch_intro/SpringConfig.java new file mode 100644 index 0000000000..91366d8721 --- /dev/null +++ b/spring-batch-intro/src/main/java/org/baeldung/spring_batch_intro/SpringConfig.java @@ -0,0 +1,79 @@ +package org.baeldung.spring_batch_intro; + +import java.net.MalformedURLException; + +import javax.sql.DataSource; + +import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; +import org.springframework.batch.core.launch.JobLauncher; +import org.springframework.batch.core.launch.support.SimpleJobLauncher; +import org.springframework.batch.core.repository.JobRepository; +import org.springframework.batch.core.repository.support.JobRepositoryFactoryBean; +import org.springframework.batch.support.transaction.ResourcelessTransactionManager; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.Resource; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.springframework.jdbc.datasource.init.DataSourceInitializer; +import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; +import org.springframework.transaction.PlatformTransactionManager; + +@Configuration +@EnableBatchProcessing +public class SpringConfig { + + @Value("org/springframework/batch/core/schema-drop-sqlite.sql") + private Resource dropReopsitoryTables; + + @Value("org/springframework/batch/core/schema-sqlite.sql") + private Resource dataReopsitorySchema; + + @Bean + public DataSource dataSource() { + DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName("org.sqlite.JDBC"); + dataSource.setUrl("jdbc:sqlite:repository.sqlite"); + return dataSource; + } + + @Bean + public DataSourceInitializer dataSourceInitializer(DataSource dataSource) + throws MalformedURLException { + ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator(); + + databasePopulator.addScript(dropReopsitoryTables); + databasePopulator.addScript(dataReopsitorySchema); + databasePopulator.setIgnoreFailedDrops(true); + + DataSourceInitializer initializer = new DataSourceInitializer(); + initializer.setDataSource(dataSource); + initializer.setDatabasePopulator(databasePopulator); + + return initializer; + } + + private JobRepository getJobRepository() throws Exception { + JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean(); + factory.setDataSource(dataSource()); + factory.setTransactionManager(getTransactionManager()); + // JobRepositoryFactoryBean's methods Throws Generic Exception, + // it would have been better to have a specific one + factory.afterPropertiesSet(); + return (JobRepository) factory.getObject(); + } + + private PlatformTransactionManager getTransactionManager() { + return new ResourcelessTransactionManager(); + } + + public JobLauncher getJobLauncher() throws Exception { + SimpleJobLauncher jobLauncher = new SimpleJobLauncher(); + // SimpleJobLauncher's methods Throws Generic Exception, + // it would have been better to have a specific one + jobLauncher.setJobRepository(getJobRepository()); + jobLauncher.afterPropertiesSet(); + return jobLauncher; + } + +} \ No newline at end of file diff --git a/spring-batch-intro/src/main/java/org/baeldung/spring_batch_intro/model/Transaction.java b/spring-batch-intro/src/main/java/org/baeldung/spring_batch_intro/model/Transaction.java new file mode 100644 index 0000000000..6e80298ff0 --- /dev/null +++ b/spring-batch-intro/src/main/java/org/baeldung/spring_batch_intro/model/Transaction.java @@ -0,0 +1,56 @@ +package org.baeldung.spring_batch_intro.model; + +import java.util.Date; + +import javax.xml.bind.annotation.XmlRootElement; + +@SuppressWarnings("restriction") +@XmlRootElement(name = "transactionRecord") +public class Transaction { + private String username; + private int userId; + private Date transactionDate; + private double amount; + + /* getters and setters for the attributes */ + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public int getUserId() { + return userId; + } + + public void setUserId(int userId) { + this.userId = userId; + } + + public Date getTransactionDate() { + return transactionDate; + } + + public void setTransactionDate(Date transactionDate) { + this.transactionDate = transactionDate; + } + + public double getAmount() { + return amount; + } + + public void setAmount(double amount) { + this.amount = amount; + } + + @Override + public String toString() { + return "Transaction [username=" + username + ", userId=" + userId + + ", transactionDate=" + transactionDate + ", amount=" + amount + + "]"; + } + +} diff --git a/spring-batch-intro/src/main/java/org/baeldung/spring_batch_intro/service/CustomItemProcessor.java b/spring-batch-intro/src/main/java/org/baeldung/spring_batch_intro/service/CustomItemProcessor.java new file mode 100644 index 0000000000..baabe79409 --- /dev/null +++ b/spring-batch-intro/src/main/java/org/baeldung/spring_batch_intro/service/CustomItemProcessor.java @@ -0,0 +1,13 @@ +package org.baeldung.spring_batch_intro.service; + +import org.baeldung.spring_batch_intro.model.Transaction; +import org.springframework.batch.item.ItemProcessor; + +public class CustomItemProcessor implements + ItemProcessor { + + public Transaction process(Transaction item) { + System.out.println("Processing..." + item); + return item; + } +} \ No newline at end of file diff --git a/spring-batch-intro/src/main/java/org/baeldung/spring_batch_intro/service/RecordFieldSetMapper.java b/spring-batch-intro/src/main/java/org/baeldung/spring_batch_intro/service/RecordFieldSetMapper.java new file mode 100644 index 0000000000..94f9e7d94e --- /dev/null +++ b/spring-batch-intro/src/main/java/org/baeldung/spring_batch_intro/service/RecordFieldSetMapper.java @@ -0,0 +1,35 @@ +package org.baeldung.spring_batch_intro.service; + +import java.text.ParseException; +import java.text.SimpleDateFormat; + +import org.baeldung.spring_batch_intro.model.Transaction; +import org.springframework.batch.item.file.mapping.FieldSetMapper; +import org.springframework.batch.item.file.transform.FieldSet; +import org.springframework.validation.BindException; + +public class RecordFieldSetMapper implements FieldSetMapper { + + public Transaction mapFieldSet(FieldSet fieldSet) throws BindException { + + SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy"); + Transaction transaction = new Transaction(); + // you can either use the indices or custom names + // I personally prefer the custom names easy for debugging and + // validating the pipelines + transaction.setUsername(fieldSet.readString("username")); + transaction.setUserId(fieldSet.readInt("userid")); + transaction.setAmount(fieldSet.readDouble(3)); + // Converting the date + String dateString = fieldSet.readString(2); + try { + transaction.setTransactionDate(dateFormat.parse(dateString)); + } catch (ParseException e) { + e.printStackTrace(); + } + + return transaction; + + } + +} diff --git a/spring-batch-intro/src/main/resources/input/record.csv b/spring-batch-intro/src/main/resources/input/record.csv new file mode 100644 index 0000000000..e554becb2a --- /dev/null +++ b/spring-batch-intro/src/main/resources/input/record.csv @@ -0,0 +1,4 @@ +username, user_id, transaction_date, transaction_amount +devendra, 1234, 31/10/2015, 10000 +john, 2134, 3/12/2015, 12321 +robin, 2134, 2/02/2015, 23411 \ No newline at end of file diff --git a/spring-batch-intro/src/main/resources/spring-batch-intro.xml b/spring-batch-intro/src/main/resources/spring-batch-intro.xml new file mode 100644 index 0000000000..93606d232f --- /dev/null +++ b/spring-batch-intro/src/main/resources/spring-batch-intro.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.baeldung.spring_batch_intro.model.Transaction + + + + + + + + + + + + + diff --git a/spring-batch-intro/src/main/resources/spring.xml b/spring-batch-intro/src/main/resources/spring.xml new file mode 100644 index 0000000000..dea261c5e6 --- /dev/null +++ b/spring-batch-intro/src/main/resources/spring.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-batch-intro/xml/output.xml b/spring-batch-intro/xml/output.xml new file mode 100644 index 0000000000..acf4969341 --- /dev/null +++ b/spring-batch-intro/xml/output.xml @@ -0,0 +1 @@ +10000.02015-10-31T00:00:00+05:301234devendra12321.02015-12-03T00:00:00+05:302134john23411.02015-02-02T00:00:00+05:302134robin \ No newline at end of file