반응형
728x90
반응형
전체 코드 보기
package com.seohae.batch.batch.fileBatch1.job;
import com.seohae.batch.batch.fileBatch1.entity.Customer;
import com.seohae.batch.batch.fileBatch1.mapper.CustomerFieldSetMapper;
import com.seohae.batch.batch.fileBatch1.mapper.CustomerFileLineTokenizer;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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.core.launch.support.RunIdIncrementer;
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.transform.Range;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
/**
* --job.name=flatFileItemJob
*/
@Slf4j
@Configuration
@RequiredArgsConstructor
public class FlatFileItemJobConfig {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
/**
* Job
* @return
*/
@Bean
public Job flatFileItemJob() {
return jobBuilderFactory.get("flatFileItemJob")
.incrementer(new RunIdIncrementer())
.start(flatFileItemStep())
.build();
}
/**
* Step
* @return
*/
@Bean
public Step flatFileItemStep() {
return stepBuilderFactory.get("flatFileItemStep")
.<Customer, Customer>chunk(10)
.reader(flatFileItemReaderStep())
.writer(flatFileItemWriterStep())
.build();
}
/**
* 1) 파일을 읽어들여 Customer.class 로 변환한다.
* @return
*/
@Bean
@StepScope
public FlatFileItemReader<Customer> flatFileItemReaderStep() {
Resource inputFile = new ClassPathResource("input/customerFixedWidth.txt");
return new FlatFileItemReaderBuilder<Customer>()
.name("FlatFileItemReaderStep")
.resource(inputFile)
.fixedLength()
/* Range 객체 (파싱해야할 칼럼의 시작 위치와 종료 위치를 나타낸다) 의 배열 지정 */
.columns(new Range[]{new Range(1, 11), new Range(12, 12), new Range(13, 22),
new Range(23, 26), new Range(27, 46), new Range(47, 62),
new Range(63, 64), new Range(65, 69)})
.names("firstName", "middleInitial", "lastName",
"addressNumber", "street", "city", "state", "zipCode")
.targetType(Customer.class)
.build();
}
/**
* 2) .delimited() 추가버전 (구분값 파일 읽기)
* @return
*/
@Bean
@StepScope
public FlatFileItemReader<Customer> flatFileItemReaderStep2() {
Resource inputFile = new ClassPathResource("input/customerFixedWidth.txt");
return new FlatFileItemReaderBuilder<Customer>()
.name("FlatFileItemReaderStep")
/* 구분자로 구분되어있을 경우 사용, default: 콤마 */
.delimited()
.names("firstName", "middleInitial", "lastName",
"addressNumber", "street", "city", "state", "zipCode")
.resource(inputFile)
.targetType(Customer.class)
.build();
}
/**
* 3) CustomerFieldSetMapper.java 설정 추가버전
* @return
*/
@Bean
@StepScope
public FlatFileItemReader<Customer> flatFileItemReaderStep3() {
Resource inputFile = new ClassPathResource("input/customerFixedWidth.txt");
return new FlatFileItemReaderBuilder<Customer>()
.name("FlatFileItemReaderStep")
/* 구분자로 구분되어있을 경우 사용, default: 콤마 */
.delimited()
.names("firstName", "middleInitial", "lastName",
"addressNumber", "street", "city", "state", "zipCode")
.resource(inputFile)
.fieldSetMapper(new CustomerFieldSetMapper())
.targetType(Customer.class)
.build();
}
/**
* 4) CustomerFileLineTokenizer.java 설정 추가버전
* @return
*/
@Bean
@StepScope
public FlatFileItemReader<Customer> flatFileItemReaderStep4() {
Resource inputFile = new ClassPathResource("input/customerFixedWidth.txt");
return new FlatFileItemReaderBuilder<Customer>()
.name("FlatFileItemReaderStep")
.lineTokenizer(new CustomerFileLineTokenizer())
.resource(inputFile)
.targetType(Customer.class)
.build();
}
/**
* ItemWriter.write 메서드에 전달된 List 내 각 아이템에 대해 출력한다.
* @return
*/
@Bean
public ItemWriter<Customer> flatFileItemWriterStep() {
return (items) -> items.forEach(System.out::println);
}
}
resources/input 경로에 파일 추가
- 1) customerFixedWidth.txt
Aimee CHoover 7341Vel Avenue Mobile AL35928
Jonas UGilbert 8852In St. Saint Paul MN57321
Regan MBaxter 4851Nec Av. Gulfport MS33193
Octavius TJohnson 7418Cum Road Houston TX51507
Sydnee NRobinson 894 Ornare. Ave Olathe KS25606
Stuart KMckenzie 5529Orci Av. Nampa ID18562
Petra ZLara 8401Et St. Georgia GA70323
Cherokee TLara 8516Mauris St. Seattle WA28720
Athena YBurt 4951Mollis Rd. Newark DE41034
Kaitlin MMacias 5715Velit St. Chandler AZ86176
Leroy XCherry 7810Vulputate St. Seattle WA37703
Connor WMontoya 4122Mauris Av. College AK99743
Byron XMedina 7875At Road Rock Springs WY37733
Penelope YSandoval 2643Fringilla Av. College AK99557
Rashad VOchoa 6587Lacus Street Flint MI96640
Jordan UOneil 170 Mattis Ave Bellevue WA44941
Caesar ODaugherty 7483Libero Ave Frankfort KY56493
Wynne DRoth 8086Erat Street Owensboro KY50476
Robin IRoberson 8014Pellentesque Street Casper WY84633
Adrienne CCarpenter 8141Aliquam Avenue Tucson AZ85057
Laura AHammond 5329Accumsan Rd. Orlando FL54848
Lacey UHolman 2421Metus. Rd. Billings MT87006
Edward IMarquez 4069Ornare Ave Lansing MI66221
Veda ZMatthews 8509Donec Av. Evansville IN26057
첫번째 방법. 파일 그대로 읽어오기
- 1) Reader 메서드 추가
/**
* 1) 파일을 읽어들여 Customer.class 로 변환한다.
* @return
*/
@Bean
@StepScope
public FlatFileItemReader<Customer> flatFileItemReaderStep() {
Resource inputFile = new ClassPathResource("input/customerFixedWidth.txt");
return new FlatFileItemReaderBuilder<Customer>()
.name("FlatFileItemReaderStep")
.resource(inputFile)
.fixedLength()
/* Range 객체 (파싱해야할 칼럼의 시작 위치와 종료 위치를 나타낸다) 의 배열 지정 */
.columns(new Range[]{new Range(1, 11), new Range(12, 12), new Range(13, 22),
new Range(23, 26), new Range(27, 46), new Range(47, 62),
new Range(63, 64), new Range(65, 69)})
.names("firstName", "middleInitial", "lastName",
"addressNumber", "street", "city", "state", "zipCode")
.targetType(Customer.class)
.build();
}
두번째방법. 파일이 콤마(,)구분으로 되어있을 경우
- 1) Reader 메서드 추가
...
/**
* 2) .delimited() 추가버전 (구분값 파일 읽기)
* @return
*/
@Bean
@StepScope
public FlatFileItemReader<Customer> flatFileItemReaderStep2() {
Resource inputFile = new ClassPathResource("input/customerFixedWidth.txt");
return new FlatFileItemReaderBuilder<Customer>()
.name("FlatFileItemReaderStep")
/* 구분자로 구분되어있을 경우 사용, default: 콤마 */
.delimited()
.names("firstName", "middleInitial", "lastName",
"addressNumber", "street", "city", "state", "zipCode")
.resource(inputFile)
.targetType(Customer.class)
.build();
}
...
세번째방법. 필드 매핑을 직접 설정해주기
- 1) CustomerFieldSetMapper.java 생성
package com.seohae.batch.batch.fileBatch1.mapper;
import com.seohae.batch.batch.fileBatch1.entity.Customer;
import org.springframework.batch.item.file.mapping.FieldSetMapper;
import org.springframework.batch.item.file.transform.FieldSet;
import org.springframework.validation.BindException;
/**
* flatFileItemReaderStep3 의 .fieldSetMapper(new CustomerFieldSetMapper()) 설정 추가
*/
public class CustomerFieldSetMapper implements FieldSetMapper<Customer> {
/**
* 커스터 매퍼 생성 (Customer 타입으로)
* 원하는 값으로 필드를 매핑할 수 있다.
* fieldSet 은 타입별로도 가능
* @param fieldSet
* @return
* @throws BindException
*/
@Override
public Customer mapFieldSet(FieldSet fieldSet) throws BindException {
Customer customer = new Customer();
customer.setAddress(fieldSet.readString("addressName") + " " + fieldSet.readString("street"));
customer.setCity(fieldSet.readString("city"));
customer.setFirstName(fieldSet.readString("firstName"));
customer.setLastName(fieldSet.readString("lastName"));
customer.setMiddleInitial(fieldSet.readString("middleInitial"));
customer.setState(fieldSet.readString("state"));
customer.setZipCode(fieldSet.readString("zipCode"));
return customer;
}
}
- 2) Reader 메서드 추가
...
/**
* 3) CustomerFieldSetMapper.java 설정 추가버전
* @return
*/
@Bean
@StepScope
public FlatFileItemReader<Customer> flatFileItemReaderStep3() {
Resource inputFile = new ClassPathResource("input/customerFixedWidth.txt");
return new FlatFileItemReaderBuilder<Customer>()
.name("FlatFileItemReaderStep")
/* 구분자로 구분되어있을 경우 사용, default: 콤마 */
.delimited()
.names("firstName", "middleInitial", "lastName",
"addressNumber", "street", "city", "state", "zipCode")
.resource(inputFile)
.fieldSetMapper(new CustomerFieldSetMapper())
.targetType(Customer.class)
.build();
}
...
.delimited() 메서드가 추가되었고, defualt 는 콤마(,)이다. 파일이 , 구분으로 되어있다고 가정하자.
네번째방법. 각 레코드별로 가져와서 설정해주기
- 1) CustomerFileLineTokenizer.java 생성
package com.seohae.batch.batch.fileBatch1.mapper;
import org.springframework.batch.item.file.transform.DefaultFieldSetFactory;
import org.springframework.batch.item.file.transform.FieldSet;
import org.springframework.batch.item.file.transform.FieldSetFactory;
import org.springframework.batch.item.file.transform.LineTokenizer;
import java.util.ArrayList;
import java.util.List;
/**
* flatFileItemReaderStep4 의 .lineTokenizer(new CustomerFileLineTokenizer()) 설정 추가
*/
public class CustomerFileLineTokenizer implements LineTokenizer {
private String delimiter = ",";
private String[] names = new String[] {
"firstName", "middleInitial", "lastName",
"addressNumber", "street", "city", "state", "zipCode"};
private FieldSetFactory fieldSetFactory = new DefaultFieldSetFactory();
/**
* 각 레코드를 전달받는 메소드를 통해 구현
* @param record
* @return
*/
@Override
public FieldSet tokenize(String record) {
/* 구분자로 레코드 가져오기 */
String[] fields = record.split(delimiter);
List<String> parseFields = new ArrayList<>();
for (int i = 0; i < fields.length; i++) {
if (i == 4) {
parseFields.set(i - 1, parseFields.get(i - 1) + " " + fields[i]);
} else {
parseFields.add(fields[i]);
}
}
return fieldSetFactory.create(parseFields.toArray(new String[0]), names);
}
}
- 2) Reader 메서드 추가
...
/**
* 4) CustomerFileLineTokenizer.java 설정 추가버전
* @return
*/
@Bean
@StepScope
public FlatFileItemReader<Customer> flatFileItemReaderStep4() {
Resource inputFile = new ClassPathResource("input/customerFixedWidth.txt");
return new FlatFileItemReaderBuilder<Customer>()
.name("FlatFileItemReaderStep")
.lineTokenizer(new CustomerFileLineTokenizer())
.resource(inputFile)
.targetType(Customer.class)
.build();
}
...
ItemWriter
...
/**
* ItemWriter.write 메서드에 전달된 List 내 각 아이템에 대해 출력한다.
* @return
*/
@Bean
public ItemWriter<Customer> flatFileItemWriterStep() {
return (items) -> items.forEach(System.out::println);
}
...
교재 '스프링배치 완벽가이드' 책 예제 따라 Job 생성해보기
반응형
'Coding > Spring Batch' 카테고리의 다른 글
[스프링 배치] Spring Batch + mysql 설정하기 (0) | 2021.10.09 |
---|---|
[스프링 배치] 두가지 포맷의 파일을 각 포맷(접두어)에 따라 처리하기 (PatternMatchingCompositeLineMapper , LineTokenizer, FieldSetMapper (0) | 2021.10.08 |
[스프링 배치] 잡의 재시작 방지/재시작 횟수제한/재실행 설정 방법 (0) | 2021.09.25 |
[스프링 배치] 오류 던지기 (throw new Exception) (2) | 2021.09.25 |
[스프링 배치] 스텝 중지시키기 setTerminateOnly() (0) | 2021.09.25 |