티스토리 뷰

Spring

Spring Batch Sample Code (2)

§무명소졸§ 2022. 10. 7. 19:00

파일을 읽어서 표준 출력을 하는 Spring 배치 프로그램을 작성해보겠다. 파일 처리는 가장 흔한 배치 형태 중 하나이지 않을까 생각한다. 

준비

H2 DB (Memory -> File)

이전 글에서 우선 실행만을 테스트 하기 위해 H2 DB 를 메모리 상태로 띄운 형태를 파일 형태로 변경하겠다. 배치 프로그램은 실행 처리가 끝나면 종료되기 때문에 배치 종료후 사라지는 메모리 형태의 DB는 테스트 결과를 확인하기 어렵다. application.yml의 H2 DB 설정 내용을 아래와 같이 변경하자. 또한 최초 기동시 스프링 배치 내부 메타 테이블을 위한 설정 정보도 추가했다.

spring:
  datasource:
    url: jdbc:h2:file:~/batchdb #file db 로 변경
    username: sa
    password: password
    driverClassName: org.h2.Driver
  batch:
    jdbc:
      initialize-schema: always #meta table 초기화

Data file

테스트를 위한 어느 정도 사이즈가 있는 파일이 필요하다. 아래 링크를 통해 파일을 다운로드 받는다.

Annual enterprise survey: 2021 financial year (provisional) – CSV

해당 사이트에 직접 들어가고 싶으면 아래 링크를 클릭하면 된다.
https://www.stats.govt.nz/large-datasets/csv-files-for-download/ 

파일명이 길기 때문에 짧게 이름을 변경하고 적당한 곳에 위치 시킨다. 파일 내용은 아래와 같다.

 

CsvTestDto

배치에서 사용할 csv 파일과 맵핑할 수 있는 getter, setter 를 가지고 있는 Dto 파일을 생성하겠다.

Chunk 

지난 블로그에서 말했듯이 스프링배치는 크게 TaskletChunk 형태의 모습으로 구성할 수 있다. 상황에 따라 적절하게 사용하면 되겠지만 그래도 스프링배치의 기능의 활용성 면에서는 Chunk 모드가 높다고 생각된다. 이번에는 Chunk 스타일로 코드를 구성하겠다.

Chunk 스타일은 Reader-Processor-Writer 형태로 구성된다. 이중 Reader , Writer 는 필수이고 Processor 는 생략 가능하다.우선 여기서는 Processor는 생략하고 Reader 와 Writer 를 구현하겠다. 아래는 Chunk 형태의 Reader 와 Writer 로 변경한 Step Bean 코드이다.

 위에 보면 .<CsvTestDto, CsvTestDto>chunk(100) 코드에 chunksize 는 Reader 가 읽어서 Writer 에게 전달하는 사이즈이고 해당 단위로 트랜잭션이 이루어진다.

FlatFileItemReader

스프링 배치에서는 이미 유용하게 사용할 수있는 ItemReader 구현체를 제공하고 있고 ItemReader 를 구현해 직접 커스텀 클래스를 구성할 수도 있다. 여기서는 FlatFileItemReader 라는 클래스를 이용해 파일을 읽을것이다. 

      return new FlatFileItemReaderBuilder()
            .name("fileItemReader")
            .resource(new ClassPathResource("big-data.csv"))
            .lineMapper(defaultLineMapper)
            .linesToSkip(1)
            .build();

FlatFileItemReader 에는 아래의 2개의 프로퍼티가 필수 값이다. 한 개는 파일의 경로를 지정하는 resource 이고 다른 한 개는 source 데이터와 target object 와의 맵핑 방법을 담고 있는 lineMapper 이다. 단지 파일 경로를 정해주는 resource 는 어려울게 없으니 lineMapper 를 한 번 살펴 보겠다. lineMapper 는 인자로  DefaultLineMapper 를 사용하고 있는데 DefaultLineMapper 역시 DelimitedLineTokenzier 와 BeanWrapperFieldSetMapper 2개의 객체가 필요하다. 각 클래스의 참조 관계를 보면 아래 그림과 같다.

 실제로 어떤 동작을 정의하는 건 DelimitedLineTokenzier, BeanWrapperFieldSetMapper 2개의 클래스에서 처리한다.

  • DelimitedLineTokenzier
    CSV 파일의 필드 구분자를 지정하고 각 값들의 필드명을 지정한다. 아래는 해당하는 코드이다.
DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer(",");
//tokenizer된 순서대로 필드명을 지정한다.
lineTokenizer.setNames("year", "industryAggregation", "industryCode", "industryName", "units",
    "variableCode",
    "variableName", "variableCategory", "value", "industryCode2");
  • BeanWrapperFieldSetMapper
    파일에서 추출된 값들을 DTO 클래스에 맵핑한다.
BeanWrapperFieldSetMapper fieldSetMapper = new BeanWrapperFieldSetMapper();
fieldSetMapper.setTargetType(CsvTestDto.class);//맵핑할 클래스
fieldSetMapper.setStrict(true);//필드 맵핑할 수 없는 필드가 존재할 경우 fail 처리

setStrict true는 DelimitedLineTokenzer 에서 선언한 필드 이름과 DTO 클래스의 변수명들과의 맵핑을 할 때 맵핑이 불가능한 (이름이 서로 다른) 필드가 존재할 경우 예외를 발생시키고 false 인 경우 null 값으로 채우고 그냥 진행시킨다.
재미있는건 이름이 꼭 같지 않더라도 지정한 이름이 변수명을 포함하고 있으면 맵핑을 한다. 아래 이미지와 같이 yearaaa 로 네임을 지정해도 DTO 클래스에 잘 담겨진다.

마지막으로 DefaultLineMapper 객체를 생성해서 위 2개의 객체를 Set 하고 FlatFileItemReader 에 Set 하면된다.

DefaultLineMapper defaultLineMapper = new DefaultLineMapper();
defaultLineMapper.setLineTokenizer(lineTokenizer);
defaultLineMapper.setFieldSetMapper(fieldSetMapper);

return new FlatFileItemReaderBuilder()
    .name("fileItemReader")
    .resource(new ClassPathResource("big-data.csv"))
    .lineMapper(defaultLineMapper)
    .linesToSkip(1)    //첫번째 라인은 header 이기 때문에 skip
    .build();

 

ItemWriter

 단순히 객체의 값을 표준 출력을 하는 Writer 객체이다. 스프링 배치에서 제공하는 클래스를 사용하지 않고 람다식을 이용해서 구현했다.

@Bean
public ItemWriter<CsvTestDto> itemWriter() {
    return items -> {
        for (CsvTestDto item : items) {
            System.out.println(item);
        }
    };
}

이제 실행을 해보면 아래와 같이 파일에 내용을 출력하는 것을 확인할 수 있다.

결론

CSV 파일을 읽어서 프린트하는 배치를 만들어 보았다. 다음에는 이어서 읽은 데이터를 DB에 저장하는 형태로 배치를 변경해보겠다.

전체 코드는 아래 github 의 chap2 브랜치 를 참고하면 된다.
https://github.com/warpgate3/spring-batch-tistory.git

 

'Spring' 카테고리의 다른 글

Spring Batch 5 GA  (0) 2022.12.23
Spring Security Class Diagram  (1) 2022.11.24
Spring Batch Sample Code (1)  (4) 2022.09.20
Spring Boot Reactive + Postgresql(r2dbc)  (0) 2020.09.23
Async method with Spring boot  (0) 2020.08.06
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크