스프링 부트 ( 2.7.0) mybatis 사용
1. A 유형 페이징 처리 방법
1) MySQLPageRequest 추가
import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; @Setter @Getter @AllArgsConstructor @NoArgsConstructor @ToString public class MySQLPageRequest { private int page; private int size; @JsonIgnore @ApiModelProperty(hidden=true) private int limit; @JsonIgnore @ApiModelProperty(hidden=true) private int offset; }
2) MySQLPageRequestHandleMethodArgumentResolver 추가
import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.math.NumberUtils; import org.springframework.core.MethodParameter; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; import lombok.extern.slf4j.Slf4j; /** * * MySQL 쿼리 페이징 LIMIT , OFFSET 값을 자동 계산하여 MysqlPageRequest 클래스 담아서 컨트롤러에서 받을 수 있게함. * */ @Slf4j public class MySQLPageRequestHandleMethodArgumentResolver implements HandlerMethodArgumentResolver{ private static final String DEFAULT_PARAMETER_PAGE="page"; private static final String DEFAULT_PARAMETER_SIZE="size"; private static final int DEFAULT_SIZE =20; @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { HttpServletRequest request=(HttpServletRequest) webRequest.getNativeRequest(); //현재 페이지 int page =NumberUtils.toInt(request.getParameter(DEFAULT_PARAMETER_PAGE)); //리스트 갯수 int offset =NumberUtils.toInt(request.getParameter(DEFAULT_PARAMETER_SIZE), DEFAULT_SIZE); //시작지점 int limit=(offset * page) - offset; log.info("page : {} ", page); log.info("limit : {} , {} ", limit, offset); return new MySQLPageRequest(page, offset, limit, offset); } @Override public boolean supportsParameter(MethodParameter parameter) { return MySQLPageRequest.class.isAssignableFrom(parameter.getParameterType()); } }
3) PageRequestParameter 추가
import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; /** 페이지 요청정보 파라미터 정보 */ @Getter @Setter @AllArgsConstructor @NoArgsConstructor @ToString public class PageRequestParameter<T> { private MySQLPageRequest pageRequest; private T parameter; }
4 ) WebMvcConfig 에 다음 메소드 추가
/** 페이징 처리 */ @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { // 페이지 리졸버 등록 resolvers.add(new MySQLPageRequestHandleMethodArgumentResolver()); }
5) 컨트롤 사용예
/**2.게시판 페이징 검색처리 목록리턴 - 페이징 검색처리 첫번째 방법 (WebMvcConfig 에 페이지 리졸버 등록 방식) */ @GetMapping({"/pageSearchList"}) @ApiOperation(value="목록조회 - WebMvcConfig 에 페이지 리졸버 등록 방식", notes="2.게시판 페이징 검색처리 목록리턴 ") public BaseResponse<List<BoardDTO>> paginationSearchList( @ApiParam MySQLPageRequest pageRequest, @ApiParam BoardSearchParameter parameter ) throws Exception{ log.info("1.pageRequest :" , pageRequest); PageRequestParameter<BoardSearchParameter> pageRequestParameter=new PageRequestParameter<BoardSearchParameter>(pageRequest, parameter); log.info("2.pageRequestParameter :" , pageRequestParameter); return new BaseResponse<List<BoardDTO>>(boardService.paginationSearchList(pageRequestParameter)); }
6) mybatis 사용예
<select id="paginationSearchList" resultType="kr.so.songjava.mvc.domain.entity.Board"> SELECT B.BOARD_SEQ, B.BOARD_TYPE, B.TITLE, B.REG_DATE FROM T_BOARD B <where> <if test="@org.apache.commons.lang3.StringUtils@isNotEmpty(parameter.keyword)"> AND B.TITLE LIKE CONCAT('%', #{parameter.keyword}, '%') </if> <if test="@org.apache.commons.lang3.ObjectUtils@isNotEmpty(parameter.boardTypes)"> AND B.BOARD_TYPE IN ( <foreach collection="parameter.boardTypes" item="value" separator=","> #{value} </foreach> ) </if> </where> ORDER BY B.REG_DATE DESC LIMIT #{pageRequest.limit} , #{pageRequest.offset} </select>
7) 출력예
{ "code": "SUCCESS", "message": null, "pageMaker": null, "displayPageNum": 0, "data": [ { "boardSeq": 256, "boardType": { "code": "FAQ", "label": "자주묻는질문" }, "title": "wJmTCEydrm", "contents": null, "regDate": "2022-06-26T03:38:59.000+00:00" }, { "boardSeq": 512, "boardType": { "code": "FAQ", "label": "자주묻는질문" }, "title": "JQvHGGREkU", "contents": null, "regDate": "2022-06-26T03:38:59.000+00:00" } ] }
2. B 유형 페이징 처리 방법 ( 전체 게시글 갯수 구함 )
1) MysqlPageMaker 추가
import java.util.List; import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.annotations.ApiModelProperty; import kr.so.songjava.mvc.domain.enums.BoardTypeInsert; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.ToString; /** MySQL PageMaker **/ @Getter @Setter @AllArgsConstructor @ToString public class MysqlPageMaker { /** mysql 들어갈 값은 page 가 아니라 pageStart, perPageNum 이다. //limit #{pageStart}, #{perPageNum} */ private int page; private int perPageNum; @JsonIgnore @ApiModelProperty(hidden=true) private int pageStart; /** 하단 페이징 << 1 2 3 4 5 6 7 8 9 10 >> */ @JsonIgnore @ApiModelProperty(hidden=true) private int totalCount; //전체 개수 @JsonIgnore @ApiModelProperty(hidden=true) private int startPage; // 시작 페이지 @JsonIgnore @ApiModelProperty(hidden=true) private int endPage; // 끝페이지 @JsonIgnore @ApiModelProperty(hidden=true) private boolean prev; // 이전 여부 @JsonIgnore @ApiModelProperty(hidden=true) private boolean next; // 다음 여부 @JsonIgnore @ApiModelProperty(hidden=true) private int displayPageNum; @JsonIgnore @ApiModelProperty(hidden=true) private int tempEndPage; //마지막 페이지 /** 검색처리 추가 */ private String searchType; //NOTICE, FAQ,INQUIRY private String keyword; public MysqlPageMaker() { this.page=1; //초기 페이지는 1 this.perPageNum=10; //limit 10 개씩 보여준다. this.displayPageNum=6; //초기 6개 } public void setPage(int page) { //페이지 번호가 0이거나 0보다 작으면 1페이지로 한다. if(page <=0){ this.page=1; return; } this.page = page; } /** MyBatis SQL 의 Mapper 에서 인식해서 가져가는 파라미터 값 메소드 #{perPageNum} */ public void setPerPageNum(int perPageNum) { //몇개 씩 보여줄것인가 이다. 최대 100개씩 보여 줄것으로 설정한다. //만약 0보다 작거나 100 보다 크면 10으로 초기화 시킨다. if(perPageNum <=0 || perPageNum >100){ this.perPageNum=10; return; } this.perPageNum = perPageNum; } /** MyBatis SQL 의 Mapper 에서 인식해서 가져가는 파라미터 값 메소드 #{pageStart} */ public int getPageStart() { //실질적으로 Mybatis 에서 파라미터로 인식해서 가져오는 것은 get 이다. // 따라서 getPageStart 에서 값을 설정한다. //시작 데이터 번호 = (페이지 번호 -1 ) * 페이지당 보여지는 개수 this.pageStart=(this.page -1)*perPageNum; return this.pageStart; } //전체 페이지 설정과 동시에 하단에 뿌려질 페이지 계산하기 public void setTotalCount(int totalCount) { this.totalCount = totalCount; calcData(); } private void calcData(){ //현재 페이지 번호 / 하단 페이지번호 수 endPage=(int)(Math.ceil(page / (double)displayPageNum)*displayPageNum); startPage=(endPage - displayPageNum) +1; tempEndPage=(int)(Math.ceil(totalCount/(double)perPageNum)); if(endPage >tempEndPage){ endPage=tempEndPage; } prev =startPage ==1 ? false :true; next =endPage *perPageNum >=totalCount ? false :true; } /** 일반적인 페이징 처리 파라미터 출력 데이터 ex) /memberList?page=4&perPageNum=10 */ public String makeQuery(int page){ UriComponents uriComponents= UriComponentsBuilder.newInstance() .queryParam("page", page) .queryParam("perPageNum", perPageNum) .build(); return uriComponents.toUriString(); } /** 일반적인 페이징 부트스트랩 출력 */ public String bootStrapPagingHTML(String url){ StringBuffer sBuffer=new StringBuffer(); sBuffer.append("<ul class='pagination'>"); if(prev){ sBuffer.append("<li><a href='"+url+makeQuery(1)+"'>처음</a></li>"); } if(prev){ sBuffer.append("<li><a href='"+url+makeQuery(startPage-1)+"'>«</a></li>"); } String active=""; for(int i=startPage; i <=endPage; i++){ if(page==i){ active="class=active"; }else{ active=""; } sBuffer.append("<li " +active+" >"); sBuffer.append("<a href='"+url+makeQuery(i)+"'>"+i+"</a></li>"); sBuffer.append("</li>"); } if(next && endPage>0){ sBuffer.append("<li><a href='"+url+makeQuery(endPage+1)+"'>»</a></li>"); } if(next && endPage>0){ sBuffer.append("<li><a href='"+url+makeQuery(tempEndPage)+"'>마지막</a></li>"); } sBuffer.append("</ul>"); return sBuffer.toString(); } /** 검색 추가 페이지 파라미터 */ public String makeSearch(int page){ UriComponents uriComponents= UriComponentsBuilder.newInstance() .queryParam("page", page) .queryParam("perPageNum", perPageNum) .queryParam("searchType", searchType) .queryParam("keyword", keyword) .build(); return uriComponents.toUriString(); } /** 검색 추가 페이징 부트스트랩 출력 */ public String bootStrapPagingSearchHTML(String url){ StringBuffer sBuffer=new StringBuffer(); sBuffer.append("<ul class='pagination'>"); if(prev){ sBuffer.append("<li><a href='"+url+makeSearch(1)+"'>처음</a></li>"); } if(prev){ sBuffer.append("<li><a href='"+url+makeSearch(startPage-1)+"'>«</a></li>"); } String active=""; for(int i=startPage; i <=endPage; i++){ if(page==i){ active="class=active"; }else{ active=""; } sBuffer.append("<li " +active+" >"); sBuffer.append("<a href='"+url+makeSearch(i)+"'>"+i+"</a></li>"); sBuffer.append("</li>"); } if(next && endPage>0){ sBuffer.append("<li><a href='"+url+makeSearch(endPage+1)+"'>»</a></li>"); } if(next && endPage>0){ sBuffer.append("<li><a href='"+url+makeSearch(tempEndPage)+"'>마지막</a></li>"); } sBuffer.append("</ul>"); return sBuffer.toString(); } /** JSON 반환 처리를 위해 */ public MysqlPageMakerResponse toResPageMaker(MysqlPageMaker pageMaker) { return MysqlPageMakerResponse.builder() .page(pageMaker.getPage()) .perPageNum(pageMaker.getPerPageNum()) .pageStart(pageMaker.getPageStart()) .totalCount(pageMaker.getTotalCount()) .startPage(pageMaker.getStartPage()) .endPage(pageMaker.getEndPage()) .prev(pageMaker.isPrev()) .next(pageMaker.isNext()) .displayPageNum(pageMaker.getDisplayPageNum()) .tempEndPage(pageMaker.getTempEndPage()) .searchType(pageMaker.getSearchType()) .keyword(pageMaker.getKeyword()) .build(); } }
2)MysqlPageMakerResponse 추가
import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.ToString; /** 반환 처리시 */ @Setter @Getter @AllArgsConstructor @ToString @Builder public class MysqlPageMakerResponse { private int page; private int perPageNum; private int pageStart; private int totalCount; //전체 개수 private int startPage; // 시작 페이지 private int endPage; // 끝페이지 private boolean prev; // 이전 여부 private boolean next; // 다음 여부 private int displayPageNum; private int tempEndPage; //마지막 페이지 private String searchType; private String keyword; }
3) 컨트롤 사용예
/** 3. 게시판 페이징 검색처리 목록리턴 - 페이징 검색처리 두번째방법 (전체 갯수 구함) */ @GetMapping({"/pageSearchList2"}) @ApiOperation(value="목록조회 - MysqlPageMaker 사용 ", notes="3.게시판 페이징 검색처리 목록리턴 ") public BaseResponse<List<BoardDTO>> paginationSearchList2( @ApiParam MysqlPageMaker pageMaker ) throws Exception{ int totalCount =boardService.getTotalCount(pageMaker); pageMaker.setTotalCount(totalCount); log.info("1.pageMaker : {} " , pageMaker); log.info("2.totalCount : {}" , totalCount); return new BaseResponse<List<BoardDTO>>(boardService.paginationSearchList2(pageMaker), pageMaker); }
4) 반환처리 BaseResponse
import kr.so.songjava.utils.pagination2.MysqlPageMaker; import kr.so.songjava.utils.pagination2.MysqlPageMakerResponse; import lombok.Data; @Data public class BaseResponse<T> { private BaseResponseCode code; private String message; private MysqlPageMakerResponse pageMaker; private int displayPageNum; private T data; public BaseResponse(T data) { this.code=BaseResponseCode.SUCCESS; this.data=data; } public BaseResponse(String status, T data) { if(status.equals("error"))this.code=BaseResponseCode.ERROR; else this.code=BaseResponseCode.SUCCESS; this.data=data; } public BaseResponse(String status, T data, String message) { if(status.equals("error"))this.code=BaseResponseCode.ERROR; else this.code=BaseResponseCode.SUCCESS; this.data=data; this.message=message; } /** MysqlPageMaker 페이지 처리 메소드 */ public BaseResponse(T data, MysqlPageMaker mysqlPageMaker) { this.code=BaseResponseCode.SUCCESS; this.data=data; this.pageMaker=mysqlPageMaker.toResPageMaker(mysqlPageMaker); } }
5) BaseResponseCode
import lombok.NoArgsConstructor; @NoArgsConstructor public enum BaseResponseCode { SUCCESS(200), // 성공 ERROR(500), // 실패 LOGIN_REQUIRED, DATA_IS_NULL, VALIDATE_REQUIRED ; private int status; BaseResponseCode(int status) { this.status = status; } public int status() { return status; } }
Boad.xml
<sql id="searchSql"> <where> <if test="@org.apache.commons.lang3.StringUtils@isNotEmpty(keyword)"> AND B.TITLE LIKE CONCAT('%', #{keyword}, '%') </if> <if test="@org.apache.commons.lang3.ObjectUtils@isNotEmpty(searchType)"> <if test="searchType == 'NOTICE'.toString()"> AND B.BOARD_TYPE ='NOTICE' </if> <if test="searchType == 'FAQ'.toString()"> AND B.BOARD_TYPE ='FAQ' </if> <if test="searchType == 'INQUIRY'.toString()"> AND B.BOARD_TYPE ='INQUIRY' </if> <if test="searchType == 'NONE'.toString()"> AND B.BOARD_TYPE ='NONE' </if> </if> </where> </sql> <select id="getTotalCount" resultType="int"> SELECT COUNT(B.BOARD_SEQ) AS cnt FROM T_BOARD B <include refid="searchSql"></include> </select> <select id="paginationSearchList2" resultType="kr.net.macaronics.mvc.domain.entity.Board"> SELECT B.BOARD_SEQ, B.BOARD_TYPE, B.TITLE, (SELECT IF( B.DEL_YN ='Y', 'true', 'false') ) as DEL_YN , B.REG_DATE FROM T_BOARD B <include refid="searchSql"></include> ORDER BY B.REG_DATE DESC LIMIT #{pageStart} , #{perPageNum} </select>
http://localhost:8080/swagger-ui.html
출력 예
{ "code": "SUCCESS", "message": null, "pageMaker": { "page": 1, "perPageNum": 3, "pageStart": 0, "totalCount": 1001, "startPage": 1, "endPage": 6, "prev": false, "next": true, "displayPageNum": 6, "tempEndPage": 334, "searchType": null, "keyword": null }, "displayPageNum": 0, "data": [ { "boardSeq": 256, "boardType": { "code": "FAQ", "label": "자주묻는질문" }, "title": "wJmTCEydrm", "contents": null, "regDate": "2022-06-26T03:38:59.000+00:00" }, { "boardSeq": 512, "boardType": { "code": "FAQ", "label": "자주묻는질문" }, "title": "JQvHGGREkU", "contents": null, "regDate": "2022-06-26T03:38:59.000+00:00" }, { "boardSeq": 768, "boardType": { "code": "FAQ", "label": "자주묻는질문" }, "title": "fPwypLLrpP", "contents": null, "regDate": "2022-06-26T03:38:59.000+00:00" } ] }
소스
댓글 ( 4)
댓글 남기기