본문 바로가기
Language/JAVA

ResultHandler(RowHandler)사용법

by 박살낼겨 2022. 3. 15.

왜 쓰나?

대용량 데이터를 Row 단위 즉, 행별로 처리하기 위함.

(= SELECT와 INSERT,UPDATE,MERGE를 한 건씩 수행) 

 

어떤 경우에 쓰이나?

필자의 경우 List에 70만건의 데이터를 담아서 처리하니 'GC overhead limit exceeded' 에러 발생 + OOM(Out Of Memory)에러 발생. 이를 해결하기 위해 썼다.

 

장점?

앞서 말한 것과 같이 '대용량 데이터'처리에 좋아 OOM에러를 피할 수 있다.(서버 멈추지 않기위해)

 

단점?

List에 담아서 처리하는 것 보다 훨~씬 느리다.

(그래서 시간 오래걸려도 상관없는 새벽시간 야음을 틈타 혼자 열심히 수행하는 Batch에 많이 쓰인다.)

 

 

본격적인 사용예시

 

<<Service.java 단>>

================================================================

public Map<String, Object> newSyncBatch(Map<String, Object> paramMap) 

    throws RuntimeException {
    final StopWatch stopWatch = new StopWatch("NewSyncBatch");

     Map<String, Object> result = new HashMap<String, Object>();
     String endDt = "";
     String strDt = "";
    
     if(paramMap.get("strDt") == null && paramMap.get("endDt") == null) {
         // 변경일시가 오늘로 부터 하루 전
         strDt = EgovDateUtil.addYearMonthDay(EgovDateUtil.getToday(), 0, 0, -1);
         endDt = EgovDateUtil.getToday();
     } else{
         strDt = MapUtil.getString(paramMap.get("strDt"));
         endDt = MapUtil.getString(paramMap.get("endDt"));
     } 
    
stopWatch.start("실행날짜 : " + strDt + " ~ " + endDt + " 배치실행");

Map<String, Object> param = new HashMap<String, Object>();
param.put("endDt", endDt);
param.put("strDt", strDt);

// 확진자 정보(어제부터 오늘까지) - CI컬럼 없는 테이블 kdcadb
infoBatch(param);

stopWatch.stop();
logger.info("Batch 실행시간 : " + stopWatch.prettyPrint());
logger.info("Batch 실행 세부사항 : " + stopWatch.getTotalTimeSeconds() + "초");
return result;
    }
    
    
private void infoBatch(Map<String, Object> param) {
dao.selectByHandler("scheduler.SELECT_NEW_SYNC", 

    param, new Handler(this, outsideDao ,param));
}

================================================================

 

step1. Service.java

infoBatch(param);

: 수행시키고자 하는 파라미터를 넘겨준다. 파라미터가 필요없으면 안 넘겨주는 메서드를 만들면 된다.

 

private void infoBatch(Map<String,Object> param

: dao에 만들어둔 selectByHandler 메서드에 맞게 파라미터들을 넘겨줘야 한다.

  파라미터들 중 new Handler(this,~~) 이 부분 핵심** 아래 참고

  dao.selectByHandler~~(this. outsideDao,~~) 이 부분 조심

  필자는 다른 DB정보를 사용했기 때문에 'A' DB에서 select한 결과로 'B' DB에 Merge를 수행시킨다.

 

 

 

 

<<DAO 단>>

================================================================

@Repository
public class DsndgnssDao {

@Autowired
@Resource(name="sqlSessionDsndgnssTemplate")
private SqlSessionTemplate sqlSessionTemplate;

public void selectByHandler(String statementName, ResultHandler handler) {
     sqlSessionTemplate.select(statementName, handler);
    }

public void selectByHandler

    (String statementName, Map param, ResultHandler handler) {
     sqlSessionTemplate.select(statementName, param, handler);
    }
    
}

================================================================

 

step2. DAO생성

: 기존 Dao.java에 메서드를 추가하면 된다.(당연 기호에 맞는 메서드명 혹은 명명규칙에 따른 메서드명)

: @Resource 부분은 해당 프로젝트에 맞는 환경설정을 한다.(DB프로퍼티 파일 같은데 설정 값이 있을 것)

 

 

 

 

<<Handler.java 단>>

================================================================

package com.abilsys.handler;

import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;

import org.apache.ibatis.session.ResultContext;
import org.apache.ibatis.session.ResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.abilsys.dao.OutsideDao;
import com.abilsys.util.MapUtil;
import com.abilsys.web.service.DsndgnssBatchService;

public class DsndgnssHandler implements ResultHandler {
    
    private OutsideDao outsideDao;

    private DsndgnssBatchService dsndgnssBatchService;
    
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    
    private String strDt;
    private String endDt;

    public DsndgnssHandler

    (DsndgnssBatchService dsndgnssBatchService, OutsideDao outsideDao, Map<String, Object> param)

    {
        this.dsndgnssBatchService = dsndgnssBatchService;
        this.outsideDao = outsideDao;
        this.strDt = MapUtil.getString(param.get("strDt"));
        this.endDt = MapUtil.getString(param.get("endDt"));
    }
    
    @SuppressWarnings("unchecked")
    @Override
    public void handleResult(ResultContext resultContext) {
Map<String, Object> resultData = (Map<String, Object>) resultContext.getResultObject();

    Map param = new HashMap<>();
     

  String patntCI =

        dsndgnssBatchService.getPatntCI(MapUtil.getString(resultData.get("PATNT_IHIDNUM"))

                                                     , MapUtil.getString(r    esultData.get("DSNDGNSS_INNB")));

    param.put("patnt_ci",patntCI);
    param.put("dsndgnss_innb",resultData.get("DSNDGNSS_INNB"));

    outsideDao.insert("dsndgnss_scheduler.MERGE_PA_DSNDGNSS_INFO_NEW", param);
    }
    
}

================================================================

 

step3. Handler.java를 생성한다.

: 클래스파일을 따로 만든이유는 에러를 방지하기 위함.

  메서드로 그냥 만들면 에러 날 수도 있단다.(어디서봤는데 기억안남)

: **중요** handleResult 메서드는 자동으로 만들어지고(?) 여튼 에러 밑줄 생기면(?) 생성하면 된다.

  이 메서드는 내부적, 시스템적으로 handler가 알아서 호출을 한다. 

  **주의** 테스트라면 SELECT 되는 데이터를 넣어주자

  DB에 데이터가 없으면 에러(null 에러)가 나고 삽질을 엄청 하게된다. 왜 안되지? 이 메서드 어디서 호출하지? ㅇㅈㄹ