spring batch로 csv 파일 DB에 저장하기

괭이밥·2023년 8월 16일

spring

목록 보기
9/10
post-thumbnail

📝 저장할 csv 파일

  • 학교 학사일정 파일

진행할 순서는 다음과 같다.
1. csv 파일 저장할 엔티티 & 테이블 생성
2. csv 파일 읽어 매핑할 dto 생성
3. spring batch Job, step 생성
4. csv reader, writer 로직
5. 실행


0. srping batch 설정

  1. 의존관계 추가
dependencies {
	// 추가 
	'org.springframework.boot:spring-boot-starter-batch'
}

  1. 어노테이션 추가
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableBatchProcessing ///SimpleBatchConfiguration을 스프링 빈으로 등록
public class Application {

	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}

}

1. 엔티티 & 테이블 생성

@Builder
@Getter
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@ToString
public class Schedule {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String month; //월

    private String date; //날짜

    private String event; //이벤트

}

테이블 생성


Repository 생성

package com.app.univchat.repository.school;

import com.app.univchat.domain.school.Schedule;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ScheduleRepository extends JpaRepository<Schedule, Long> {
}

2. Dto 만들기

  • csv 파일 읽을 때 매핑시켜줄 Dto
    • 이 때 csv 파일과 필드 순서가 일치해야 한다.
  • Dto를 Entity로 반환하는 toEntity() 메서드 생성
@Builder
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(value = "학시일정")
public class ScheduleDto {

    @ApiModelProperty(name = "월", example = "8월")
    private String month;

    @ApiModelProperty(name = "날짜", example = "8.1 ~ 25")
    private String date;

    @ApiModelProperty(name = "이벤트", example = "휴학원 제출기간 (성심)")
    private String event;

    /**
     * Schedule 엔티티 반환
     * @return
     */
    public Schedule toEntity(){
        return Schedule.builder()
                .month(this.month)
                .date(this.date)
                .event(this.event)
                .build();

    }
}

3. Job, Step 생성

FileReaderJobConfig 클래스 생성

@Configuration
@RequiredArgsConstructor
public class FileReaderJobConfig {

        private final JobBuilderFactory jobBuilderFactory;
        private final StepBuilderFactory stepBuilderFactory;

    private static final int chunkSize = 1000; //데이터 처리할 row size


    /**
     * 학사일정 저장 Job
     * Job은 여러 Step을 가질 수 있음
     */
    @Bean
    public Job csvScheduleJob(){
        return jobBuilderFactory.get("csvScheduleJob")
                .start(csvScheduleReaderStep())
                .build();
    }


    /**
    * csv 파일 읽고 DB에 쓰는 Step
    */
    @Bean
    public Step csvScheduleReaderStep(){
        return stepBuilderFactory.get("csvScheduleReaderStep")
        		//<reader에 넘겨줄 타입, writer에 넙겨줄 타입>
                .<ScheduleDto, ScheduleDto>chunk(chunkSize) 
//                .reader() csv 파일 읽고 넘겨줌
//                .writer() 받은 데잍터 DB에 저장
//                .allowStartIfComplete(true) 
                .build();
    }


}

4. csv 파일 읽기 & 쓰기

csv file reader

/**
 * class.csv 파일 읽기
 */
@Configuration
@RequiredArgsConstructor
public class CsvReader {

    /**
     * 학사일정 파일 읽기
     */
    @Bean
    public FlatFileItemReader<ScheduleDto> csvScheduleReader(){
        /* 파일읽기 */
        FlatFileItemReader<ScheduleDto> flatFileItemReader = new FlatFileItemReader<>();
        flatFileItemReader.setResource(new ClassPathResource("/csv/schedule.csv")); //읽을 파일 경로 지정
        flatFileItemReader.setEncoding("UTF-8"); //인토딩 설정

        /* defaultLineMapper: 읽으려는 데이터 LineMapper을 통해 Dto로 매핑 */
        DefaultLineMapper<ScheduleDto> defaultLineMapper = new DefaultLineMapper<>();

        /* delimitedLineTokenizer : csv 파일에서 구분자 지정하고 구분한 데이터 setNames를 통해 각 이름 설정 */
        DelimitedLineTokenizer delimitedLineTokenizer = new DelimitedLineTokenizer(","); //csv 파일에서 구분자
        delimitedLineTokenizer.setNames("month", "date", "event"); //행으로 읽은 데이터 매칭할 데이터 각 이름
        defaultLineMapper.setLineTokenizer(delimitedLineTokenizer); //lineTokenizer 설정

        /* beanWrapperFieldSetMapper: 매칭할 class 타입 지정 */
        BeanWrapperFieldSetMapper<ScheduleDto> beanWrapperFieldSetMapper = new BeanWrapperFieldSetMapper<>();
        beanWrapperFieldSetMapper.setTargetType(ScheduleDto.class);

        defaultLineMapper.setFieldSetMapper(beanWrapperFieldSetMapper); //fieldSetMapper 지정

        flatFileItemReader.setLineMapper(defaultLineMapper); //lineMapper 지정

        return flatFileItemReader;

    }
}

csv file writer

import org.springframework.batch.item.ItemWriter;

@Configuration
@RequiredArgsConstructor
public class CsvScheduleWriter implements ItemWriter<ScheduleDto> {

    private final ScheduleRepository scheduleRepository;

    @Override
    public void write(List<? extends ScheduleDto> items) throws Exception {
        List<Schedule> scheduleList = new ArrayList<>();

        items.forEach(getScheduleDto -> {
            Schedule schedule = getScheduleDto.toEntity();
            scheduleList.add(schedule);
        });

        scheduleRepository.saveAll(scheduleList);

    }
}

5. 3에서 작성한 Step 마무리

@Configuration
@RequiredArgsConstructor
public class FileReaderJobConfig {

        private final JobBuilderFactory jobBuilderFactory;
        private final StepBuilderFactory stepBuilderFactory;
        private final CsvReader csvReader; //추가
        private  final CsvScheduleWriter csvScheduleWriter; //추가

    @Bean
    public Step csvScheduleReaderStep(){
        return stepBuilderFactory.get("csvScheduleReaderStep")
                .<ScheduleDto, ScheduleDto>chunk(chunkSize)
                .reader(csvReader.csvScheduleReader()) //추가
                .writer(csvScheduleWriter) //추가
//                .allowStartIfComplete(true)
                .build();
    }
}

실행

쿼리문 확인

DB 확인

  • BATCH_JOB_EXECUTION: Job Instance 추가된 것 확인 (3번째)

  • BATCH_STEP_EXECUTION: Step 동작 성공적으로 끝낸 것을 볼 수 있음(COMPLETED)

  • csv 파일에 있는 데이터 DB에 무사 저장한 것 확인




만약 실행이 안 된다면?

application.yml에 다음 추가

spring:
  batch:
    jdbc:
      initialize-schema: always


Step을 다시 실행하고 싶다면?

step 설정에 다음 추가

    @Bean
    public Step csvScheduleReaderStep(){
        return stepBuilderFactory.get("csvScheduleReaderStep")
                .<ScheduleDto, ScheduleDto>chunk(chunkSize)
                .reader(csvReader.csvScheduleReader())
                .writer(csvScheduleWriter)
                .allowStartIfComplete(true) //추가
                .build();
    }
profile
개발도 하고 싶은 클라우드 엔지니어

0개의 댓글