본문 바로가기

Java/MyBatis

MyBatis에서 Paging 처리 하기.

MyBatis를 통해서 Paging 처리 하기. (MS-SQL 2012 이상)


최근 진행중인 프로젝트를 마무리 해가며, 뒤로 뒤로.. 미뤄둔 공통 Paging 작업을 처리했다.

반영할 운영서버에서는 MS-SQL을 사용하고 있었고, 버전은 2016으로 최신버전이었다.


MS-SQL에서 페이징을 처리하는 방법은 크게 2가지로 요약할 수 있다고 한다.


1. ROW_NUMBER 함수를 이용하는 방법.

서브 쿼리에서 ROW_NUMBER 함수를 이용해 행번호를 할당하고, 상위 쿼리에서 이를 기준으로 필터링하는 방법이다.

대략 아래와 같은 구조를 가지게 된다.

SELECT *
FROM
(
SELECT ROW_NUMBER() OVER(ORDER BY ..) AS ROW_NUM,
...
)
WHERE ROW_NUM BETWEEN (#{pageNumber} - 1) * (#{rowsPerPage} + 1) AND (#{pageNumber} * #{rowsPerPage})
ORDER BY ..


2. OFFSET/FETCH 를 사용하는 방법.

SQL SERVER 2012 에서 추가된, 페이징 전용 쿼리다.

대략 아래와 같은 구조를 가지게 된다.

SELECT ..
FROM ..
ORDER BY ..
OFFSET ((#{pageNumber} - 1) * #{rowsPerPage}) ROWS
FETCH NEXT #{rowsPerPage} ROWS ONLY


SQL SERVER의 버전에 따라서 두가지 방법 중 한가지를 선택하면 된다.

물론 2012 이상의 버전을 사용하고 있다면, 2번(OFFSET/FETCH) 방법을 추천한다.


두가지 방법의 차이점은 여기에서 확인할 수 있다.


화면에서 페이징을 처리하기 위해서는 아래의 몇가지의 값이 필요하다.

1. PageNumber - 표시할 페이지 번호.

2. RowsPerPage - 한페이지에 표시할 데이터의 건수.

3. TotalCount - 조회된 총 데이터의 건수.

4. TotalPage - 표시할 총 페이지의 건수.


PageNumber와 RowsPerPage는 화면에서 전달되는 파라메터일 것 이다.

이를 가지고 조회된 TotalCount 를 RowsPerPage로 나누고 올림하면 TotalPage 를 도출 할 수 있다.


만약 105건의 레코드가 존재 하고, 한페이지에 표시할 행의 수가 10건이라면, 여기에서는 11개의 페이지가 필요하게 된다.

105 / 10 = 10.5 이고 이를 올림하면 11이 된다.


계산을 어디에서 해도 되지만, 나는 MyBatis의 include 태그를 최대한 활용하기 위해, 쿼리에서 모두 해결하기로 했다.

include 태그는 MyBatis에서 반복되는 쿼리문을 저장하여 사용할 수 있는 방법이다.


하나의 쿼리에서 모든 작업을 하기 위해 WITH 문을 사용하기로 했다.

모든 조회 쿼리의 상단(header) 하단(footer)에 추가될 공통 SQL문을 저장하고, 모든 Select의 상, 하단에 inlcude 하는 방법이다.


Paging 을 위해 header SQLfooter SQL을 저장하기 위한, PaginationMapper.xml 을 추가하고 아래와 같이 작성한다.


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="PaginationMapper">
<sql id="header">
WITH
SEARCH_ORG
AS
(
</sql>
<sql id="footer">
),
TOTAL_COUNT <!-- [총건수] -->
AS
(
SELECT COUNT(1) AS TOTAL_COUNT
FROM SEARCH_ORG A1 <!-- [조회원본] -->
)
SELECT A1.*,
B1.TOTAL_COUNT,
ISNULL(#{pageNumber}, 1) AS PAGE_NUMBER,
CEILING(CONVERT(FLOAT, B1.TOTAL_COUNT) / ISNULL(#{rowPerPage}, 10))
AS TOTAL_PAGE
FROM SEARCH_ORG A1 <!-- [조회원본] -->
CROSS JOIN
TOTAL_COUNT B1 <!-- [총페이지] -->
ORDER BY
A1.${ORDER} DESC
OFFSET (ISNULL(#{pageNumber}, 1) - 1) * ISNULL(#{rowPerPage}, 10) ROWS
FETCH NEXT ISNULL(#{rowPerPage}, 10) ROWS ONLY
</sql>
</mapper>


실제로 조회되는 쿼리는 아래와 같이 작성한다.


<select id="selectList">
<include refid="PaginationMapper.header"/>
SELECT
...
FROM
...
<include refid="PaginationMapper.footer">
<property name="ORDER" value="..."/>
</include>
</select>



SQL SERVER의 OFFSET/FETCH 문을 사용하기 위해서는, 필수로 ORDER BY 절이 필요하다.

공통 PaginationMapper.headerPaginationMapper.footer 를 사용하는 다수의 쿼리에서 정렬 기준이 모두 다를 수 있으므로, include 태그의 property 태그를 사용하여 직접 정렬 기준 컬럼을 설정하도록 한다.


조회를 하면, 조회된 모든 행에 총 건수(TotalCount)와 총 페이지 건수(TotalPage)가 조회된다.


이것을 프론트단에 전달하며 프론트단의 페이징 처리를 하면 된다.


필자는 jQuery의 Pagination Plugin을 사용했다.


#mybatis #paging #pagination

'Java > MyBatis' 카테고리의 다른 글

MyBatis에서 프로시져 호출하기.  (0) 2018.03.16
MyBatis 설정 오류.  (0) 2016.10.28