본문 바로가기

개발/java & web

spring ibatis jxl 사용 엑셀 데이터 일괄 처리

웹에서 대용량의 엑셀 데이터를 입력해야 하는 상황.

1. 파일 업로드 과정은 생략...
2. jxl 사용하여 엑셀 파일 읽기(poi보다 대용량 처리가 빠르다고 함)
  : workbook 생성하는 과정이 다소 오래 걸림(개발시 10M 에 30초 가량, 운영에선 좀 낫겠지..)
  , 파일이 10M 넘어가니 뻗어버린다..

3. 100건씩 list에 담아 ibatis에 보냄
  : 10,100,200 건씩 테스트 결과 가장 효과적, 500건 넘으면 outofmemory.... 서버 성능에 따라 적당히..

4. ibatis에서 배치 일괄 처리
  : 반드시 트랜잭션 처리(startTransaction...)를 같이 해줘야 함. 트랜잭션 처리 삭제하고 테스트하니 일반 처리와 다를 게 없다.
   배치 처리하니 10분 넘던게 1분 이내로 줄어 들었다.
   그리고 커밋을 마지막에 한번 만 해주려고 했더니 뻗어 버린다.
   그래서 org.apache.commons.dbcp.BasicDataSource 설정의 maxActive 를 늘려주니 늘려 준 만큼 효과가 있긴 하다.
   굳이 그렇게까지 할 필요가 없어 그냥 100건씩 커밋한다.
   어차피 임시 테이블에 넣는 거라서 실패 시 초기화 하면 되니까..


# XXXController.java
  //파일 업로드 이후부터..
   long fileStartTime = System.currentTimeMillis();
   String serverPath = (String)fileUpload.get("serverPath");
   //엑셀 파일 읽기
   Workbook workbook = Workbook.getWorkbook(new File(serverPath));
   Sheet[] sheets = workbook.getSheets();
   
   //파일분석 시간 및 데이터 입력 시간
   fileStartTime = System.currentTimeMillis()-fileStartTime;
   long startTime = System.currentTimeMillis();
   
   String load_dt = DateUtil.getShortDateString();
   String tableName = this.getTableName(request);
   String initQueryId = "mercLoad.truncateMercData";
   
   for (Sheet sheet : sheets) {
    //현재 sheet의 총 열과 행
    int totalRows = sheet.getRows();
    int totalCols = sheet.getColumns();
    List<Map> list = new ArrayList<Map>();
    
    for (int i = 1; i < totalRows; i++) {
     Map<String, String> insertMap = new HashMap<String, String>();
     insertMap.put("TABLE_NAME", tableName);
     insertMap.put("LOAD_DT",  load_dt);
     
     for (int j = 0; j < totalCols; j++) {
      Cell cell = sheet.getCell(j, i);
      if(cell == null) continue;
      
      switch (j) {
       case 0:
        insertMap.put("컬럼명", cell.getContents());
        break;
       case 1:
        insertMap.put("컬럼명", cell.getContents());
        break;
        ............................................
       default:
        break;
      }//switch
     }//cell
     
     list.add(insertMap);
     
     int lastRow = totalRows-1;
     
     //100건씩 insert 후 list clear
     if(list.size() == 100 || i == lastRow){
      System.out.println("list.size() : "+list.size()+", i : "+i);
      //--insert
      commonDAO.load("mercLoad.loadMercData", list, insertMap, initQueryId);
      
      list.clear();
     }
     
    }//row
    
    logger.info("총 건수 : "+(totalRows-1));
   
   }//sheet
    
   logger.info("파일분석 소요시간 : "+fileStartTime);
   logger.info("파일입력 소요시간 : "+(System.currentTimeMillis() - startTime));

# XXXDao.java
  /**
  * 배치작업 트랜잭션 일괄 처리시 사용 [godbasic, 2010.03.23]
  * @param arg0 : query id
  * @param list : 일괄 처리할 list
  * @param object : 테이블 초기화 시 넘겨줄 data
  * @param initQueryId : 테이블 초기화 시 넘겨줄 query id
  * @throws DataAccessException
  * @throws SQLException
  */
 public void load(String arg0, List list, Object object, String initQueryId) throws DataAccessException, SQLException {
  try {
   this.getSqlMapClient().startTransaction();
   this.getSqlMapClient().startBatch();

   
   Iterator iterator = list.iterator();
   while (iterator.hasNext()) {
    Map map = (Map) iterator.next();
    this.getSqlMapClient().insert(arg0, map);
   }
   
   this.getSqlMapClient().executeBatch();
   this.getSqlMapClient().commitTransaction();

   
  } catch (SQLException e) {
   e.printStackTrace();
   //delete table
   this.getSqlMapClientTemplate().delete(initQueryId, object);
  } finally {
   this.getSqlMapClient().endTransaction();
  }
 }

10분 넘게 걸렸던 작업이 1분으로 줄어들었다.
뿌듯..