Mybatis > RowBounds 의 고찰

 


리스트의 페이징(Paging)을 보던 중 처음보는 클래스를 발견했다.

RowBounds 클래스이다.

사용하는 곳을 본적이 없어서 이번에 처음 보게 되었다.

페이징은 보통 쿼리에서 해결하곤 했다.

크거나 같다 (>= <=) , between 등을 이용해서...

 

그런데 RowBounds 클래스가 페이징을 해준다는 것이다.

파라미터로 가져오고 싶은 구간을 넘겨주면 그 구간의 데이터를 리턴한다는 것이다.

바로 API를 뒤져봤다.

 



 

다음은 Mybatis API에서 발췌한 정보이다. ( 출처 )

마지막으로 리턴되는 데이터의 범위를 제한하거나 결과를 핸들링 하는 로직을 부여할 수 있는 3개의 select 메소드가 있다.

 

1
2
3
4
5
<E> List<E> selectList (String statement, Object parameter, RowBounds rowBounds)
<K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowbounds)
void select (String statement, Object parameter, ResultHandler<T> handler)
void select (String statement, Object parameter, RowBounds rowBounds, ResultHandler<T> handler)
 
cs

 


RowBounds 파라미터는 마이바티스로 하여금 특정 개수 만큼의 레코드를 건너띄게 한다. RowBounds클래스는 offsetlimit 둘다 가지는 생성자가 있다.

1
2
3
 int offset = 100;
 int limit = 25;
 RowBounds rowBounds = new RowBounds(offset, limit); 
cs


가장 좋은 성능을 위해 결과셋의 타입을 SCROLL_SENSITIVE나 SCROLL_INSENSITIVE로 사용하라.

ResultHandler파라미터는 레코드별로 다룰수 있도록 해준다. List에 추가할수도 있고 Map, Set을 만들수도 있으며 각각의 결과를 그냥 던질수도 있다. ResultHandler로 많은 것을 할 수 있고 마이바티스는 결과셋을 다루기 위해 내부적으로 사용한다.

 

인터페이스는 매우 간단하다.

1
2
3
4
 package org.apache.ibatis.session;
 public interface ResultHandler<T> {
   void handleResult(ResultContext<extends T> context);
 }
cs

이런 내용이다.

 

읽어보면 offset 만큼 건너띄고 limit 만큼 가져온다 라고 해석된다.

 


여기서 몇가지 의문점이 생겼다.

어떤 방식으로 페이징처럼 처리되는지 궁금했다.

예를들면 컬랙션을 이용하여 특정 구간을 잘라서 처리하는지 쿼리를 변조해서 처리하는지 궁금했다.

성능도 어느정도일지 궁금했다.

 


그래서 200만건 데이터를 가지고 테스트 해보았다.

먼저 보통의 페이징 기법(Paging Query)과 비교했다.

 


페이지는 10개씩 보여주도록 했다.

결과는 신기했다.

 

일단 RowBounds 클래스를 사용했을 때 Log4j에 찍히는 쿼리의 변화는 없었다.

그말인 즉슨 XML에 정의된 쿼리 그대로 console에 출력됐다.

 

하지만 데이터는 계속 offset + limit 갯수만큼 가져왔다.

 

소스를 까보면 어떻게 처리될지 알 수 있겠지만...

 

일단은 그냥넘어갔다.

 

다음 표를 보자.

페이지에 따른 조회된 데이터 갯수를 정리해 보았다.

(소스를 까보지 않았으니 Log4j 로 출력되는 데이터 결과값 기준으로 작성)

 

 

 페이징 기법(Pagin Query)

 RowBounds

 1 페이지 ( 1 ~ 10 )

 10건 조회

 10건 조회

 2 페이지 ( 11 ~ 20 )

 10건 조회

 20개 조회 후 10번째 부터 10건

 3 페이지 ( 21 ~ 30 )

 10건 조회

 30개 조회 후 20번째 부터 10건

.

.

.

 

 10 페이지 ( 91 ~ 100 )

 10건 조회

 100개 조회 후 90번째 부터 10건

 

 

 



더이상의 테스트는 의미없다.

 

마지막 페이지를 눌렀다가 서버를 강제종료했다.

 

RowBounds 는 소량의 데이터에서 필요한 구간을 가져올 경우 엄청나게 효율적이라고 판단된다.

 

소스도 깔끔하니 보기 좋다.

 

하지만 건너띄어야 할 데이터를 모두 가져온다는 함정이 있다.

 

이는 건너띄어야 할 데이터가 많으면 많을수록 불리하다.

 

속도 역시 너무 좋지 않았다.

 

 

 

결론적으로 RowBounds 클래스 원리와 특성을 알고 난 후 적절한 상황 (소량의 데이터이며 증가할 여지가 없는 등 ?) 에서의 사용은 긍정적이다.

그러나 그렇지 않은 경우에는 완전히 배제하는 것을 추천한다.
 

by 개발자 CofS 2016.08.09 09:36
  • 지승훈 2019.04.04 10:09 신고 ADDR EDIT/DEL REPLY

    페이징시 저는 rowbons를 사용하고있는데요.

    rowbons는 파라미터 offset , limit 들어오는데요 offset+limit 만큼 데이터를 불러와

    offset 만큼 자르고 그 이후 부터 데이터를 가져오는데요. ???맞나요??? 10,10 이라면 20개를 조회해서 맨앞 10개 데이터 자르고 11부터 19까지 출력을 한다로 이해했습니다.



    리스트 출력 개수는 한페이지당 10개라고 가정한다면 35개의 데이터를 표현하려면 4페이지가 필요합니다.

    그런데 0,10 10,10 20,10 30,10

    1 2 3 4 이렇게 페이지가 있다면 4페이지는 5개의 데이터만 불러와야하는데 30,10이면 40개중 30~39데이터를 반환할텐데 현재 5개만 가져와야하는데 뭔가 안맞는거 같아서요 이게 어떻게 돌아가는건가요?

    • Favicon of https://cofs.tistory.com BlogIcon 개발자 CofS 2019.04.04 10:27 신고 EDIT/DEL

      안녕하세요 ㅎㅎ
      일단 테스트는 해보지 않고 말씀드림을 양해부탁드립니다

      마지막 페이지 일 경우에는 2가지 정도로 예상할 수 있겠네요
      35개 데이터를 모두 가져와서 10개를 잘라서 리턴해야 하는데 30부터 자르기 시작하면 남은 데이터가 5개뿐이기 때문에 5개만 리스트로 리턴하거나 값이있는 리스트 5개+ 값이 없는 리스트5개 총 10개를 리턴 하거나 할 것 같아요 ㅎㅎ

      혹시나 테스트 해보시고 피드백 주시면 감사하겠습니다 ^^!!

  • 지승훈 2019.04.04 10:57 신고 ADDR EDIT/DEL REPLY

    그런데 1의 경우 남은 데이터가 5개라는것을 어떻게 인식할수 있을까요 rowbouns가요?? 4페이지라면 30,10 인데요 그럼 무조건 40개의 데이터를 일단 가지고 온다는거 아닌가요???

    아니면



    SELECT ROWNUM AS RNUM, A.*
    FROM
    (SELECT * FROM TB_BOARD
    <if test="keyword != null">
    <choose>
    <when test="selected == 'subject'">
    WHERE TITLE LIKE '%' || #{keyword} || '%'
    </when>
    <when test="selected == 'content'">
    WHERE CONTENTS LIKE '%' || #{keyword} || '%'
    </when>
    <otherwise>
    </otherwise>
    </choose>
    </if>
    ORDER BY CREA_DTM ASC) A
    ORDER BY RNUM DESC

    이조건에 맞게 가져와서 짜른다는 건가요 설명상으로는 40개를 다 가져와서 30개를 자른다고 이해가 가서요

    • Favicon of https://cofs.tistory.com BlogIcon 개발자 CofS 2019.04.04 11:06 신고 EDIT/DEL

      오픈소스라 직접 mybatis 소스 오픈해보시는것도 좋을 것 같네요 ㅎㅎ
      제 예상엔 mybatis에도 RowMapper 같은 핸들러 들이 존재할 것이고 그런 핸들러들이 하게 되면 조회된 것들만 반환하게 되겠죠 ?
      그럼 첫번째 경우처럼 동작하지 않을까요 ?ㅎㅎ

  • 지승훈 2019.04.04 11:20 신고 ADDR EDIT/DEL REPLY

    혹시 위에 설명중에 제 소스상으로 4페이지라면 30,10 인데요 그럼 무조건 40개의 데이터를 일단 가지고 온다는거 아닌가요??? 아니면 제 쿼리 대로 결과를 일단 가져오고 자른다는 건가요? mybatis 문서를 보아도 해석이 잘안가서요 ㅠㅠ

    • Favicon of https://cofs.tistory.com BlogIcon 개발자 CofS 2019.04.04 11:26 신고 EDIT/DEL

      본문은 이론에 대한 설명입니다 ㅎㅎ
      참고해주세요 ㅎㅎ

  • 지승훈 2019.04.04 11:40 신고 ADDR EDIT/DEL REPLY

    마지막 질문 하나만 더 드릴게요 제가 35개 조회할떄 4페이지까지 만들어진다했는데 그럼 조회가 된것은 35개 인데 rowbounds를 통해 만들어지 30,10 ->두개 더해서 앞에 만큼 넘어 데이터 가져옴 35개밖에 안되는데 40개를 어떻게 가져올수있쬬??

    • Favicon of https://cofs.tistory.com BlogIcon 개발자 CofS 2019.04.04 13:53 신고 EDIT/DEL

      제가 본문을 제대로 읽어보지도 않고서 이럴것이다 예측으로 답변을 드리니 자꾸 산으로 가네요 ㅠㅠ

      정리합니다.
      rowbounds 는 일단 기본적으로 selectlist 함수를 사용합니다.
      그럼 당연히 return 개수는 데이터 최대 개수를 넘지 못합니다.
      (github 소스코드 참고)

      최대 개수를 넘어선 값을 리턴하려면 result handler 구현하시면 가능합니다.

      모든 내용은 본문에 있고 문서도 참고해 주시면 됩니다.
      http://www.mybatis.org/mybatis-3/getting-started.html

      오랜만에 보니까 제가 뭘 썻었는지도 새록새록 합니다.

      처음부터 정확한 답변을 드렸어야 하는데 죄송합니다 ㅠ