From 833bdab7849b94749644f3694ff60c0477380548 Mon Sep 17 00:00:00 2001 From: BangKiHyun Date: Sat, 4 Dec 2021 16:50:35 +0900 Subject: [PATCH 1/3] Fix: chapter7 test code - add CustomerReaderJobConfiguration.java - add MapperJobConfiguration.java - add MultiFileReaderJobConfiguration.java - QuoteJobConfiguration.java --- .../job/CustomReaderJobConfiguration.java | 115 ++++++++++++++++ .../chap7/job/DelimitedJobConfiguration.java | 55 +------- .../chap7/job/MapperJobConfiguration.java | 64 +++++++++ .../job/MultiFileReaderJobConfiguration.java | 125 ++++++++++++++++++ .../job/PatternMatchingJobConfiguration.java | 80 +---------- .../chap7/job/QuoteJobConfiguration.java | 63 +++++++++ .../job/DelimitedJobConfigurationTests.java | 5 +- .../PatternMatchingJobConfigurationTests.java | 6 +- 8 files changed, 373 insertions(+), 140 deletions(-) create mode 100644 definitive-guide/src/main/java/com/javabom/definitiveguide/chap7/job/CustomReaderJobConfiguration.java create mode 100644 definitive-guide/src/main/java/com/javabom/definitiveguide/chap7/job/MapperJobConfiguration.java create mode 100644 definitive-guide/src/main/java/com/javabom/definitiveguide/chap7/job/MultiFileReaderJobConfiguration.java create mode 100644 definitive-guide/src/main/java/com/javabom/definitiveguide/chap7/job/QuoteJobConfiguration.java diff --git a/definitive-guide/src/main/java/com/javabom/definitiveguide/chap7/job/CustomReaderJobConfiguration.java b/definitive-guide/src/main/java/com/javabom/definitiveguide/chap7/job/CustomReaderJobConfiguration.java new file mode 100644 index 0000000..34baa30 --- /dev/null +++ b/definitive-guide/src/main/java/com/javabom/definitiveguide/chap7/job/CustomReaderJobConfiguration.java @@ -0,0 +1,115 @@ +package com.javabom.definitiveguide.chap7.job; + +import com.javabom.definitiveguide.chap7.job.reader.CustomerFileReader; +import com.javabom.definitiveguide.chap7.mapper.TransactionFieldSetMapper; +import com.javabom.definitiveguide.chap7.model.CustomerTransactions; +import lombok.RequiredArgsConstructor; +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.configuration.annotation.StepScope; +import org.springframework.batch.item.ItemWriter; +import org.springframework.batch.item.file.FlatFileItemReader; +import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder; +import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper; +import org.springframework.batch.item.file.mapping.FieldSetMapper; +import org.springframework.batch.item.file.mapping.PatternMatchingCompositeLineMapper; +import org.springframework.batch.item.file.transform.DelimitedLineTokenizer; +import org.springframework.batch.item.file.transform.LineTokenizer; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; + +import java.util.HashMap; +import java.util.Map; + +@Configuration +@RequiredArgsConstructor +public class CustomReaderJobConfiguration { + + public static final String JOB_NAME = "chap7_custom_reader_job"; + public static final String STEP_NAME = "chap7_custom_reader_step"; + + private final JobBuilderFactory jobBuilderFactory; + private final StepBuilderFactory stepBuilderFactory; + + @Bean(name = JOB_NAME) + public Job job() { + return this.jobBuilderFactory.get(JOB_NAME) + .start(customTransactionLineTokenizerFileStep()) + .build(); + } + + @Bean(name = STEP_NAME) + public Step customTransactionLineTokenizerFileStep() { + return this.stepBuilderFactory.get(STEP_NAME) + .chunk(10) + .reader(customerFileReader()) + .writer(patternMatchingItemWriter()) + .build(); + } + + @Bean + public CustomerFileReader customerFileReader() { + return new CustomerFileReader(customerTransactionsItemReader(null)); + } + + @Bean + @StepScope + public FlatFileItemReader customerTransactionsItemReader( + @Value("#{jobParameters['customerFile']}") ClassPathResource inputFile) { + return new FlatFileItemReaderBuilder() + .name("customerTransactionsItemReader") + .lineMapper(customerTransactionsLineMapper()) + .resource(inputFile) + .build(); + } + + @Bean + public PatternMatchingCompositeLineMapper customerTransactionsLineMapper() { + Map lineTokenizerMap = new HashMap<>(2); + lineTokenizerMap.put("CUST*", customerLineTokenizer()); //고객 정보 포맷 + lineTokenizerMap.put("TRANS*", transactionLineTokenizer()); //거래 레코드 포맷 + + Map fieldSetMapperMap = new HashMap<>(2); + BeanWrapperFieldSetMapper customerFieldSetMapper = new BeanWrapperFieldSetMapper<>(); + customerFieldSetMapper.setTargetType(CustomerTransactions.class); + + fieldSetMapperMap.put("CUST*", customerFieldSetMapper); + fieldSetMapperMap.put("TRANS*", new TransactionFieldSetMapper()); + + PatternMatchingCompositeLineMapper lineMapper = new PatternMatchingCompositeLineMapper(); + lineMapper.setTokenizers(lineTokenizerMap); + lineMapper.setFieldSetMappers(fieldSetMapperMap); + + return lineMapper; + } + + @Bean(name = "custom_reader_line_tokenizer") + public DelimitedLineTokenizer customerLineTokenizer() { + DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer(); + lineTokenizer.setNames("firstName", + "middleInitial", + "lastName", + "address", + "city", + "state", + "zipCode"); + lineTokenizer.setIncludedFields(1, 2, 3, 4, 5, 6, 7); //prefix 무시 (인덱스 시작: 0) + return lineTokenizer; + } + + @Bean(name = "custom_reader_transaction_line_tokenizer") + public DelimitedLineTokenizer transactionLineTokenizer() { + DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer(); + lineTokenizer.setNames("prefix", "accountNumber", "transactionDate", "amount"); + return lineTokenizer; + } + + @Bean(name = "custom_item_writer") + public ItemWriter patternMatchingItemWriter() { + return (items -> items.forEach(System.out::println)); + } +} diff --git a/definitive-guide/src/main/java/com/javabom/definitiveguide/chap7/job/DelimitedJobConfiguration.java b/definitive-guide/src/main/java/com/javabom/definitiveguide/chap7/job/DelimitedJobConfiguration.java index a9bc9cd..366a08a 100644 --- a/definitive-guide/src/main/java/com/javabom/definitiveguide/chap7/job/DelimitedJobConfiguration.java +++ b/definitive-guide/src/main/java/com/javabom/definitiveguide/chap7/job/DelimitedJobConfiguration.java @@ -30,7 +30,7 @@ public class DelimitedJobConfiguration { @Bean(name = JOB_NAME) public Job job() { return this.jobBuilderFactory.get(JOB_NAME) - .start(quoteFileStep()) + .start(delimitedFileStep()) .build(); } @@ -43,24 +43,6 @@ public Step delimitedFileStep() { .build(); } - @Bean(name = "quote" + STEP_NAME) - public Step quoteFileStep() { - return this.stepBuilderFactory.get("quote" + STEP_NAME) - .chunk(10) - .reader(quoteDelimitedCustomerItemReader(null)) - .writer(itemWriter()) - .build(); - } - - @Bean(name = "mapper" + STEP_NAME) - public Step mapperFileStep() { - return this.stepBuilderFactory.get("mapper" + STEP_NAME) - .chunk(10) - .reader(mapperCustomerItemReader(null)) - .writer(mapperItemWriter()) - .build(); - } - @Bean @StepScope public FlatFileItemReader delimitedCustomerItemReader( @@ -76,43 +58,8 @@ public FlatFileItemReader delimitedCustomerItemReader( .build(); } - @Bean - @StepScope - public FlatFileItemReader quoteDelimitedCustomerItemReader( - @Value("#{jobParameters['customerFile']}") ClassPathResource inputFile) { - return new FlatFileItemReaderBuilder() - .name("quoteDelimitedCustomerItemReader") - .resource(inputFile) - .delimited() - .quoteCharacter('#') - .names("firstName", "middleInitial", "lastName", - "addressNumber", "street", "city", "state") - .targetType(Customer.class) - .build(); - } - - @Bean - @StepScope - public FlatFileItemReader mapperCustomerItemReader( - @Value("#{jobParameters['customerFile']}") ClassPathResource inputFile) { - return new FlatFileItemReaderBuilder() - .name("mapperCustomerItemReader") - .resource(inputFile) - .delimited() - .names("firstName", "middleInitial", "lastName", - "addressNumber", "street", "city", "state", - "zipCode") - .fieldSetMapper(new CustomerFieldSetMapper()) - .build(); - } - @Bean(name = "delimited_item_writer") public ItemWriter itemWriter() { return (items -> items.forEach(System.out::println)); } - - @Bean(name = "mapper_item_writer") - public ItemWriter mapperItemWriter() { - return (items -> items.forEach(System.out::println)); - } } diff --git a/definitive-guide/src/main/java/com/javabom/definitiveguide/chap7/job/MapperJobConfiguration.java b/definitive-guide/src/main/java/com/javabom/definitiveguide/chap7/job/MapperJobConfiguration.java new file mode 100644 index 0000000..ac42a88 --- /dev/null +++ b/definitive-guide/src/main/java/com/javabom/definitiveguide/chap7/job/MapperJobConfiguration.java @@ -0,0 +1,64 @@ +package com.javabom.definitiveguide.chap7.job; + +import com.javabom.definitiveguide.chap7.mapper.CustomerFieldSetMapper; +import com.javabom.definitiveguide.chap7.model.CustomerAddress; +import lombok.RequiredArgsConstructor; +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.configuration.annotation.StepScope; +import org.springframework.batch.item.ItemWriter; +import org.springframework.batch.item.file.FlatFileItemReader; +import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; + +@Configuration +@RequiredArgsConstructor +public class MapperJobConfiguration { + + public static final String JOB_NAME = "chap7_mapper_job"; + public static final String STEP_NAME = "chap7_mapper_step"; + + private final JobBuilderFactory jobBuilderFactory; + private final StepBuilderFactory stepBuilderFactory; + + @Bean(name = JOB_NAME) + public Job job() { + return this.jobBuilderFactory.get(JOB_NAME) + .start(mapperFileStep()) + .build(); + } + + @Bean(name = STEP_NAME) + public Step mapperFileStep() { + return this.stepBuilderFactory.get(STEP_NAME) + .chunk(10) + .reader(mapperCustomerItemReader(null)) + .writer(mapperItemWriter()) + .build(); + } + + @Bean + @StepScope + public FlatFileItemReader mapperCustomerItemReader( + @Value("#{jobParameters['customerFile']}") ClassPathResource inputFile) { + return new FlatFileItemReaderBuilder() + .name("mapperCustomerItemReader") + .resource(inputFile) + .delimited() + .names("firstName", "middleInitial", "lastName", + "addressNumber", "street", "city", "state", + "zipCode") + .fieldSetMapper(new CustomerFieldSetMapper()) + .build(); + } + + @Bean(name = "mapper_item_writer") + public ItemWriter mapperItemWriter() { + return (items -> items.forEach(System.out::println)); + } +} diff --git a/definitive-guide/src/main/java/com/javabom/definitiveguide/chap7/job/MultiFileReaderJobConfiguration.java b/definitive-guide/src/main/java/com/javabom/definitiveguide/chap7/job/MultiFileReaderJobConfiguration.java new file mode 100644 index 0000000..54a26b3 --- /dev/null +++ b/definitive-guide/src/main/java/com/javabom/definitiveguide/chap7/job/MultiFileReaderJobConfiguration.java @@ -0,0 +1,125 @@ +package com.javabom.definitiveguide.chap7.job; + +import com.javabom.definitiveguide.chap7.job.reader.CustomerFileReader; +import com.javabom.definitiveguide.chap7.mapper.TransactionFieldSetMapper; +import com.javabom.definitiveguide.chap7.model.CustomerTransactions; +import lombok.RequiredArgsConstructor; +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.configuration.annotation.StepScope; +import org.springframework.batch.item.ItemWriter; +import org.springframework.batch.item.file.FlatFileItemReader; +import org.springframework.batch.item.file.MultiResourceItemReader; +import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder; +import org.springframework.batch.item.file.builder.MultiResourceItemReaderBuilder; +import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper; +import org.springframework.batch.item.file.mapping.FieldSetMapper; +import org.springframework.batch.item.file.mapping.PatternMatchingCompositeLineMapper; +import org.springframework.batch.item.file.transform.DelimitedLineTokenizer; +import org.springframework.batch.item.file.transform.LineTokenizer; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; + +import java.util.HashMap; +import java.util.Map; + +@Configuration +@RequiredArgsConstructor +public class MultiFileReaderJobConfiguration { + + public static final String JOB_NAME = "chap7_multi_reader_job"; + public static final String STEP_NAME = "chap7_multi_reader_step"; + + private final JobBuilderFactory jobBuilderFactory; + private final StepBuilderFactory stepBuilderFactory; + + @Bean(name = JOB_NAME) + public Job job() { + return this.jobBuilderFactory.get(JOB_NAME) + .start(customMultiFileStep()) + .build(); + } + + @Bean(name = STEP_NAME) + public Step customMultiFileStep() { + return this.stepBuilderFactory.get(STEP_NAME) + .chunk(10) + .reader(customerMultiFileItemReader(null)) + .writer(patternMatchingItemWriter()) + .build(); + } + + @Bean + @StepScope + public MultiResourceItemReader customerMultiFileItemReader( + @Value("#{jobParameters['customerFile']}") ClassPathResource[] inputFiles) { + return new MultiResourceItemReaderBuilder<>() + .name("customerMultiFileItemReader") + .resources(inputFiles) + .delegate(customerMultiFileReader()) + .build(); + } + + @Bean + public CustomerFileReader customerMultiFileReader() { + return new CustomerFileReader(customerMultiItemReader()); + } + + @Bean + public FlatFileItemReader customerMultiItemReader() { + return new FlatFileItemReaderBuilder() + .name("customerMultiItemReader") + .lineMapper(customerTransactionsLineMapper()) + .build(); + } + + @Bean(name = "multiFile_custom_transaction_line_mapper") + public PatternMatchingCompositeLineMapper customerTransactionsLineMapper() { + Map lineTokenizerMap = new HashMap<>(2); + lineTokenizerMap.put("CUST*", customerLineTokenizer()); //고객 정보 포맷 + lineTokenizerMap.put("TRANS*", transactionLineTokenizer()); //거래 레코드 포맷 + + Map fieldSetMapperMap = new HashMap<>(2); + BeanWrapperFieldSetMapper customerFieldSetMapper = new BeanWrapperFieldSetMapper<>(); + customerFieldSetMapper.setTargetType(CustomerTransactions.class); + + fieldSetMapperMap.put("CUST*", customerFieldSetMapper); + fieldSetMapperMap.put("TRANS*", new TransactionFieldSetMapper()); + + PatternMatchingCompositeLineMapper lineMapper = new PatternMatchingCompositeLineMapper(); + lineMapper.setTokenizers(lineTokenizerMap); + lineMapper.setFieldSetMappers(fieldSetMapperMap); + + return lineMapper; + } + + @Bean(name = "multiFile_custom_line_tokenizer") + public DelimitedLineTokenizer customerLineTokenizer() { + DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer(); + lineTokenizer.setNames("firstName", + "middleInitial", + "lastName", + "address", + "city", + "state", + "zipCode"); + lineTokenizer.setIncludedFields(1, 2, 3, 4, 5, 6, 7); //prefix 무시 (인덱스 시작: 0) + return lineTokenizer; + } + + @Bean(name = "multiFile_transaction_line_tokenizer") + public DelimitedLineTokenizer transactionLineTokenizer() { + DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer(); + lineTokenizer.setNames("prefix", "accountNumber", "transactionDate", "amount"); + return lineTokenizer; + } + + @Bean(name = "multi_item_writer") + public ItemWriter patternMatchingItemWriter() { + return (items -> items.forEach(System.out::println)); + } +} diff --git a/definitive-guide/src/main/java/com/javabom/definitiveguide/chap7/job/PatternMatchingJobConfiguration.java b/definitive-guide/src/main/java/com/javabom/definitiveguide/chap7/job/PatternMatchingJobConfiguration.java index 9171361..ee3ebf0 100644 --- a/definitive-guide/src/main/java/com/javabom/definitiveguide/chap7/job/PatternMatchingJobConfiguration.java +++ b/definitive-guide/src/main/java/com/javabom/definitiveguide/chap7/job/PatternMatchingJobConfiguration.java @@ -41,7 +41,7 @@ public class PatternMatchingJobConfiguration { @Bean(name = JOB_NAME) public Job job() { return this.jobBuilderFactory.get(JOB_NAME) - .start(customMultiFileStep()) + .start(customAddressLineTokenizerFileStep()) .build(); } @@ -85,84 +85,6 @@ public PatternMatchingCompositeLineMapper patternMatchingLineMapper() { return lineMapper; } - @Bean(name = "second" + STEP_NAME) - public Step customTransactionLineTokenizerFileStep() { - return this.stepBuilderFactory.get("second" + STEP_NAME) - .chunk(10) - .reader(customerFileReader()) - .writer(patternMatchingItemWriter()) - .build(); - } - - @Bean - public CustomerFileReader customerFileReader() { - return new CustomerFileReader(customerTransactionsItemReader(null)); - } - - @Bean - @StepScope - public FlatFileItemReader customerTransactionsItemReader( - @Value("#{jobParameters['customerFile']}") ClassPathResource inputFile) { - return new FlatFileItemReaderBuilder() - .name("customerTransactionsItemReader") - .lineMapper(customerTransactionsLineMapper()) - .resource(inputFile) - .build(); - } - - @Bean - public PatternMatchingCompositeLineMapper customerTransactionsLineMapper() { - Map lineTokenizerMap = new HashMap<>(2); - lineTokenizerMap.put("CUST*", customerLineTokenizer()); //고객 정보 포맷 - lineTokenizerMap.put("TRANS*", transactionLineTokenizer()); //거래 레코드 포맷 - - Map fieldSetMapperMap = new HashMap<>(2); - BeanWrapperFieldSetMapper customerFieldSetMapper = new BeanWrapperFieldSetMapper<>(); - customerFieldSetMapper.setTargetType(CustomerTransactions.class); - - fieldSetMapperMap.put("CUST*", customerFieldSetMapper); - fieldSetMapperMap.put("TRANS*", new TransactionFieldSetMapper()); - - PatternMatchingCompositeLineMapper lineMapper = new PatternMatchingCompositeLineMapper(); - lineMapper.setTokenizers(lineTokenizerMap); - lineMapper.setFieldSetMappers(fieldSetMapperMap); - - return lineMapper; - } - - @Bean(name = "third" + STEP_NAME) - public Step customMultiFileStep() { - return this.stepBuilderFactory.get("third" + STEP_NAME) - .chunk(10) - .reader(customerMultiFileItemReader(null)) - .writer(patternMatchingItemWriter()) - .build(); - } - - @Bean - @StepScope - public MultiResourceItemReader customerMultiFileItemReader( - @Value("#{jobParameters['customerFile']}") ClassPathResource[] inputFiles) { - return new MultiResourceItemReaderBuilder<>() - .name("customerMultiFileItemReader") - .resources(inputFiles) - .delegate(customerMultiFileReader()) - .build(); - } - - @Bean - public CustomerFileReader customerMultiFileReader() { - return new CustomerFileReader(customerMultiItemReader()); - } - - @Bean - public FlatFileItemReader customerMultiItemReader() { - return new FlatFileItemReaderBuilder() - .name("customerMultiItemReader") - .lineMapper(customerTransactionsLineMapper()) - .build(); - } - @Bean public DelimitedLineTokenizer customerLineTokenizer() { DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer(); diff --git a/definitive-guide/src/main/java/com/javabom/definitiveguide/chap7/job/QuoteJobConfiguration.java b/definitive-guide/src/main/java/com/javabom/definitiveguide/chap7/job/QuoteJobConfiguration.java new file mode 100644 index 0000000..3467b18 --- /dev/null +++ b/definitive-guide/src/main/java/com/javabom/definitiveguide/chap7/job/QuoteJobConfiguration.java @@ -0,0 +1,63 @@ +package com.javabom.definitiveguide.chap7.job; + +import com.javabom.definitiveguide.chap7.model.Customer; +import lombok.RequiredArgsConstructor; +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.configuration.annotation.StepScope; +import org.springframework.batch.item.ItemWriter; +import org.springframework.batch.item.file.FlatFileItemReader; +import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; + +@Configuration +@RequiredArgsConstructor +public class QuoteJobConfiguration { + + public static final String JOB_NAME = "chap7_quote_job"; + public static final String STEP_NAME = "chap7_quote_step"; + + private final JobBuilderFactory jobBuilderFactory; + private final StepBuilderFactory stepBuilderFactory; + + @Bean(name = JOB_NAME) + public Job job() { + return this.jobBuilderFactory.get(JOB_NAME) + .start(quoteFileStep()) + .build(); + } + + @Bean(name = STEP_NAME) + public Step quoteFileStep() { + return this.stepBuilderFactory.get(STEP_NAME) + .chunk(10) + .reader(quoteDelimitedCustomerItemReader(null)) + .writer(itemWriter()) + .build(); + } + + @Bean + @StepScope + public FlatFileItemReader quoteDelimitedCustomerItemReader( + @Value("#{jobParameters['customerFile']}") ClassPathResource inputFile) { + return new FlatFileItemReaderBuilder() + .name("quoteDelimitedCustomerItemReader") + .resource(inputFile) + .delimited() + .quoteCharacter('#') + .names("firstName", "middleInitial", "lastName", + "addressNumber", "street", "city", "state") + .targetType(Customer.class) + .build(); + } + + @Bean(name = "quote_item_writer") + public ItemWriter itemWriter() { + return (items -> items.forEach(System.out::println)); + } +} diff --git a/definitive-guide/src/test/java/com/javabom/definitiveguide/chap7/job/DelimitedJobConfigurationTests.java b/definitive-guide/src/test/java/com/javabom/definitiveguide/chap7/job/DelimitedJobConfigurationTests.java index a61d3b1..a4c1788 100644 --- a/definitive-guide/src/test/java/com/javabom/definitiveguide/chap7/job/DelimitedJobConfigurationTests.java +++ b/definitive-guide/src/test/java/com/javabom/definitiveguide/chap7/job/DelimitedJobConfigurationTests.java @@ -13,7 +13,6 @@ import static org.assertj.core.api.Assertions.assertThat; -@Disabled("TODO 방기현") @BatchSpringTest class DelimitedJobConfigurationTests { @@ -44,7 +43,7 @@ public void quoteDelimitedCustomerItemReaderTest() throws Exception { .toJobParameters(); //when - final JobExecution execution = jobLauncher.launchJob(DelimitedJobConfiguration.JOB_NAME, jobParameters); + final JobExecution execution = jobLauncher.launchJob(QuoteJobConfiguration.JOB_NAME, jobParameters); //then assertThat(execution.getStatus()).isEqualTo(BatchStatus.COMPLETED); @@ -59,7 +58,7 @@ public void mapperCustomerItemReaderTest() throws Exception { .toJobParameters(); //when - final JobExecution execution = jobLauncher.launchJob(DelimitedJobConfiguration.JOB_NAME, jobParameters); + final JobExecution execution = jobLauncher.launchJob(MapperJobConfiguration.JOB_NAME, jobParameters); //then assertThat(execution.getStatus()).isEqualTo(BatchStatus.COMPLETED); diff --git a/definitive-guide/src/test/java/com/javabom/definitiveguide/chap7/job/PatternMatchingJobConfigurationTests.java b/definitive-guide/src/test/java/com/javabom/definitiveguide/chap7/job/PatternMatchingJobConfigurationTests.java index f51b6b5..d95d1df 100644 --- a/definitive-guide/src/test/java/com/javabom/definitiveguide/chap7/job/PatternMatchingJobConfigurationTests.java +++ b/definitive-guide/src/test/java/com/javabom/definitiveguide/chap7/job/PatternMatchingJobConfigurationTests.java @@ -2,7 +2,6 @@ import com.javabom.definitiveguide.test.BatchSpringTest; import com.javabom.definitiveguide.test.TestJobLauncher; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.batch.core.*; @@ -13,7 +12,6 @@ import static org.assertj.core.api.Assertions.assertThat; -@Disabled("TODO 방기현") @BatchSpringTest class PatternMatchingJobConfigurationTests { @@ -47,7 +45,7 @@ public void customerFileItemReaderTest() throws Exception { .toJobParameters(); //when - final JobExecution execution = jobLauncher.launchJob(PatternMatchingJobConfiguration.JOB_NAME, jobParameters); + final JobExecution execution = jobLauncher.launchJob(CustomReaderJobConfiguration.JOB_NAME, jobParameters); final List stepExecutions = new ArrayList<>(execution.getStepExecutions()); //then @@ -68,7 +66,7 @@ public void multiFileItemReaderTest() throws Exception { .toJobParameters(); //when - final JobExecution execution = jobLauncher.launchJob(PatternMatchingJobConfiguration.JOB_NAME, jobParameters); + final JobExecution execution = jobLauncher.launchJob(MultiFileReaderJobConfiguration.JOB_NAME, jobParameters); final List stepExecutions = new ArrayList<>(execution.getStepExecutions()); //then From aea2b5f4085ba87d0d53bd28ebabec0fe68facb5 Mon Sep 17 00:00:00 2001 From: BangKiHyun Date: Sun, 5 Dec 2021 15:02:36 +0900 Subject: [PATCH 2/3] Fix: chapter6 test code --- .../chap6/TransactionReader.java | 2 +- .../chap6/domain/Transaction.java | 2 +- .../chap6/job/TransactionProcessingJob.java | 8 +++---- .../src/main/resources/Input/summaryFile.csv | 1 + .../main/resources/Input/transactionFile.csv | 2 +- .../src/main/webapp/input/summaryFile.csv | 0 .../job/TransactionProcessingJobTest.java | 23 +++++++++++++++---- 7 files changed, 26 insertions(+), 12 deletions(-) create mode 100644 definitive-guide/src/main/resources/Input/summaryFile.csv create mode 100644 definitive-guide/src/main/webapp/input/summaryFile.csv diff --git a/definitive-guide/src/main/java/com/javabom/definitiveguide/chap6/TransactionReader.java b/definitive-guide/src/main/java/com/javabom/definitiveguide/chap6/TransactionReader.java index be02bf7..d6c938e 100644 --- a/definitive-guide/src/main/java/com/javabom/definitiveguide/chap6/TransactionReader.java +++ b/definitive-guide/src/main/java/com/javabom/definitiveguide/chap6/TransactionReader.java @@ -29,7 +29,7 @@ private Transaction process(FieldSet fieldSet) { if (fieldSet != null) { if (fieldSet.getFieldCount() > 1) { result = new Transaction(); - result.setAccountNumber(fieldSet.readString(0));; + result.setAccountNumber(Integer.parseInt(fieldSet.readString(0))); result.setTimestamp(fieldSet.readDate(1, "yyyy-MM-DD HH:mm:ss")); result.setAmount(fieldSet.readDouble(2)); recordCount++; diff --git a/definitive-guide/src/main/java/com/javabom/definitiveguide/chap6/domain/Transaction.java b/definitive-guide/src/main/java/com/javabom/definitiveguide/chap6/domain/Transaction.java index b9e4ff5..b68b738 100644 --- a/definitive-guide/src/main/java/com/javabom/definitiveguide/chap6/domain/Transaction.java +++ b/definitive-guide/src/main/java/com/javabom/definitiveguide/chap6/domain/Transaction.java @@ -8,7 +8,7 @@ @Getter @Setter public class Transaction { - private String accountNumber; + private int accountNumber; private Date timestamp; private double amount; } diff --git a/definitive-guide/src/main/java/com/javabom/definitiveguide/chap6/job/TransactionProcessingJob.java b/definitive-guide/src/main/java/com/javabom/definitiveguide/chap6/job/TransactionProcessingJob.java index 82d53f9..dfacddf 100644 --- a/definitive-guide/src/main/java/com/javabom/definitiveguide/chap6/job/TransactionProcessingJob.java +++ b/definitive-guide/src/main/java/com/javabom/definitiveguide/chap6/job/TransactionProcessingJob.java @@ -103,8 +103,8 @@ public JdbcCursorItemReader accountSummaryReader(DataSource data .sql("SELECT ACCOUNT_NUMBER, CURRENT_BALANCE " + "FROM ACCOUNT_SUMMARY A " + "WHERE A.ID IN (" + - " SELECT DISTINCT T.ACCOUNT_SUMMARY_ID " + - " FROM TRANSACTION T) " + + "SELECT DISTINCT T.ACCOUNT_NUMBER " + + "FROM TRANSACTION T) " + "ORDER BY A.ACCOUNT_NUMBER") .rowMapper((resultSet, rowNumber) -> { AccountSummary accountSummary = new AccountSummary(); @@ -170,9 +170,9 @@ public JdbcBatchItemWriter transactionWriter(DataSource dataSource) return new JdbcBatchItemWriterBuilder() .itemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<>()) .sql("INSERT INTO TRANSACTION " + - "(ACCOUNT_SUMMARY_ID, TIMESTAMP, AMOUNT) " + + "(ACCOUNT_NUMBER, TIMESTAMP, AMOUNT) " + "VALUES ((SELECT ID FROM ACCOUNT_SUMMARY " + - " WHERE ACCOUNT_NUMBER = :accountNumber), " + + "WHERE ACCOUNT_NUMBER = :accountNumber), " + ":timestamp, :amount)") .dataSource(dataSource) .build(); diff --git a/definitive-guide/src/main/resources/Input/summaryFile.csv b/definitive-guide/src/main/resources/Input/summaryFile.csv new file mode 100644 index 0000000..6f03504 --- /dev/null +++ b/definitive-guide/src/main/resources/Input/summaryFile.csv @@ -0,0 +1 @@ +1,51524,-15.05 \ No newline at end of file diff --git a/definitive-guide/src/main/resources/Input/transactionFile.csv b/definitive-guide/src/main/resources/Input/transactionFile.csv index b84c677..699b0c6 100644 --- a/definitive-guide/src/main/resources/Input/transactionFile.csv +++ b/definitive-guide/src/main/resources/Input/transactionFile.csv @@ -97,4 +97,4 @@ 339599,2018-01-13 18:53:29,884.16 991085,2018-01-13 18:53:29,-15.05 297579,2018-01-13 18:53:29,-344.38 -100 \ No newline at end of file +99 \ No newline at end of file diff --git a/definitive-guide/src/main/webapp/input/summaryFile.csv b/definitive-guide/src/main/webapp/input/summaryFile.csv new file mode 100644 index 0000000..e69de29 diff --git a/definitive-guide/src/test/java/com/javabom/definitiveguide/chap5/job/TransactionProcessingJobTest.java b/definitive-guide/src/test/java/com/javabom/definitiveguide/chap5/job/TransactionProcessingJobTest.java index ff4de32..899a0ae 100644 --- a/definitive-guide/src/test/java/com/javabom/definitiveguide/chap5/job/TransactionProcessingJobTest.java +++ b/definitive-guide/src/test/java/com/javabom/definitiveguide/chap5/job/TransactionProcessingJobTest.java @@ -4,6 +4,8 @@ import com.javabom.definitiveguide.chap6.job.TransactionProcessingJob; import com.javabom.definitiveguide.test.BatchSpringTest; import com.javabom.definitiveguide.test.TestJobLauncher; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.batch.core.ExitStatus; @@ -28,26 +30,37 @@ import static org.assertj.core.api.Assertions.assertThat; -@Disabled("TODO 방기현") -@ActiveProfiles("mysql") @BatchSpringTest public class TransactionProcessingJobTest { private static final String INSERT_SQL = "INSERT INTO ACCOUNT_SUMMARY(account_number, current_balance) VALUES (?, ?)"; + @Autowired private TestJobLauncher testJobLauncher; + @Autowired private JdbcTemplate jdbcTemplate; + @BeforeEach + public void setUp() { + jdbcTemplate.execute("CREATE TABLE ACCOUNT_SUMMARY (id INT, account_number VARCHAR, current_balance VARCHAR)"); + jdbcTemplate.execute("CREATE TABLE TRANSACTION (account_number INT, timestamp TIMESTAMP, amount DOUBLE)"); + } + + @AfterEach + public void teardown() { + jdbcTemplate.execute("DROP TABLE ACCOUNT_SUMMARY"); + jdbcTemplate.execute("DROP TABLE TRANSACTION"); + } + @Test public void migrationTransactionAccountNumber() throws IOException { - Path path = Paths.get("src/main/resources/input/transactionFile.csv"); File transactionFile = new ClassPathResource("input/transactionFile.csv").getFile(); String[] transactions = new String(Files.readAllBytes(transactionFile.toPath())).split("\n"); for (String transaction : transactions) { String[] row = transaction.split(","); Transaction t = new Transaction(); - t.setAccountNumber(row[0]); + t.setAccountNumber(Integer.parseInt(row[0])); t.setAmount(0); jdbcTemplate.update(new PreparedStatementCreator() { @Override @@ -65,7 +78,7 @@ public PreparedStatement createPreparedStatement(Connection con) throws SQLExcep public void transactionTest() { JobParameters jobParameters = new JobParametersBuilder() .addString("transactionFile", "input/transactionFile.csv") - .addString("summaryFile", "file:///{summaryFilePath}") + .addString("summaryFile", "input/summaryFile.csv") .toJobParameters(); JobExecution jobExecution = testJobLauncher.launchJob(TransactionProcessingJob.JOB_NAME, jobParameters); From 55c70457d840f7ae5ad2dab4fa31a8fc6517a463 Mon Sep 17 00:00:00 2001 From: choiyusung Date: Mon, 6 Dec 2021 01:27:15 +0900 Subject: [PATCH 3/3] =?UTF-8?q?8=EC=9E=A5=20=EC=95=84=EC=9D=B4=ED=85=9C=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=EC=84=B8=EC=84=9C=20=EC=98=88=EC=A0=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- definitive-guide/build.gradle | 1 + .../test/FileReadBatchConfiguration.java | 1 + .../definitiveguide/chap8/Customer.java | 17 ++++ .../ItemProcessorAdapterConfiguration.java | 94 +++++++++++++++++++ .../chap8/UniqueLastNameValidator.java | 20 ++++ .../chap8/UpperCaseNameService.java | 16 ++++ .../chap8/ValidatingConfiguration.java | 93 ++++++++++++++++++ .../config/DefiniteBatchConfig.java | 7 +- .../FileLineMapperGenerator.java | 4 +- ...ItemProcessorAdapterConfigurationTest.java | 66 +++++++++++++ .../chap8/ValidatingConfigurationTest.java | 77 +++++++++++++++ 11 files changed, 388 insertions(+), 8 deletions(-) create mode 100644 definitive-guide/src/main/java/com/javabom/definitiveguide/chap8/Customer.java create mode 100644 definitive-guide/src/main/java/com/javabom/definitiveguide/chap8/ItemProcessorAdapterConfiguration.java create mode 100644 definitive-guide/src/main/java/com/javabom/definitiveguide/chap8/UniqueLastNameValidator.java create mode 100644 definitive-guide/src/main/java/com/javabom/definitiveguide/chap8/UpperCaseNameService.java create mode 100644 definitive-guide/src/main/java/com/javabom/definitiveguide/chap8/ValidatingConfiguration.java rename definitive-guide/src/main/java/com/javabom/definitiveguide/{chap13/test => config}/FileLineMapperGenerator.java (93%) create mode 100644 definitive-guide/src/test/java/com/javabom/definitiveguide/chap8/ItemProcessorAdapterConfigurationTest.java create mode 100644 definitive-guide/src/test/java/com/javabom/definitiveguide/chap8/ValidatingConfigurationTest.java diff --git a/definitive-guide/build.gradle b/definitive-guide/build.gradle index e5dbc97..f27eff5 100644 --- a/definitive-guide/build.gradle +++ b/definitive-guide/build.gradle @@ -5,6 +5,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-batch' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-quartz' + implementation 'org.springframework.boot:spring-boot-starter-validation' implementation group: 'javax.xml.bind', name: 'jaxb-api', version: '2.3.0' implementation group: 'org.springframework', name: 'spring-oxm', version: '3.0.0.RELEASE' diff --git a/definitive-guide/src/main/java/com/javabom/definitiveguide/chap13/test/FileReadBatchConfiguration.java b/definitive-guide/src/main/java/com/javabom/definitiveguide/chap13/test/FileReadBatchConfiguration.java index 5521a57..fc8bda1 100644 --- a/definitive-guide/src/main/java/com/javabom/definitiveguide/chap13/test/FileReadBatchConfiguration.java +++ b/definitive-guide/src/main/java/com/javabom/definitiveguide/chap13/test/FileReadBatchConfiguration.java @@ -1,5 +1,6 @@ package com.javabom.definitiveguide.chap13.test; +import com.javabom.definitiveguide.config.FileLineMapperGenerator; import lombok.RequiredArgsConstructor; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; diff --git a/definitive-guide/src/main/java/com/javabom/definitiveguide/chap8/Customer.java b/definitive-guide/src/main/java/com/javabom/definitiveguide/chap8/Customer.java new file mode 100644 index 0000000..86e5aab --- /dev/null +++ b/definitive-guide/src/main/java/com/javabom/definitiveguide/chap8/Customer.java @@ -0,0 +1,17 @@ +package com.javabom.definitiveguide.chap8; + +import lombok.Data; +import lombok.RequiredArgsConstructor; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +@Data +public class Customer { + + @Size(max = 10) + private String firstName; + + @NotBlank + private String lastName; +} diff --git a/definitive-guide/src/main/java/com/javabom/definitiveguide/chap8/ItemProcessorAdapterConfiguration.java b/definitive-guide/src/main/java/com/javabom/definitiveguide/chap8/ItemProcessorAdapterConfiguration.java new file mode 100644 index 0000000..7cfd30f --- /dev/null +++ b/definitive-guide/src/main/java/com/javabom/definitiveguide/chap8/ItemProcessorAdapterConfiguration.java @@ -0,0 +1,94 @@ +package com.javabom.definitiveguide.chap8; + +import com.javabom.definitiveguide.config.FileLineMapperGenerator; +import lombok.RequiredArgsConstructor; +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.JobScope; +import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; +import org.springframework.batch.core.configuration.annotation.StepScope; +import org.springframework.batch.item.ItemProcessor; +import org.springframework.batch.item.ItemWriter; +import org.springframework.batch.item.adapter.ItemProcessorAdapter; +import org.springframework.batch.item.file.FlatFileItemReader; +import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder; +import org.springframework.batch.item.support.CompositeItemProcessor; +import org.springframework.batch.item.validator.BeanValidatingItemProcessor; +import org.springframework.batch.item.validator.ValidatingItemProcessor; +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 java.io.File; +import java.util.Arrays; + +@RequiredArgsConstructor +@Configuration +public class ItemProcessorAdapterConfiguration { + public static final String JOB_NAME = "ItemProcessorAdapterJob"; + public static final String STEP_NAME = "ItemProcessorAdapterStep"; + + private final JobBuilderFactory jobFactory; + private final StepBuilderFactory stepFactory; + private final UpperCaseNameService upperCaseNameService; + + @Bean(name = JOB_NAME) + public Job job() { + return this.jobFactory.get(JOB_NAME) + .start(step()) + .build(); + } + + @Bean(STEP_NAME) + @JobScope + public Step step() { + return stepFactory.get(STEP_NAME) + .chunk(100) + .reader(csvReader(null)) + .processor(processor()) + .writer(writer()) + .build(); + } + + + @Bean(STEP_NAME + "Reader") + @StepScope + public FlatFileItemReader csvReader( + @Value("#{jobParameters[fileName]}") String fileName + ) { + return new FlatFileItemReaderBuilder() + .name(STEP_NAME + "Reader") + .resource(new FileSystemResource(new File(fileName))) + .lineMapper(FileLineMapperGenerator.generateLineMapper(Customer.class, ",")) + .build(); + } + + @Bean(STEP_NAME + "Processor") + @StepScope + public ItemProcessor processor() { + CompositeItemProcessor itemProcessor = new CompositeItemProcessor<>(); + itemProcessor.setDelegates( + Arrays.asList( + new ValidatingItemProcessor<>(new UniqueLastNameValidator()), + adapterProcessor() + ) + ); + return itemProcessor; + } + + public ItemProcessor adapterProcessor() { + ItemProcessorAdapter adapter = new ItemProcessorAdapter<>(); + adapter.setTargetObject(upperCaseNameService); + adapter.setTargetMethod("upperCase"); + return adapter; + } + + @Bean(STEP_NAME + "Writer") + @StepScope + public ItemWriter writer() { + return System.out::println; + } + +} diff --git a/definitive-guide/src/main/java/com/javabom/definitiveguide/chap8/UniqueLastNameValidator.java b/definitive-guide/src/main/java/com/javabom/definitiveguide/chap8/UniqueLastNameValidator.java new file mode 100644 index 0000000..87b2ab2 --- /dev/null +++ b/definitive-guide/src/main/java/com/javabom/definitiveguide/chap8/UniqueLastNameValidator.java @@ -0,0 +1,20 @@ +package com.javabom.definitiveguide.chap8; + +import org.springframework.batch.item.ItemStreamSupport; +import org.springframework.batch.item.validator.ValidationException; +import org.springframework.batch.item.validator.Validator; + +import java.util.HashSet; +import java.util.Set; + +public class UniqueLastNameValidator implements Validator { + private Set lastNames = new HashSet<>(); + + @Override + public void validate(Customer value) throws ValidationException { + if(lastNames.contains(value.getLastName())){ + throw new ValidationException("중복 발생 - " + value.getLastName()); + } + lastNames.add(value.getLastName()); + } +} diff --git a/definitive-guide/src/main/java/com/javabom/definitiveguide/chap8/UpperCaseNameService.java b/definitive-guide/src/main/java/com/javabom/definitiveguide/chap8/UpperCaseNameService.java new file mode 100644 index 0000000..55a6e6b --- /dev/null +++ b/definitive-guide/src/main/java/com/javabom/definitiveguide/chap8/UpperCaseNameService.java @@ -0,0 +1,16 @@ +package com.javabom.definitiveguide.chap8; + +import org.springframework.stereotype.Service; + +import java.util.Locale; + +@Service +public class UpperCaseNameService { + + public Customer upperCase(Customer source){ + Customer destination = new Customer(); + destination.setFirstName(source.getFirstName().toUpperCase(Locale.ROOT)); + destination.setLastName(source.getLastName().toUpperCase(Locale.ROOT)); + return destination; + } +} diff --git a/definitive-guide/src/main/java/com/javabom/definitiveguide/chap8/ValidatingConfiguration.java b/definitive-guide/src/main/java/com/javabom/definitiveguide/chap8/ValidatingConfiguration.java new file mode 100644 index 0000000..bc6c604 --- /dev/null +++ b/definitive-guide/src/main/java/com/javabom/definitiveguide/chap8/ValidatingConfiguration.java @@ -0,0 +1,93 @@ +package com.javabom.definitiveguide.chap8; + +import com.javabom.definitiveguide.config.FileLineMapperGenerator; +import lombok.RequiredArgsConstructor; +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.JobScope; +import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; +import org.springframework.batch.core.configuration.annotation.StepScope; +import org.springframework.batch.item.ItemProcessor; +import org.springframework.batch.item.ItemWriter; +import org.springframework.batch.item.file.FlatFileItemReader; +import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder; +import org.springframework.batch.item.support.ClassifierCompositeItemProcessor; +import org.springframework.batch.item.validator.BeanValidatingItemProcessor; +import org.springframework.batch.item.validator.ValidatingItemProcessor; +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 java.io.File; + +@RequiredArgsConstructor +@Configuration +public class ValidatingConfiguration { + + public static final String JOB_NAME = "ValidatingJob"; + public static final String STEP_NAME = "ValidatingStep"; + + private final JobBuilderFactory jobFactory; + private final StepBuilderFactory stepFactory; + + @Bean(name = JOB_NAME) + public Job job() { + return this.jobFactory.get(JOB_NAME) + .start(step()) + .build(); + } + + @Bean(STEP_NAME) + @JobScope + public Step step() { + return stepFactory.get(STEP_NAME) + .chunk(100) + .reader(csvReader(null)) + .processor(processor()) + .writer(writer()) + .build(); + } + + + @Bean(STEP_NAME + "Reader") + @StepScope + public FlatFileItemReader csvReader( + @Value("#{jobParameters[fileName]}") String fileName + ) { + return new FlatFileItemReaderBuilder() + .name(STEP_NAME + "Reader") + .resource(new FileSystemResource(new File(fileName))) + .lineMapper(FileLineMapperGenerator.generateLineMapper(Customer.class, ",")) + .build(); + } + + @Bean(STEP_NAME + "Processor") + @StepScope + public ItemProcessor processor() { + ItemProcessor customValidProcessor = new ValidatingItemProcessor<>(new UniqueLastNameValidator()); + ClassifierCompositeItemProcessor processor = new ClassifierCompositeItemProcessor<>(); + processor.setClassifier(item -> { + if (item.getFirstName().length() > 5) { + return beanValidProcessor(); + } else { + return customValidProcessor; + } + }); + return processor; + } + + @Bean(STEP_NAME + "BeanValidProcessor") + @StepScope + public ItemProcessor beanValidProcessor() { + return new BeanValidatingItemProcessor<>(); + } + + @Bean(STEP_NAME + "Writer") + @StepScope + public ItemWriter writer() { + return System.out::println; + } + +} diff --git a/definitive-guide/src/main/java/com/javabom/definitiveguide/config/DefiniteBatchConfig.java b/definitive-guide/src/main/java/com/javabom/definitiveguide/config/DefiniteBatchConfig.java index 3457fca..ae76338 100644 --- a/definitive-guide/src/main/java/com/javabom/definitiveguide/config/DefiniteBatchConfig.java +++ b/definitive-guide/src/main/java/com/javabom/definitiveguide/config/DefiniteBatchConfig.java @@ -9,12 +9,7 @@ @Configuration @EnableBatchProcessing // 애플리케이션 내에서 한 번만 적용됨 @ComponentScan(value = { - "com.javabom.definitiveguide.chap2", - "com.javabom.definitiveguide.chap13", - "com.javabom.definitiveguide.chap4", - "com.javabom.definitiveguide.chap5", - "com.javabom.definitiveguide.chap6", - "com.javabom.definitiveguide.chap7" + "com.javabom.definitiveguide" }) @Import(value = { BatchServiceConfig.class, diff --git a/definitive-guide/src/main/java/com/javabom/definitiveguide/chap13/test/FileLineMapperGenerator.java b/definitive-guide/src/main/java/com/javabom/definitiveguide/config/FileLineMapperGenerator.java similarity index 93% rename from definitive-guide/src/main/java/com/javabom/definitiveguide/chap13/test/FileLineMapperGenerator.java rename to definitive-guide/src/main/java/com/javabom/definitiveguide/config/FileLineMapperGenerator.java index 025f136..040fe6b 100644 --- a/definitive-guide/src/main/java/com/javabom/definitiveguide/chap13/test/FileLineMapperGenerator.java +++ b/definitive-guide/src/main/java/com/javabom/definitiveguide/config/FileLineMapperGenerator.java @@ -1,4 +1,4 @@ -package com.javabom.definitiveguide.chap13.test; +package com.javabom.definitiveguide.config; import org.springframework.batch.item.file.LineMapper; import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper; @@ -12,7 +12,7 @@ public class FileLineMapperGenerator { - static LineMapper generateLineMapper(Class value, String separator) { + public static LineMapper generateLineMapper(Class value, String separator) { DefaultLineMapper lineMapper = new DefaultLineMapper<>(); List fieldNames = extractFieldNames(value.getDeclaredFields()); diff --git a/definitive-guide/src/test/java/com/javabom/definitiveguide/chap8/ItemProcessorAdapterConfigurationTest.java b/definitive-guide/src/test/java/com/javabom/definitiveguide/chap8/ItemProcessorAdapterConfigurationTest.java new file mode 100644 index 0000000..f56576c --- /dev/null +++ b/definitive-guide/src/test/java/com/javabom/definitiveguide/chap8/ItemProcessorAdapterConfigurationTest.java @@ -0,0 +1,66 @@ +package com.javabom.definitiveguide.chap8; + +import com.javabom.definitiveguide.test.BatchSpringTest; +import com.javabom.definitiveguide.test.TestJobLauncher; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +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.beans.factory.annotation.Autowired; + +import java.io.File; +import java.io.FileWriter; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +@BatchSpringTest +class ItemProcessorAdapterConfigurationTest { + + + @Autowired + private TestJobLauncher testJobLauncher; + private final String fileName = "testFile.csv"; + private File setupFile; + + @BeforeEach + void setUp() { + this.setupFile = new File(fileName); + } + + @AfterEach + public void tearDown() { + this.setupFile.delete(); + } + + @Test + @DisplayName("service 로직을 processor로 재활용할 수 있다 - 대문자 변환") + void test(){ + //given + JobParameters jobParameters = new JobParametersBuilder() + .addString("fileName", fileName) + .toJobParameters(); + writeContents("cys,lastName"); + + //when + //then + JobExecution jobExecution = testJobLauncher.launchJob(ItemProcessorAdapterConfiguration.JOB_NAME, jobParameters); + assertThat(jobExecution.getStatus()).isEqualTo(BatchStatus.COMPLETED); + } + + + void writeContents(String... contents) { + try (FileWriter pw = new FileWriter(fileName, true)) { + for (String line : contents) { + pw.write(line); + } + pw.flush(); + } catch (Exception e) { + throw new IllegalStateException("write file"); + } + } +} \ No newline at end of file diff --git a/definitive-guide/src/test/java/com/javabom/definitiveguide/chap8/ValidatingConfigurationTest.java b/definitive-guide/src/test/java/com/javabom/definitiveguide/chap8/ValidatingConfigurationTest.java new file mode 100644 index 0000000..d925259 --- /dev/null +++ b/definitive-guide/src/test/java/com/javabom/definitiveguide/chap8/ValidatingConfigurationTest.java @@ -0,0 +1,77 @@ +package com.javabom.definitiveguide.chap8; + +import com.javabom.definitiveguide.chap8.ValidatingConfiguration; +import com.javabom.definitiveguide.test.BatchSpringTest; +import com.javabom.definitiveguide.test.TestJobLauncher; +import org.junit.jupiter.api.*; +import org.springframework.batch.core.*; +import org.springframework.beans.factory.annotation.Autowired; + +import java.io.File; +import java.io.FileWriter; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; + +@BatchSpringTest +class ValidatingConfigurationTest { + + + @Autowired + private TestJobLauncher testJobLauncher; + private final String fileName = "testFile.csv"; + private File setupFile; + + @BeforeEach + void setUp() { + this.setupFile = new File(fileName); + } + + @AfterEach + public void tearDown() { + this.setupFile.delete(); + } + + @Test + @DisplayName("bean valid 확인") + void test(){ + //given + JobParameters jobParameters = new JobParametersBuilder() + .addString("fileName", fileName) + .toJobParameters(); + writeContents("사이즈가 10을 넘는다,lastName"); + + //when + //then + JobExecution jobExecution = testJobLauncher.launchJob(ValidatingConfiguration.JOB_NAME, jobParameters); + assertThat(jobExecution.getStatus()).isEqualTo(BatchStatus.FAILED); + } + + @Test + @DisplayName("custom valid 확인") + void test2(){ + //given + JobParameters jobParameters = new JobParametersBuilder() + .addString("fileName", fileName) + .addString("processorType","UniqueLastNameValid") + .toJobParameters(); + writeContents("cys,lastName\n","cys2,lastName"); + + //when + //then + JobExecution jobExecution = testJobLauncher.launchJob(ValidatingConfiguration.JOB_NAME, jobParameters); + assertThat(jobExecution.getStatus()).isEqualTo(BatchStatus.FAILED); + } + + + void writeContents(String... contents) { + try (FileWriter pw = new FileWriter(fileName, true)) { + for (String line : contents) { + pw.write(line); + } + pw.flush(); + } catch (Exception e) { + throw new IllegalStateException("write file"); + } + } +} \ No newline at end of file