ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spring Batch with QuerydslItemReader
    JPA 2020. 5. 2. 12:06
    반응형

    무신사 java 개발팀에서는 다양한 배치 작업을 하고 있는데요

    spring batch를 사용하여 배치 작업을 하고 있습니다

    저희는 database에 접근하기 위해서 querydsl을 사용하고 있는데요

    spring batch에서 querydsl을 지원해주고 있지 않습니다.

    그래서 어쩔수 없이 작년에 코드를 만들었는데요 생각 보다 간단합니다.

    필요하신 분들이 계실 것 같아서 공유하도록 하겠습니다.

    저희가 실제로 실무에서 사용하고 있는 코드이기도 합니다만 기본 샘플이니 수정할 게 있다면 수정해서 사용해주세요!

     

    package com.musinsa.batch.customreader;
    
    import com.musinsa.config.db.JpaSqlCustomQuery;
    import org.apache.commons.lang3.ObjectUtils;
    import org.springframework.dao.DataAccessResourceFailureException;
    
    import javax.persistence.EntityManager;
    import java.util.concurrent.CopyOnWriteArrayList;
    
    
    public class QuerydslItemReader<T> extends AbstractPagingCustomItemReader<T> {
    
        private JpaSqlCustomQuery<T> jpasqlCustomQuery;
        private EntityManager entityManager;
        private Callback callback;
    
        public QuerydslItemReader(JpaSqlCustomQuery<T> jpasqlCustomQuery) {
            this.jpasqlCustomQuery = jpasqlCustomQuery;
        }
    
        public QuerydslItemReader(JpaSqlCustomQuery<T> jpasqlCustomQuery, Callback callback) {
            this.jpasqlCustomQuery = jpasqlCustomQuery;
            this.callback = callback;
        }
    
        @Override
        protected void doOpen() throws Exception {
            super.doOpen();
    
            entityManager = jpasqlCustomQuery.getEntityManager();
    
            if (entityManager == null) {
                throw new DataAccessResourceFailureException("Unable to obtain an EntityManager");
            }
    
        }
    
        @Override
        protected void doReadPage() throws Exception {
    
            if (results == null) {
                results = new CopyOnWriteArrayList<>();
            } else {
                results.clear();
            }
    
            int page = getPage();
            int pageSize = getPageSize();
    
            jpasqlCustomQuery.offset(page * pageSize).limit(pageSize);
    
            results.addAll(jpasqlCustomQuery.fetch());
    
            if (ObjectUtils.isNotEmpty(callback)) {
                callback.call(results);
            }
    
        }
    
        @Override
        protected void doJumpToPage(int itemIndex) {
    
        }
    
        @Override
        protected void doClose() throws Exception {
            entityManager.clear();
            super.doClose();
        }
    }
    

     

     

    참고로 AbstractPagingCustomItemReader는 복사해서 저희가 클래스로 따로 만들어서 그것을 상속해서 사용하도록 하고 있습니다

    callback같은 경우에는 제거 해서 사용하도록 해주세요!

    그러나 올려둔 이유는 있습니다 그 이유는 스프링 배치에서는 일대 다 조인을 해서 사용하기 보다는 1:1 관계에 있는 것만 조인을 해서 가져와서 1:N 조인 할 놈은 callback 함수에서 따로 다시 조인을 하거나 in 조건으로 가져오게 됩니다

    일대 다 조인을 하게 되면 가져와야 될 사이즈가 너무 커지게 됩니다 물론 그렇게 됐을 때 사이즈가 얼마 안되면 상관이 없지만

    배치 작업은 최소 메인 데이터가 몇십만건이기때문에 그놈이 일대 다 했을 경우 몇배가 커질 수도 있어서 callback클래스에 있는 call을 사용해서 처리 합니다 같은 데이터가 여러개 반복되서 전송될 경우 네트워크를 통해서 같은 데이터를 여러번 보내는 문제가 발생 할 수 있습니다 용량도 늘어나고 그러면 전송속도도 더 떨어지겠죠? 또한 db에서도 같은 데이터를 여러번 끌어올리는 문제도 있을거고요 물론 dbms의 캐싱이나 성능 개선으로 많이 개선이 되었겠지만 그래도 안하는게 성능에는 더 좋을 것 같습니다.

     

    궁금한거 있으시면 댓글 달아주세요

     

     

    반응형

    댓글

Designed by Tistory.