SpringBatch : JpaPagingItemReader

    이 글은 인프런 정수원님의 강의를 복습하며 작성한 글입니다.

    JpaPagingItemReader

    JpaPagingItemReader는 Paging 기반의 구현체다. EntityManagerFactory를 넘기고, EntityManager를 바탕으로 JPA 형식으로 쿼리를 날려 Item을 DB에서 읽어올 수 있다.  또한 JpaPagingItemReader는 JdbcPagingItemReader와 마찬가지로 doRead() 메서드에서 동기화 처리가 되어있기 때문에 멀티 쓰레드 환경에서도 안전하다. 

     

    JpaPagingItemReader API

    @Bean
    public ItemReader<Customer> customerItemReader() {
    
        HashMap<String, Object> paramValues = new HashMap<>();
        paramValues.put("idValue", 2091L);
    
        return new JpaPagingItemReaderBuilder<Customer>()
                .name("JpaPagingItemReaderJob")
                .entityManagerFactory(emf)
                .pageSize(2)
                .queryString("select c from Customer c where c.id = :idValue")
                .parameterValues(paramValues)
                .build();
    }

    JpaPagingItemReader는 JpaPagingItemReaderBuilder에 설정값을 설정하고, 이 Builder 클래스를 통해서 값을 만들게 된다. 따라서 Builder를 먼저 생성하고, Builder에 아래 API를 사용해서 값을 설정해주면 된다. 

    • name : JpaPagingItemReade의 이름을 설정해준다.
    • pageSize : 한번에 불러올 페이지 크기를 설정해준다. 
    • queryString : JPQL을 작성해준다. 이 때, 객체의 대/소문자를 유의해준다.
    • EntityManagerFactory : EntityManager를 생성할 EntityManagerFactory를 지정한다. DI로 처리
    • ParameterValue : JPQL에서 사용할 쿼리 파라미터를 저장해준다. <String, Object> HashMap을 만들어 전달해줌. 

    Order By가 제공이 안되서 불편할 수도 있다고 하겠지만, 이미 JPQL에서 ORDER BY 구문을 사용해서 값을 정리할 수 있다. 따라서 이런 부분을 신경써서 코딩을 하면 될 것 같다.

     

    JpaPagingItemReader 구조

    1. Step은 Open 메서드를 통해 ItemStream을 열고, ItemStream은 사용할 EntityManager를 EntityManagerFactory를 통해서 만들어준다. 
    2. Step은 Read를 통해서 JpaPagingItemReader로 넘어간다. 여기서 EntityManager는 JPQL을 보내고, 페이지 단위로 Query라는 객체에 저장해둔다. 이 객체는 Result List에 저장되어서 PagingItemReader로 전달된다.  따로 Mapper는 필요없는게, JPA 자체가 ORM이라 맵핑을 해주기 때문이다.
    3. 처리가 끝났으면, ItemStream.close를 통해서 처리를 해준다. 

     

    MaxItemCount의 의미

    MaxItemCount는 Reader가 읽어올 수 있는 최대값을 의미한다. 예를 들어 테이블에 10,000개의 데이터가 있다고 했을 때, MaxItemCount가 1,000이고, Chunk가 10이면, MaxItemCount 1,000개에 대한 값만 처리를 한다. 예를 들어 이런 상황에서 ItemWriter가 DB에 값을 밀어넣는 상황이라고 가정하면, 실제로 DB에는 1,000개의 데이터만 들어가게 된다. 

     

    결론을 정리하면 다음과 같다고 이해를 하면 될 것 같다

    • Chunk : 한번에 처리할 단위의 데이터 크기
    • maxItemCount : 처리할 데이터의 전체 크기. DB에 30,000개가 있을 때, 이 값을 10,000으로 설정하면 실제 결과물은 10,000개만 만들어진다.

     

     

     

     

    JpaPagingItemReader는 Thread-Free

    AbstractPagingItemReader.doRead()

    JpaPagingItemReader는 Thread-Free하다. JpaPaginItemReader는 AbstractPaingItemReader.doRead()를 통해서 Page 단위로 값을 가져온다. 그런데 이 때, doRead() 메서드가 synchronized 키워드로 동기화 처리가 되어있다. 따라서 Thread Free하게 동작하는 것을 알 수 있다. 

    doReadPage로 들어가보면 entityManager.createQuery()를 통해 JPQL을 직접 만들어서 값을 가져오는 것을 확인할 수 있다. 

     

    테스트 코드

    https://github.com/chickenchickenlove/springbatchstudy/tree/main/SpringBatchLecture/main/java/io/springbatch/springbatchlecture/dbitemreader

     

    테스트 코드 실행 결과

    GitHub에 있는 테스트 코드를 실행했다. 실행해보면 JPQL이 정상적으로 나가고, ItemProcessor까지 들어오는 것을 확인할 수 있다. 

     

     

    댓글

    Designed by JB FACTORY