목차 | |
1. | paging |
2. | comment |
1. paging
◈ list.html
<!DOCTYPE html>
<html lang="en"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/layout}">
<div layout:fragment="content">
<div class="container-md">
<h1>Board List Page</h1>
<table class="table table-hover">
<thead>
<tr>
<th>#</th>
<th>title</th>
<th>writer</th>
<th>regDate</th>
</tr>
</thead>
<tbody>
<tr th:each="bvo:${list}">
<td>[[${bvo.bno}]]</td>
<td><a th:href="@{/board/detail(bno=${bvo.bno})}">[[${bvo.title}]]</a></td>
<td>[[${bvo.writer}]]</td>
<td>[[${bvo.regAt}]]</td>
</tr>
</tbody>
</table>
<!-- 페이지네이션 라인 -->
<!-- th:classappend : 동적 클래스 추가 -->
<nav aria-label="Page navigation example">
<ul class="pagination justify-content-center">
<li th:if="${pgvo.hasPrev}" class="page-item">
<a class="page-link" th:href="@{/board/list(pageNo = ${pgvo.startPage - 1})}" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<th:block th:each="i : ${#numbers.sequence(pgvo.startPage, pgvo.endPage)}">
<li class="page-item" aria-current="page" th:classappend="${pgvo.pageNo eq i ? 'active' : ''}">
<a class="page-link" th:href="@{/board/list(pageNo = ${i})}">[[${i}]]</a>
</li>
</th:block>
<li th:if="${pgvo.hasNext}" class="page-item">
<a class="page-link" th:href="@{/board/list(pageNo = ${pgvo.endPage + 1})}" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
</div>
</div>
◈ PagingVO.java
package com.ezen.boot_JPA.dto;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.data.domain.Page;
@Getter
@Setter
@ToString
public class PagingVO {
private int totalPage;
private int startPage;
private int endPage;
private boolean hasPrev, hasNext;
private int pageNo;
public PagingVO(Page<BoardDTO> list, int pageNo){
this.pageNo = pageNo + 1;
this.totalPage = list.getTotalPages();
this.endPage = (int)Math.ceil(this.pageNo / 10.0) * 10;
this.startPage = endPage - 9;
if(endPage > totalPage){
endPage = totalPage;
}
this.hasPrev = this.startPage > 10;
this.hasNext = this.endPage < this.totalPage;
}
}
◈ BoardController.java
package com.ezen.boot_JPA.controller;
import com.ezen.boot_JPA.dto.BoardDTO;
import com.ezen.boot_JPA.dto.PagingVO;
import com.ezen.boot_JPA.service.BoardService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import java.util.List;
@Slf4j
@RequestMapping("/board/*")
@RequiredArgsConstructor
@Controller
public class BoardController {
private final BoardService boardService;
@GetMapping("/register")
public void register(){}
@PostMapping("/register")
public String register(BoardDTO boardDTO){
log.info(">>> boardDTO >>> {}", boardDTO);
// insert, update, delete => return 1 row
// jpa insert, update, delete => return id
Long bno = boardService.insert(boardDTO);
log.info(">>> insert >>> {}", bno > 0 ? "OK" : "FAIL");
return "/index";
}
/*@GetMapping("/list")
public void list(Model model){
// paging이 없는 케이스
List<BoardDTO> list = boardService.getList();
model.addAttribute("list", list);
}*/
@GetMapping("/list")
public void list(Model model, @RequestParam(value = "pageNo", defaultValue = "0", required = false) int pageNo){
// 화면에서 들어오는 pageNo = 1 / 0으로 처리가 되어야 함
// 화면에서 들어오는 pageNo = 2 / 1로 처리가 되어야 함
log.info(">>> pageNo >>> {}", pageNo);
pageNo = (pageNo == 0 ? 0 : pageNo - 1);
log.info(">>> pageNo >>> {}", pageNo);
Page<BoardDTO> list = boardService.getList(pageNo);
log.info(">>> list >>> {}", list.toString());
log.info(">>> totalCount >>> {}", list.getTotalElements()); // 전체 글 수
log.info(">>> totalPage >>> {}", list.getTotalPages()); // 전체 페이지 수 => realEndPage
log.info(">>> pageNumber >>> {}", list.getNumber()); // 전체 페이지 번호 => pageNo
log.info(">>> pageSize >>> {}", list.getSize()); // 한 페이지에 표시되는 길이 => qty
log.info(">>> next >>> {}", list.hasNext()); // next 여부
log.info(">>> prev >>> {}", list.hasPrevious()); // prev 여부
PagingVO pgvo = new PagingVO(list, pageNo);
log.info(">>> pgvo >>> {}", pgvo.toString());
model.addAttribute("list", list);
model.addAttribute("pgvo", pgvo);
}
@GetMapping("/detail")
public void modify(Model model, @RequestParam("bno") Long bno){
BoardDTO boardDTO = boardService.getDetail(bno);
model.addAttribute("boardDTO", boardDTO);
}
@PostMapping("/modify")
public String modify(BoardDTO boardDTO, RedirectAttributes redirectAttributes){
Long bno = boardService.modify(boardDTO);
redirectAttributes.addAttribute("bno", boardDTO.getBno());
return "redirect:/board/detail";
}
@GetMapping("/delete")
public String delete(@RequestParam("bno") Long bno){
boardService.delete(bno);
return "redirect:/board/list";
}
}
◈ BoardService.java
package com.ezen.boot_JPA.service;
import com.ezen.boot_JPA.dto.BoardDTO;
import com.ezen.boot_JPA.entity.Board;
import org.springframework.data.domain.Page;
import java.util.List;
public interface BoardService {
// 추상 메서드만 가능한 인터페이스
// 메서드가 default(접근제한자) 구현 가능
Long insert(BoardDTO boardDTO);
// BoardDTO(class): bno title writer content regAt modAt
// Board(table) : bno title writer content
// BoardDTO => board 변환
// 화면에서 가져온 BoardDTO 객체를 저장을 위한 Board 객체로 변환
default Board convertDtoToEntity(BoardDTO boardDTO){
return Board.builder()
.bno(boardDTO.getBno())
.title(boardDTO.getTitle())
.writer(boardDTO.getWriter())
.content(boardDTO.getContent())
.build();
}
// board => BoardDTO 변환
// DB에서 가져온 Board 객체를 화면에 뿌리기 위한 BoardDTO 객체로 변환
default BoardDTO convertEntityToDto(Board board){
return BoardDTO.builder()
.bno(board.getBno())
.title(board.getTitle())
.writer(board.getWriter())
.content(board.getContent())
.regAt(board.getRegAt())
.modAt(board.getModAt())
.build();
}
// List<BoardDTO> getList();
Page<BoardDTO> getList(int pageNo);
BoardDTO getDetail(Long bno);
Long modify(BoardDTO boardDTO);
void delete(Long bno);
}
◈ BoardServiceImpl.java
package com.ezen.boot_JPA.service;
import com.ezen.boot_JPA.dto.BoardDTO;
import com.ezen.boot_JPA.entity.Board;
import com.ezen.boot_JPA.repository.BoardRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@Slf4j
@RequiredArgsConstructor
@Service
public class BoardServiceImpl implements BoardService{
private final BoardRepository boardRepository;
@Override
public Long insert(BoardDTO boardDTO) {
// 저장 객체는 Board
// save() : insert 후 저장 객체의 id를 리턴
// save() Entity 객체를 파라미터로 전송
return boardRepository.save(convertDtoToEntity(boardDTO)).getBno();
}
// @Override
// public List<BoardDTO> getList() {
// // 컨트롤러로 보내야 하는 리턴은 List<BoardDTO>
// // DB에서 가져오는 리턴은 List<Board> > BoardDTO 객체로 변환
// // findAll()
// // 정렬 : Sort.by(Sort.Direction.DESC, "정렬기준 칼럼명")
// List<Board> boardList = boardRepository.findAll(Sort.by(Sort.Direction.DESC, "bno"));
// /* List<BoardDTO> boardDTOList = new ArrayList<>();
// for(Board board : boardList){
// boardDTOList.add(convertEntityToDto(board));
// }*/
// List<BoardDTO> boardDTOList = boardList.stream()
// .map(b -> convertEntityToDto(b)).toList();
// return boardDTOList;
// }
@Override
public Page<BoardDTO> getList(int pageNo) {
// pageNo = 0부터 시작
// 0 => limit 0, 10 / 1 => limit 10, 10
Pageable pageable = PageRequest.of(pageNo, 10,
Sort.by("bno").descending());
Page<Board> list = boardRepository.findAll(pageable);
Page<BoardDTO> boardDTOList = list.map(b -> convertEntityToDto(b));
return boardDTOList;
}
@Override
public BoardDTO getDetail(Long bno) {
/* findById : 아이디(PK)를 주고 해당 객체를 리턴
findById의 리턴타입 Optional<Board> 타입으로 리턴
Optional<T> : nullPointException이 발생하지 않도록 도와줌.
Optional.isEmpty() : null일 경우 확인 가능 true / false
Optional.isPresent() : 값이 있는지 확인 true / false
Optional.get() : 객체 가져오기
* */
Optional<Board> optional = boardRepository.findById(bno);
if(optional.isPresent()){
BoardDTO boardDTO = convertEntityToDto(optional.get());
return boardDTO;
}
return null;
}
@Override
public Long modify(BoardDTO boardDTO) {
// update : jpa는 업데이트가 없음.
// 기존 객체를 가져와서 set 수정 후 다시 저장
Optional<Board> optional = boardRepository.findById(boardDTO.getBno());
if(optional.isPresent()){
Board entity = optional.get();
entity.setTitle(boardDTO.getTitle());
entity.setContent(boardDTO.getContent());
// 다시 저장 (기존 객체에 덮어쓰기)
return boardRepository.save(entity).getBno();
}
return null;
}
// 삭제 : deleteById(id)
@Override
public void delete(Long bno) {
boardRepository.deleteById(bno);
}
}
▷ 출력
2. comment
◈ detail.html
<!DOCTYPE html>
<html lang="en"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/layout}">
<div layout:fragment="content">
<div class="container-md">
<h1>Board Detail Page [[boardDTO.bno]]</h1>
<form action="/board/modify" method="post" id="modForm">
<div class="mb-3">
<label for="r" class="form-label">Created At</label>
<input type="text" class="form-control" name="regDate" id="r" th:value="${boardDTO.regAt}" readonly>
</div>
<div class="mb-3">
<input type="hidden" class="form-control" name="bno" id="n" th:value="${boardDTO.bno}">
</div>
<div class="mb-3">
<label for="title" class="form-label">Title</label>
<input type="text" class="form-control" name="title" id="title" th:value="${boardDTO.title}" readonly>
</div>
<div class="mb-3">
<label for="writer" class="form-label">Writer</label>
<input type="text" class="form-control" name="writer" id="writer" th:value="${boardDTO.writer}" readonly>
</div>
<div class="mb-3">
<label for="content" class="form-label">Content</label>
<textarea class="form-control" name="content" id="content" rows="3" readonly>[[${boardDTO.content}]]</textarea>
</div>
<button type="button" id="listBtn" class="btn btn-primary">List</button>
<button type="button" id="modBtn" class="btn btn-warning">Modify</button>
<a th:href="@{/board/delete(bno=${boardDTO.bno})}">
<button type="button" id="delBtn" class="btn btn-danger">Delete</button>
</a>
</form>
<!-- comment line -->
<!-- post -->
<div class="input-group mb-3" >
<span class="input-group-text" id="cmtWriter"></span>
<input type="text" id="cmtText" class="form-control" placeholder="Add Comment..." aria-label="Username" aria-describedby="basic-addon1">
<button type="button" id="cmtAddBtn" class="btn btn-secondary">post</button>
</div>
<!-- spread -->
<ul class="list-group list-group-flush" id="cmtListArea">
<li class="list-group-item">
<div class="ms-2 me-auto">
<div class="fw-bold">writer</div>
Content
</div>
<span class="badge text-bg-primary rounded-pill">regDate</span>
</li>
</ul>
<!-- moreBtn-->
<div>
<button type="button" id="moreBtn" data-page="1" class="btn btn-dark" style="visibility:hidden">MORE + </button>
</div>
<!-- modal -->
<div class="modal fade" id="myModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="cmtWriterMod">${authNick }</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<input type="text" class="form-control" id="cmtTextMod">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" id="cmtModBtn" class="btn btn-primary">changes</button>
</div>
</div>
</div>
</div>
<script th:inline="javascript">
let bnoVal = [[${boardDTO.bno}]]
console.log(bnoVal);
</script>
</div>
<script th:src="@{/js/boardDetail.js}"></script>
<script th:src="@{/js/boardComment.js}"></script>
<script>
spreadCommentList(bnoVal);
</script>
</div>
◈ boardComment.js
console.log("boardComment.js in");
console.log(bnoVal);
document.getElementById('cmtAddBtn').addEventListener('click', ()=>{
const cmtText = document.getElementById('cmtText');
if(cmtText == null || cmtText == ''){
alert('댓글 내용을 입력해주세요')
cmtText.focus();
return false;
}
const cmtData = {
bno : bnoVal,
writer : document.getElementById('cmtWriter').innerText,
content : cmtText.value
};
postCommentToServer(cmtData).then(result => {
if(result > 0){
alert("댓글 등록 성공");
cmtText.value = "";
}
// 댓글 뿌리기 호출
spreadCommentList(bnoVal);
})
});
async function postCommentToServer(cmtData) {
try {
const url = "/comment/post";
const config = {
method: "post",
headers:{
'Content-Type':'application/json; charset=utf-8'
},
body: JSON.stringify(cmtData)
};
const resp = await fetch(url, config);
const result = await resp.text();
return result;
} catch (error) {
console.log(error);
}
}
function spreadCommentList(bno, page=1){
getCommentListFromServer(bno, page).then(result =>{
console.log(result);
const ul = document.getElementById('cmtListArea');
if(result.content.length > 0){
if(page == 1){
ul.innerHTML=""; // 기존 샘플값 비우기.
}
for(let cvo of result.content){
console.log(cvo);
let li = `<li data-cno = "${cvo.cno}" class="list-group-item">`;
li += `<div class="ms-2 me-auto">`;
li += `<div class="fw-bold">Anomyous</div>`;
li += `${cvo.content}`;
li += `</div>`;
li += `<span class="badge text-bg-primary rounded-pill">${cvo.regDate}</span>`;
// if(nickName == cvo.writer){
li += `<button type="button" class="btn btn-outline-warning btn-sm mod" data-bs-toggle="modal" data-bs-target="#myModal">%</button>`;
li += `<button type="button" class="btn btn-outline-danger btn-sm del">X</button>`;
// }
li += `</li>`;
ul.innerHTML += li;
}
// page 처리
let moreBtn= document.getElementById('moreBtn');
// 전체 페이지가 현재 페이지보다 크다면... (나와야 할 페이지가 존재한다면...)
if(page < result.totalPages){
moreBtn.style.visibility = "visible"; // 표시
moreBtn.dataset.page = page + 1;
}else{
moreBtn.style.visibility = "hidden"; // 숨김
}
}else{
let li = `<li class="list-group-item">Comment List Empty</li>`;
ul.innerHTML = li;
}
});
};
async function getCommentListFromServer(bno, page) {
try{
const resp = await fetch("/comment/list/"+bno+"/"+page);
const result = await resp.json();
return result;
} catch(error){
console.log(error);
}
}
document.addEventListener('click',(e)=> {
if(e.target.id == 'moreBtn'){
let page = parseInt(e.target.dataset.page);
spreadCommentList(bnoVal, page);
}
if(e.target.classList.contains('del')){
let cno = e.target.closest('li').dataset.cno;
removeCommentToServer(cno).then(result => {
if(result > 0){
alert("삭제 성공!!");
spreadCommentList(bnoVal);
}
})
}
if(e.target.classList.contains('mod')){
let li = e.target.closest('li');
let modWriter = li.querySelector(".fw-bold").innerText;
document.getElementById('cmtWriterMod').innerText = modWriter;
let cmtText =li.querySelector('.fw-bold').nextSibling; // 쿼리 값의 같은 부모의 다른 형제 값
document.getElementById('cmtTextMod').value = cmtText.nodeValue; // 그냥 넣으면 Object로 입력
// 수정 버튼 id = cmtModBtn dataset
document.getElementById('cmtModBtn').setAttribute("data-cno", li.dataset.cno);
}
if(e.target.id == 'cmtModBtn'){
let cmtModData={
cno : e.target.dataset.cno,
content : document.getElementById('cmtTextMod').value
}
console.log(cmtModData);
// 비동기 처리
updateCommentToServer(cmtModData).then(result =>{
if(result > 0){
alert("수정 성공!!");
}
// 모달창 닫기
document.querySelector('.btn-close').click();
spreadCommentList(bnoVal);
});
}
});
async function updateCommentToServer(cmtModData) {
try{
const url = "/comment/update"
const config ={
method : "put",
headers : {
'content-type':'application/json; charset=utf-8'
},
body : JSON.stringify(cmtModData)
}
const resp = await fetch(url, config);
const result = await resp.text();
return result;
} catch(error){
console.log(error);
}
}
async function removeCommentToServer(cno) {
try{
const url = "/comment/remove/"+cno;
const config={
method:'get'
}
const resp = await fetch(url, config);
const result = await resp.text();
return result;
} catch(error){
console.log(error);
}
}
◈ CommentDTO.java
package com.ezen.boot_JPA.dto;
import lombok.*;
import java.time.LocalDateTime;
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class CommentDTO {
private long cno;
private long bno;
private String writer;
private String content;
private LocalDateTime regAt;
private LocalDateTime modAt;
}
◈ Comment.java
package com.ezen.boot_JPA.entity;
import jakarta.persistence.*;
import lombok.*;
@Entity
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Comment extends TimeBase{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long cno;
@Column(nullable = false)
private long bno;
@Column(length = 200, nullable = false)
private String writer;
@Column(length = 1000, nullable = false)
private String content;
}
◈ CommentController.java
package com.ezen.boot_JPA.controller;
import com.ezen.boot_JPA.dto.CommentDTO;
import com.ezen.boot_JPA.service.CommentService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/comment/*")
@RequiredArgsConstructor
@Slf4j
public class CommentController {
private final CommentService commentService;
@PostMapping("/post")
@ResponseBody
public String post(@RequestBody CommentDTO commentDTO){
long cno = commentService.post(commentDTO);
return cno > 0 ? "1" : "0";
}
@GetMapping("/list/{bno}/{page}")
@ResponseBody
public Page<CommentDTO> list(@PathVariable("bno") long bno, @PathVariable("page") int page){
// page = 0부터
// 1 page => 0 / 2 page => 1
page = (page == 0 ? 0 : page - 1);
// List<CommentDTO> list = commentService.getList(bno);
Page<CommentDTO> list = commentService.getList(bno, page);
log.info(">>> list >>> {}", list);
return list;
}
@PutMapping("/update")
@ResponseBody
public String update(@RequestBody CommentDTO commentDTO){
long cno = commentService.update(commentDTO);
return cno > 0 ? "1" : "0";
}
@GetMapping("/remove/{cno}")
public long remove(@PathVariable("cno") long cno){
long isOk = commentService.remove(cno);
return isOk > 0 ? 1 : 0;
}
}
◈ CommentService.java
package com.ezen.boot_JPA.service;
import com.ezen.boot_JPA.dto.CommentDTO;
import com.ezen.boot_JPA.entity.Comment;
import org.springframework.data.domain.Page;
import java.util.List;
public interface CommentService {
long post(CommentDTO commentDTO);
// convert 작업
// DTO => Entity
default Comment convertDtoToEntity(CommentDTO commentDTO){
return Comment.builder()
.cno(commentDTO.getCno())
.bno(commentDTO.getBno())
.writer(commentDTO.getWriter())
.content(commentDTO.getContent())
.build();
}
default CommentDTO convertEntityToDto(Comment comment) {
return CommentDTO.builder()
.cno(comment.getCno())
.bno(comment.getBno())
.writer(comment.getWriter())
.content(comment.getContent())
.build();
}
// List<CommentDTO> getList(long bno);
Page<CommentDTO> getList(long bno, int page);
long remove(long cno);
long update(CommentDTO commentDTO);
}
◈ CommentServiceImpl.java
package com.ezen.boot_JPA.service;
import com.ezen.boot_JPA.dto.CommentDTO;
import com.ezen.boot_JPA.entity.Comment;
import com.ezen.boot_JPA.repository.CommentRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
@RequiredArgsConstructor
@Slf4j
public class CommentServiceImpl implements CommentService{
private final CommentRepository commentRepository;
@Override
public long post(CommentDTO commentDTO) {
return commentRepository.save(convertDtoToEntity(commentDTO)).getBno();
}
// @Override
// public List<CommentDTO> getList(long bno) {
// // select * from comment where bno = ${bno}
// List<Comment> list = commentRepository.findByBno(bno);
// List<CommentDTO> dtoList = list.stream().map(c -> convertEntityToDto(c)).toList();
// return dtoList;
// }
@Override
public Page<CommentDTO> getList(long bno, int page) {
Pageable pageable = PageRequest.of(page, 5, Sort.by("cno").descending());
Page<Comment> list = commentRepository.findByBno(bno, pageable);
Page<CommentDTO> dtoPageList = list.map(c -> convertEntityToDto(c));
return dtoPageList;
}
@Override
public long update(CommentDTO commentDTO) {
Optional<Comment> optionalComment = commentRepository.findById(commentDTO.getCno());
if(optionalComment.isPresent()){
Comment comment = optionalComment.get();
comment.setContent(commentDTO.getContent());
return commentRepository.save(comment).getCno();
}
return 0;
}
@Override
public long remove(long cno) {
Optional<Comment> comment = commentRepository.findById(cno);
if(comment.isPresent()) {
commentRepository.deleteById(cno);
return 1;
}else {
return 0;
}
}
}
◈ CommentRepository.java
package com.ezen.boot_JPA.repository;
import com.ezen.boot_JPA.entity.Comment;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
/* JpaRepository<entity name, id class> */
public interface CommentRepository extends JpaRepository<Comment, Long> {
// List<Comment> findByBno(long bno);
Page<Comment> findByBno(long bno, Pageable pageable);
}
▷ 출력
'Java > JPA' 카테고리의 다른 글
JPA 기초(search) - AWS 풀스택 과정 86일차 (0) | 2024.11.27 |
---|---|
JPA 기초(security) - AWS 풀스택 과정 85일차 (0) | 2024.11.26 |
JPA 기초(file) - AWS 풀스택 과정 84일차 (0) | 2024.11.25 |
JPA 설정 및 기초 - AWS 풀스택 과정 82일차 (0) | 2024.11.21 |