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 SQL과 footer 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.header 와 PaginationMapper.footer 를 사용하는 다수의 쿼리에서 정렬 기준이 모두 다를 수 있으므로, include 태그의 property 태그를 사용하여 직접 정렬 기준 컬럼을 설정하도록 한다.
조회를 하면, 조회된 모든 행에 총 건수(TotalCount)와 총 페이지 건수(TotalPage)가 조회된다.
이것을 프론트단에 전달하며 프론트단의 페이징 처리를 하면 된다.
필자는 jQuery의 Pagination Plugin을 사용했다.
#mybatis #paging #pagination
'Java > MyBatis' 카테고리의 다른 글
MyBatis에서 프로시져 호출하기. (0) | 2018.03.16 |
---|---|
MyBatis 설정 오류. (0) | 2016.10.28 |