일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 스마일게이트
- Redis
- Inno DB
- 디자인 패턴
- spring security
- 서버
- component
- JPA 낙관적락
- JPA
- 캠프
- 안드로이드
- bean
- Pessimistic Lock
- Optimistic Lock
- 낙관적락 비관적락 차이
- Android
- Transaction isolation level
- spring
- JPA Lock
- 서버개발캠프
- 스프링 로그
- flask
- 스프링
- spring security 인증
- 개발
- JPA 동시성
- annotation
- 스프링 log
- 암호화
- JPA 비관적락
- Today
- Total
모르는게 많은 개발자
[SpringBoot] thymeleaf + Mybatis + PageHelper 페이징 간단예제 본문
토이 프로젝트를 진행하며 페이징 작업을 해야하는데 PageHelper + thymeleaf + Mybatis로 진행한 정보가 부족한것 같아 직접 포스팅하고자 글을 쓴다. 이 글에는 검색기능까지 포함된 리스트 페이징을 다뤄본다.
1. PageHelper 의존성 추가, application.yml 설정
일반적으로 페이징을 구현한다하면 여러가지 다소 귀찮은 작업들이 들어간다.(DB 데이터 개수, 현재 페이지, 이전 페이지, 다음 페이지 등등)
PageHelper는 이러한 귀찮은 작업들을 다 구현해놓은 Github에 올라와있는 오픈소스다
github.com/pagehelper/Mybatis-PageHelper
일단 프로젝트에 gradle에 pagehelper를 추가해주자
기본적으로 mybatis와 thymeleaf도 사용하기에 두개의 의존성도 같이 추가해준다.
다음은 아래와 같이 application.yml을 설정해준다.
mapper-locations은 resources/mapper 경로에 mapper.xml을 설정했을 때 기준이다.
2. PageHelper
PageHelper는 아래와 같이 paging적용에 필요한 값들을 알아서 만들어 객체화 해주고, 페이지에 따른 리스트 쿼리를 자동으로 생성해 각 페이지에 맞는 데이터를 list에 넣어준다.
3. 구현
DB Table
DB table은 user라는 이름의 table로 다음처럼 데이터가 들어있다.
Entity
name, email을 넣을 Entity 클래스
//PagingEntity.java
@Data
public class PagingEntity {
String email;
String name;
}
SearchDto
검색값 넣을 dto
//SearchDto.java
@Data
public class SearchDto {
private String keyword;
private String search;
}
Mapper.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="com.cjw.shorturl.paging.PagingRepository">
<select id="findUser" resultType="com.cjw.shorturl.paging.PagingEntity">
SELECT email, name
FROM user
WHERE 1=1
<if test="keyword == 'name'">
AND name LIKE CONCAT('%',#{search},'%')
</if>
<if test="keyword == 'email'">
AND email LIKE CONCAT('%',#{search},'%')
</if>
</select>
</mapper>
mybatis mapper작성한다. name과 email만 가져오고, 검색 기능때문에 동적 쿼리를 넣어 name, email검색을 할 수 있다.
Repository
Page<>는 Paging 관련 데이터와 리스트 데이터를 넣는 객체다.
//PagingRepository.java
@Repository
@Mapper
public interface PagingRepository {
Page<PagingEntity> findUser(SearchDto search);
}
Service
PageHelper.startPage(현재 페이지, 페이지에 출력될 데이터 개수)를 설정하면 mybatis에서 페이지에 맞는 데이터와 설정한 개수로 가져온다.
//PagingService.java
@Service
@RequiredArgsConstructor
public class PagingService {
private final PagingRepository repository;
public Page<PagingEntity> getUserList(int pageNo, SearchDto search) throws Exception {
PageHelper.startPage(pageNo, 10);
return repository.findUser(search);
}
}
Controller
이제 Service에서 가져온 데이터를 PageInfo를 통해 프론트에 출력될 데이터(페이지 번호 리스트 등)을 계산해준다.
new PageInfo<>(Page객체, 페이징 번호 개수);
//PagingController.java
@Controller
@RequiredArgsConstructor
@Slf4j
public class PagingController {
private final PagingService service;
@GetMapping("/page")
public String page(@ModelAttribute SearchDto search,
@RequestParam(required = false, defaultValue = "1") int pageNum, Model model) throws Exception {
PageInfo<PagingEntity> p = new PageInfo<>(service.getUserList(pageNum, search), 10);
model.addAttribute("users", p);
model.addAttribute("search", search);
return "/paging";
}
}
thymeleaf
다음 페이지를 갈 때 기존 검색어를 유지하기 위해 href에 검색데이터를 controller로 계속 보내준다.
<!DOCTYPE html>
<html lang="en"
xmlns:th="http://www.thymeleaf.org">
<body>
<!-- 검색 -->
<form action="" method="get">
<div>
<select name="keyword" id="keyword">
<option value="name">이름</option>
<option value="email">이메일</option>
</select>
<input type="text" id="search" name="search">
<button type="submit">검색</button>
</div>
</form>
<!-- 리스트 테이블 -->
<table>
<thead>
<tr>
<th scope="col">이름</th>
<th scope="col">이메일</th>
</tr>
</thead>
<tbody>
<tr th:each="user : ${users.getList()}">
<th th:text="${user.getName()}"></th>
<td th:text="${user.getEmail()}"></td>
</tr>
</tbody>
</table>
<!-- 페이징 -->
<nav>
<ul>
<!-- getPrePage : 이전 페이지 -->
<li th:classappend="${users.getPrePage() == 0} ? 'disabled'">
<!-- href: /page?pageNum=이전페이지&keyword=검색분류&search=검색어 -->
<a th:href="@{/page(pageNum=${users.getPrePage()}, keyword=${search.getKeyword()}, search=${search.getSearch()})}">Previous</a>
</li>
<!-- getNavigateFirstPage() : 페이징의 처음 숫자 getNavigateLastPage : 페이징의 마지막 숫자를 이용해 페이징 숫자 출력 -->
<!-- href : /page?pageNum=선택한 페이지&keyword=검색분류&search=검색어 -->
<li th:each="page: ${#numbers.sequence(users.getNavigateFirstPage(), users.getNavigateLastPage())}" th:classappend="${page == users.getPageNum()} ? 'active'">
<a th:text="${page}" th:href="@{/page(pageNum=${page}, keyword=${search.getKeyword()}, search=${search.getSearch()})}"></a>
</li>
<!-- getPrePage : 다음 페이지 -->
<li th:classappend="${users.getNextPage() == 0} ? 'disabled'">
<!-- href: /page?pageNum=다음페이지&keyword=검색분류&search=검색어 -->
<a th:href="@{/page(pageNum=${users.getNextPage()}, keyword=${search.getKeyword()}, search=${search.getSearch()})}">Next</a>
</li>
</ul>
</nav>
</body>
</html>
4. 결과
화면 결과를 살펴보면 아래처럼 검색창, 리스트, 페이징으로 나타난다. 예제 알아보기 쉽게 디자인은 고려하지 않았다.
'스프링' 카테고리의 다른 글
[JPA] 2. 엔티티 매핑 @어노테이션 정리/예제 (1) | 2021.02.16 |
---|---|
[스프링] Spring Security 인증은 어떻게 이루어질까? (5) | 2021.01.21 |
[SpringBoot] spring-security 회원가입/로그인/권한 예제 (2) | 2020.10.02 |
[JPA] 1. 영속성 컨텍스트(Persistence Context) 정리 (0) | 2020.08.17 |
[Spring] Boot와JPA(Mysql)을 이용한 Rest API 간단 예제 (0) | 2020.05.07 |