[SpringBatch] SpringBatch로 csv 파일 읽는 방법

한지연·2023년 9월 18일
0

자바17, 스프링부트3.1.3, 스프링배치5.0.3을 이용하여 코드를 작성하였습니다.
스프링 배치로 csv 파일을 읽은 후 필드를 추가하여 새로운 csv 파일을 생성하는 배치 코드입니다

1. 의존성 추가

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-batch'
	testImplementation 'org.springframework.batch:spring-batch-test'
}

2. 객체 생성, csv 파일 준비

@Data
public class Player implements Serializable {

    private String ID;
    private String lastName;
    private String firstName;
    private String position;
    private int birthYear;
    private int debutYear;

    public String toString() {
        return "PLAYER:ID=" + ID + ",Last Name=" + lastName +
                ",First Name=" + firstName + ",Position=" + position +
                ",Birth Year=" + birthYear + ",DebutYear=" +
                debutYear;
    }
@Data // 데이터 가공 후 새로운 csv 파일에 담을 객체
public class PlayerYears implements Serializable {

    private String ID;
    private String lastName;
    private String firstName;
    private String position;
    private int birthYear;
    private int debutYear;

    private int yearsExperience;

    public PlayerYears(Player item) {
        this.ID = item.getID();
        this.lastName = item.getLastName();
        this.firstName = item.getFirstName();
        this.position = item.getPosition();
        this.birthYear = item.getBirthYear();
        this.debutYear = item.getDebutYear();
        this.yearsExperience = LocalDateTime.now().getYear() - item.getDebutYear();
    }


    public String toString() {
        return "PLAYER:ID=" + ID + ",Last Name=" + lastName +
                ",First Name=" + firstName + ",Position=" + position +
                ",Birth Year=" + birthYear + ",DebutYear=" +
                debutYear;
    }



  • resources 아래 csv 파일을 생성하여 해당 데이터를 붙여넣기
ID,lastName,firstName,position,birthYear,debutYear
"AbduKa00,Abdul-Jabbar,Karim,rb,1974,1996",
"AbduRa00,Abdullah,Rabih,rb,1975,1999",
"AberWa00,Abercrombie,Walter,rb,1959,1982",
"AbraDa00,Abramowicz,Danny,wr,1945,1967",
"AdamBo00,Adams,Bob,te,1946,1969",
"AdamCh00,Adams,Charlie,wr,1979,2003"

3. FieldSetMapper 만들기

public class PlayerFieldSetMapper implements FieldSetMapper<Player> {
    public Player mapFieldSet(FieldSet fieldSet) {
        Player player = new Player();

        player.setID(fieldSet.readString(0));
        player.setLastName(fieldSet.readString(1));
        player.setFirstName(fieldSet.readString(2));
        player.setPosition(fieldSet.readString(3));
        player.setBirthYear(fieldSet.readInt(4));
        player.setDebutYear(fieldSet.readInt(5));

        return player;
    }
}

FieldSetMapper: 특수한 타입의 필드를 변환할 때 사용

4. Job, Step 생성

@Configuration
@RequiredArgsConstructor
public class FileDataReadWriteConfig {

	//Job
    @Bean
    public Job fileReadWriteJob(JobRepository jobRepository, PlatformTransactionManager transactionManager) throws Exception {

        return new JobBuilder("fileReadWriteJob", jobRepository)
                .incrementer(new RunIdIncrementer())
                .start(fileReadStep(jobRepository, transactionManager
                ))
                .build();
    }


	// Step
    @JobScope
    @Bean
    public Step fileReadStep(
            JobRepository jobRepository,
            PlatformTransactionManager transactionManager

    ) throws Exception {
        return new StepBuilder("fileReadWriteStep", jobRepository)
                .<Player, PlayerYears>chunk(5, transactionManager)
                .reader(playerFlatFileItemReader())
                .processor(playerYearsItemProcessor())
                .writer(playerFlatFileItemWriter())
                .build();
    }


	// ItemWriter
    @StepScope
    @Bean
    public FlatFileItemWriter<PlayerYears> playerFlatFileItemWriter() {
    	// 데이터의 첫줄 필드명을 만들어 줌
        BeanWrapperFieldExtractor<PlayerYears> fieldExtractor = new BeanWrapperFieldExtractor<>();
        fieldExtractor.setNames(new String[]{"ID", "lastName", "position", "yearsExperience"});
        fieldExtractor.afterPropertiesSet();

		// 구분자로 ','를 사용
        DelimitedLineAggregator<PlayerYears> lineAggregator = new DelimitedLineAggregator<>();
        lineAggregator.setDelimiter(",");
        lineAggregator.setFieldExtractor(fieldExtractor);

		// 저장할 파일의 위치와 이름을 지정
        FileSystemResource outputResource = new FileSystemResource("src/main/resources/players_output.txt");

        return new FlatFileItemWriterBuilder<PlayerYears>()
                .name("playerItemWriter")
                .resource(outputResource)
                .lineAggregator(lineAggregator)
                .build();
    }

	// ItemProcessor
    @StepScope
    @Bean
    public ItemProcessor<Player, PlayerYears> playerYearsItemProcessor() {
        return new ItemProcessor<Player, PlayerYears>() {
            @Override
            public PlayerYears process(Player item) throws Exception {
            	// 데이터를 Player -> PlayerYears로 가공하여 반환
                return new PlayerYears(item);
            }
        };
    }

	//ItemReader
    @StepScope
    @Bean
    public FlatFileItemReader<Player> playerFlatFileItemReader() throws Exception {

        return new FlatFileItemReaderBuilder<Player>()
                .name("playerItemReader")
                // 해당 위치에 있는 파일을 읽음
                .resource(new FileSystemResource("src/main/resources/players.csv"))
                // ','을 기준으로 데이터를 자름
                .lineTokenizer(new DelimitedLineTokenizer())
                // 파일의 첫 번째 줄은 스킵
                .linesToSkip(1)
                .fieldSetMapper(new PlayerFieldSetMapper())
                .build();
    }

}


JobRepository: 메타데이터 관리, 배치 수행 관련 데이터 관리
JobLauncher: Job을 실행 시켜줌
Job: 하나의 배치 작업, flow라고도 함, Step을 가져야 함
Step: Job의 세부적인 내용, 읽기- 가공 - 쓰기의 묶음으로 되어 있음



🔗 출처 - 스프링배치 공식 문서
🔗 출처 - 스프링배치 인프런 강의

profile
배우고 활용하는 것을 즐기는 개발자, 한지연입니다!

0개의 댓글