FlatFileItemReader
와는 반대로 도메인 객체를 문자열로 변환해서 쓴다.FieldExtrator
LineAggregator
T
를 Object
의 배열 형태로 반환한다.public interface FieldExtractor<T> {
Object[] extract(T item);
}
FieldExtractor
도 스프링에서 기본 구현체를 제공한다.
BeanWrapperFieldExtractor
getter
메서드로 필드값을 추출RecordFieldExtractor
T
도메인 객체를 받아 문자열로 결합하여 반환한다.public interface LineAggregator<T> {
String aggregate(T item);
}
DelimitedLineAggregator
FormatterLineAggregator
LineAggregator.aggregate()
내부에서 FieldExtractor
를 사용하는 구조기에 두 컴포넌트 모두 T
타입 객체를 파라미터로 받는다.
LineAggregator
를 구현한 ExtractorLineAggregator
추상 클래스 내부에서 FieldExtractor
를 사용한다.DelimitedLineAggregator
, FormatterLineAggregator
은 ExtractorLineAggregator
를 구현한다.FlatFileItemWriter
를 구성한다.@Bean
@StepScope
fun deathNoteWriter(
@Value("#{jobParameters['outputDir']}") outputDir: String,
): FlatFileItemWriter<DeathNote> =
FlatFileItemWriterBuilder<DeathNote>()
.name("deathNoteWriter")
.resource(FileSystemResource("$outputDir/death_notes.csv"))
.delimited()
.delimiter(",")
.sourceType(DeathNote::class.java)
.names("victimId", "victimName", "executionDate", "causeOfDeath")
.headerCallback { writer: Writer -> writer.write("처형ID,피해자명,처형일자,사인") }
.build()
name()
FlatFileItemWriter
의 고유 이름 지정resource()
WritableResource
**타입의 리소스를 지정delimited()
LineAggregator
구현체로 DelimitedLineAggregator
가 지정된다.
FlatFileItemReaderBuilder
에선delimited
를 호출하면DelimitedLineTokenizer
가 지정되는 것과 유사
delimiter()
FieldExtractor
구성 방법
FlatFileItemWriterBuilder
의 fieldExtractor()
메서드를 사용FieldExtractor
를 사용해야할 때 사용FlatFileItemWriterBuilder
의 sourceType()
메서드로 전달한 도메인 객체에 따라 자동 구성되는 방식BeanWrapperFieldExtractor
, RecordFieldExtractor
만 지원한다.sourceType
을 호출하지 않아도 자동으로 BeanWrapperFieldExtractor
가 선택된다.names()
headerCallback()
FlatFileHeaderCallback
이 전달된다.FlatFileItemWriter
초기화 시 호출되어 헤더를 작성한다.footerCallback()
도 있다.fieldExtractor
를 등록할 수 있다.
fieldExtractor
를 등록하면 sourceType
과 names
는 무시되므로 제거하면 된다.@Bean
@StepScope
fun deathNoteWriter(
@Value("#{jobParameters['outputDir']}") outputDir: String,
): FlatFileItemWriter<DeathNote> =
FlatFileItemWriterBuilder<DeathNote>()
.name("deathNoteWriter")
.resource(FileSystemResource("$outputDir/death_notes.csv"))
.delimited()
.delimiter(",")
.fieldExtractor(fieldExtractor())
.headerCallback { writer: Writer -> writer.write("처형ID,피해자명,처형일자,사인") }
.build()
fun fieldExtractor(): FieldExtractor<DeathNote> =
RecordFieldExtractor(DeathNote::class.java)
.apply { setNames("victimId", "executionDate", "causeOfDeath") }
FormatterLineAggregator
를 사용해야 한다.@Bean
@StepScope
fun deathNoteFormatterLineWriter(
@Value("#{jobParameters['outputDir']}") outputDir: String?,
): FlatFileItemWriter<DeathNote> =
FlatFileItemWriterBuilder<DeathNote>()
.name("deathNoteWriter")
.resource(FileSystemResource("$outputDir/death_note_report.txt"))
.formatted()
.format("처형 ID: %s | 처형일자: %s | 피해자: %s | 사인: %s")
.sourceType(DeathNote::class.java)
.names("victimId", "executionDate", "victimName", "causeOfDeath")
.headerCallback { writer: Writer -> writer!!.write("================= 처형 기록부 =================") }
.footerCallback { writer: Writer -> writer!!.write("================= 처형 완료 ==================") }
.build()
formatted()
LineAggregator
구현체로 FormatterLineAggregator
를 지정한다.FormatterLineAggregator
동작 방식
String customRecord = String.format("피해자: %s | 처형방식: %s", fields)
format()
String.format
에서 사용하는 것과 동일한 포맷을 사용FlatFileItemWriterBuilder
에서 다음 옵션을 통해 어떻게 파일을 처리할지 조정할 수 있다.
shouldDeleteIfExists
true
false
로 설정하면 FlatFileItemWriter
는 예외를 던진다.append
false
true
로 설정되면 shouldDeleteIfExists
가 자동으로 false
로 설정되며 기존 파일이 있어도 예외가 발생하지 않는다.shouldDeleteIfEmpty
false
true
일 때 결과 파일에 헤더와 푸터 외에 데이터가 하나도 쓰여지지 않은 경우 파일을 삭제한다.FlatFileItemWriter
는 데이터를 즉시 파일에 쓰지 않고 내부 버퍼에 일시적으로 저장해둔다.
beforeCommit()
콜백 호출 시FlatFileItemWriterBuilder
의 transactional()
메서드로 설정 가능하다.
true
FlatFileItemWriterBuilder
는 forceSync()
메서드를 제공한다.
false
true
로 설정하면 데이터를 캐시가 아닌 디스크에 즉시 동기화 시킨다.forceSync
는 이 문제를 피하기 위해 설정하는 것MultiResourceItemWriter
ItemWriter
구현체delegateItemWriter()
)@Bean
@StepScope
fun multiResourceItemWriter(
@Value("#{jobParameters['outputDir']}") outputDir: String?,
): MultiResourceItemWriter<DeathNote?> {
return MultiResourceItemWriterBuilder<DeathNote?>()
.name("multiDeathNoteWriter")
.resource(FileSystemResource("$outputDir/death_note"))
.itemCountLimitPerResource(10)
.delegate(delegateItemWriter())
.resourceSuffixCreator { index: Int -> String.format("_%03d.txt", index) }
.build()
}
name()
resource()
ItemWriter
에 전달할 리소스 지정itemCountLimitPerResource()
delegate()
ItemWriter
지정resourceSuffixCreator
_001.txt
, _002.txt