[Spring Boot] 11. Spring Boot Project (Blog v2 - jpa)_14.Board - Search

김미숙's avatar
Jul 22, 2025
[Spring Boot] 11. Spring Boot Project (Blog v2 - jpa)_14.Board - Search
repository 내의 함수는 많아도 되나, controller, service 내의 함수들은 1대1매칭이어야함
 
BoardController
  • @RequestParam(required = false, value = "keyword") String keyword
package shop.mtcoding.blog.board; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpSession; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.validation.Errors; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import shop.mtcoding.blog.user.User; @RequiredArgsConstructor @Controller public class BoardController { private final BoardService boardService; private final HttpSession session; @GetMapping("/") public String list(HttpServletRequest request, @RequestParam(required = false, value = "page", defaultValue = "0") Integer page, @RequestParam(required = false, value = "keyword") String keyword) { System.out.println("keyword: " + keyword); User sessionUser = (User) session.getAttribute("sessionUser"); if (sessionUser == null) { request.setAttribute("model", boardService.글목록보기(null, page, keyword)); // 로그인 안 했을 때 } else { request.setAttribute("model", boardService.글목록보기(sessionUser.getId(), page, keyword)); // 로그인 했을 때 } return "board/list"; } @PostMapping("/board/save") public String save(@Valid BoardRequest.SaveDTO saveDTO, Errors errors) { User sessionUser = (User) session.getAttribute("sessionUser"); boardService.글쓰기(saveDTO, sessionUser); return "redirect:/"; } @GetMapping("/board/save-form") public String saveForm() { return "board/save-form"; } @GetMapping("/board/{id}") public String detail(@PathVariable("id") Integer id, HttpServletRequest request) { User sessionUser = (User) session.getAttribute("sessionUser"); // 비로그인 시 상세보기 Integer sessionUserId = (sessionUser == null ? null : sessionUser.getId()); BoardResponse.DetailDTO detailDTO = boardService.글상세보기(id, sessionUserId); request.setAttribute("model", detailDTO); return "board/detail"; } @GetMapping("/board/{id}/update-form") public String updateForm(@PathVariable("id") Integer id, HttpServletRequest request) { User sessionUser = (User) session.getAttribute("sessionUser"); Board board = boardService.업데이트글보기(id, sessionUser.getId()); request.setAttribute("model", board); return "board/update-form"; } @PostMapping("/board/{id}/update") public String update(@PathVariable("id") Integer id, @Valid BoardRequest.UpdateDTO reqDTO, Errors errors) { User sessionUser = (User) session.getAttribute("sessionUser"); boardService.게시글수정(reqDTO, id, sessionUser.getId()); return "redirect:/board/" + id; } @PostMapping("/board/{id}/delete") public String delete(@PathVariable("id") Integer id) { User sessionUser = (User) session.getAttribute("sessionUser"); boardService.게시글삭제(id, sessionUser.getId()); return "redirect:/"; } }
 
BoardService
package shop.mtcoding.blog.board; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import shop.mtcoding.blog._core.error.ex.Exception403; import shop.mtcoding.blog._core.error.ex.Exception404; import shop.mtcoding.blog.love.Love; import shop.mtcoding.blog.love.LoveRepository; import shop.mtcoding.blog.reply.ReplyRepository; import shop.mtcoding.blog.user.User; import java.util.List; @RequiredArgsConstructor @Service public class BoardService { private final BoardRepository boardRepository; private final LoveRepository loveRepository; private final ReplyRepository replyRepository; @Transactional public void 글쓰기(BoardRequest.SaveDTO saveDTO, User sessionUser) { Board board = saveDTO.toEntity(sessionUser); boardRepository.save(board); } public BoardResponse.DTO 글목록보기(Integer userId, Integer page, String keyword) { if (userId == null) { Long totalCount = boardRepository.totalCount(); List<Board> boards = boardRepository.findAll(page, keyword); return new BoardResponse.DTO(boards, page, totalCount.intValue()); // 로그인 안 했을때 } else { Long totalCount = boardRepository.totalCount(userId); List<Board> boards = boardRepository.findAll(userId, page, keyword); return new BoardResponse.DTO(boards, page, totalCount.intValue()); // 로그인 했을 때 } } public BoardResponse.DetailDTO 글상세보기(Integer id, Integer userId) { Board board = boardRepository.findByIdJoinUserAndReplies(id); Love love = loveRepository.findByUserIdAndBoardId(userId, id); Boolean isLove = love == null ? false : true; Integer loveId = love == null ? null : love.getId(); Long loveCount = loveRepository.findByBoardId(board.getId()); BoardResponse.DetailDTO detailDTO = new BoardResponse.DetailDTO(board, userId, isLove, loveCount.intValue(), loveId); return detailDTO; } public Board 업데이트글보기(Integer id, Integer sessionUserId) { Board boardPs = boardRepository.findById(id); if (boardPs == null) throw new Exception404("게시글을 찾을 수 없습니다."); if (!boardPs.getUser().getId().equals(sessionUserId)) throw new Exception403("권한이 없습니다."); return boardPs; } @Transactional public Board 게시글수정(BoardRequest.UpdateDTO reqDTO, Integer id, Integer sessionUserId) { Board board = boardRepository.findById(id); if (board == null) throw new Exception404("게시글을 찾을 수 없습니다"); if (!board.getUser().getId().equals(sessionUserId)) throw new Exception403("권한이 없습니다."); board.update(reqDTO.getTitle(), reqDTO.getContent(), reqDTO.getIsPublic()); return board; } @Transactional public void 게시글삭제(Integer id, Integer sessionUserId) { // 게시글 존재 확인 Board boardPs = boardRepository.findById(id); if (boardPs == null) throw new Exception404("게시글을 찾을 수 없습니다"); if (!boardPs.getUser().getId().equals(sessionUserId)) throw new Exception403("권한이 없습니다."); // 좋아요 삭제 loveRepository.deleteByBoardId(boardPs.getId()); boardRepository.deleteById(id); } }
 
BoardRepository
→ 오버로딩 해도 되고, 동적쿼리로 바꿔도 됨
→ 검색: where절에 걸린다
package shop.mtcoding.blog.board; import jakarta.persistence.EntityManager; import jakarta.persistence.Query; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; import java.util.List; @RequiredArgsConstructor @Repository public class BoardRepository { private final EntityManager em; public void save(Board board) { em.persist(board); } // 그룹함수 -> Long으로 return [test해서 무슨 타입으로 들어오는지 확인 필요] // 1. 로그인 안 했을 때 -> 4개 // 3. 로그인 했을 때 -> ssar이 아니면 -> 4개 public Long totalCount() { Query query = em.createQuery("select count(b) from Board b where b.isPublic = true", Long.class); // 스칼라 data return (Long) query.getSingleResult(); } // 2. 로그인 했을 때 -> ssar -> 5개 public Long totalCount(int userId) { Query query = em.createQuery("select count(b) from Board b where b.isPublic = true or b.user.id = :userId", Long.class); // 스칼라 data query.setParameter("userId", userId); return (Long) query.getSingleResult(); } // locahost:8080?page=0 -> 로그인 X public List<Board> findAll(int page, String keyword) { String sql; // 동적쿼리 if (keyword == null) { sql = "select b from Board b where b.isPublic = true order by b.id desc"; } else { sql = "select b from Board b where b.isPublic = true and b.title like :keyword order by b.id desc"; } Query query = em.createQuery(sql, Board.class); if (keyword != null) { query.setParameter("keyword", "%" + keyword + "%"); } query.setFirstResult(page * 3); query.setMaxResults(3); return query.getResultList(); } // 로그인 O public List<Board> findAll(Integer userId, int page, String keyword) { String sql; // 동적쿼리 if (keyword == null) { sql = "select b from Board b where b.isPublic = true or b.user.id = :userId order by b.id desc"; } else { sql = "select b from Board b where b.isPublic = true or b.user.id = :userId and b.title like :keyword order by b.id desc"; } Query query = em.createQuery(sql, Board.class); query.setParameter("userId", userId); if (keyword != null) { query.setParameter("keyword", "%" + keyword + "%"); } query.setFirstResult(page * 3); query.setMaxResults(3); return query.getResultList(); } // public List<Board> findAll(Integer userId) { // Integer를 써야 null을 넘길 수 있다 // // 동적 query // String s1 = "select b from Board b where b.isPublic = true or b.user.id = :userId order by b.id desc"; // String s2 = "select b from Board b where b.isPublic = true order by b.id desc"; // // Query query = null; // if (userId == null) { // query = em.createQuery(s2, Board.class); // } else { // query = em.createQuery(s1, Board.class); // query.setParameter("userId", userId); // } // // return query.getResultList(); // } public Board findById(Integer id) { return em.find(Board.class, id); } public Board findByIdJoinUser(Integer id) { // b -> board에 있는 필드만 프로잭션 / fetch를 써야 board안에 있는 user 객체도 같이 프로잭션됨 Query query = em.createQuery("select b from Board b join fetch b.user u where b.id = :id", Board.class); // inner join (on절은 생략가능하다) -> 객체지향 쿼리 query.setParameter("id", id); return (Board) query.getSingleResult(); } public Board findByIdJoinUserAndReplies(Integer id) { Query query = em.createQuery("select b from Board b join fetch b.user u left join fetch b.replies r left join fetch r.user where b.id = :id order by r.id desc", Board.class); // left join (on절은 생략가능하다) -> 객체지향 쿼리 query.setParameter("id", id); return (Board) query.getSingleResult(); } public void deleteById(Integer id) { Board board = em.find(Board.class, id); // 영속성 컨텍스트에 넣기 em.remove(board); // 연관된 Reply들도 함께 삭제됨 (Cascade 동작) } }
 
list
{{> layout/header}} <div class="container p-5"> <div class="mb-3 d-flex justify-content-end"> //queryString <form action="/" class="d-flex" method="get"> <input class="form-control me-2" type="text" placeholder="검색..." name="keyword"> <button type="submit" class="btn btn-primary">Search</button> </form> </div> {{#model.boards}} <div class="card mb-3"> <div class="card-body"> <div class="d-flex justify-content-between"> <h4 class="card-title mb-3">{{title}}</h4> {{^isPublic}} <div class="d-flex align-items-start"> <span class="badge bg-secondary">비공개</span> </div> {{/isPublic}} </div> <a href="/board/{{id}}" class="btn btn-primary">상세보기</a> </div> </div> {{/model.boards}} <ul class="pagination d-flex justify-content-center"> <li class="page-item {{#model.isFirst}}disabled{{/model.isFirst}}"><a class="page-link" href="?page={{model.prev}} ">Previous</a></li> {{#model.numbers}} <li class="page-item"><a class="page-link" href="?page={{.}}">{{.}}</a></li> {{/model.numbers}} <li class="page-item {{#model.isLast}}disabled{{/model.isLast}}"><a class="page-link" href="?page={{model.next}} ">Next</a></li> </ul> </div> {{> layout/footer}}
 
notion image
notion image
검색하면 잘 되나 페이지 버튼 눌렀을때 쿼리스트링에서 키워드가 날아감
list에서 쿼리스트링도 나오게 수정해도 null이면 화면이 안 나옴 → null 허용 필요
 

최종 수정

→ 값이 있거나 / 공백이거나 (nul 들어올 일 없음)
 

isempty

좋은 질문이에요! isEmpty()isBlank()는 비슷해 보이지만, 검사하는 기준이 달라요.
주로 Java에서 사용하는 메서드고, String 값을 검사할 때 차이를 잘 이해하고 써야 해요.

isEmpty()

  • 문자열의 길이가 0인지 확인합니다.
  • 즉, 문자열이 비어 있는지만 검사해요.
  • 공백은 있는 것으로 간주하기 때문에 false를 반환해요.
java 복사편집 String s1 = ""; // 길이 0 String s2 = " "; // 공백 3개 System.out.println(s1.isEmpty()); // true System.out.println(s2.isEmpty()); // false

isBlank()

  • 문자열이 비어있거나, 공백 문자(스페이스, 탭, 개행)로만 구성되어 있는지 확인해요.
  • Java 11부터 추가된 메서드입니다.
java 복사편집 String s1 = ""; // 길이 0 String s2 = " "; // 공백 3개 System.out.println(s1.isBlank()); // true System.out.println(s2.isBlank()); // true

🧠 정리

메서드
true가 되는 경우
공백만 있어도 true?
isEmpty()
문자열 길이가 0일 때
❌ 아니요
isBlank()
공백만 있거나 길이가 0일 때
✅ 네!

필요에 따라 선택해서 쓰면 돼요!
예: 사용자 입력이 "공백만 있는 문자열"인지까지 막고 싶으면 isBlank() 쓰면 좋고, 단순히 값이 입력됐는지만 확인하려면 isEmpty()도 괜찮아요.
혹시 어디에 쓰시려는 건지도 알려주면 더 구체적으로 추천해드릴 수 있어요 :)
 
BoardResponse
package shop.mtcoding.blog.board; import lombok.Data; import shop.mtcoding.blog.reply.Reply; import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; public class BoardResponse { @Data public static class DTO { private List<Board> boards; private Integer prev; private Integer next; private Integer current; private Integer size; // 3 private Integer totalCount; private Integer totalPage; private boolean isFirst; // current == 0 private boolean isLast; // 다음페이지에서 못 넘어가게 계산 필요 -> totalCount, size = 3 totalPages == current private List<Integer> numbers; private String keyword; public DTO(List<Board> boards, Integer current, Integer totalCount, String keyword) { this.boards = boards; this.prev = current - 1; this.next = current + 1; this.size = 3; // 보통은 final로 따로 빼서 씀 - 그래야 수정이 적어진다 this.totalCount = totalCount; // given 으로 처리 후 따로 연산(given으로 test 먼저 필요) -> test 끝나면 DB에서 들고옴 this.totalPage = makeTotalPage(totalCount, size); this.isFirst = current == 0; this.isLast = (totalPage - 1) == current; // totalPages는 1부터 시작하는데 current는 0부터 시작하니까 totalPages-1 필요 System.out.println("isLast: " + isLast); this.numbers = makeNumbers(current, totalPage); this.keyword = keyword; // null이거나 값이 있거나 } // page 계산 함수 private Integer makeTotalPage(int totalCount, int size) { int rest = totalCount % size > 0 ? 1 : 0; // 나머지 -> 5 / 3 = 나머지 2 , 6 / 3 = 나머지 0 // 나머지가 0이 아니면 rest = 1을 page에 더함 return totalCount / size + rest; // 전체 페이지 } private List<Integer> makeNumbers(int current, int totalPage) { List<Integer> numbers = new ArrayList<>(); int start = (current / 5) * 5; int end = Math.min(start + 5, totalPage); for (int i = start; i < end; i++) { numbers.add(i); } return numbers; } } // 깊은 복사 @Data public static class DetailDTO { private Integer id; private String title; private String content; private Boolean isPublic; private Boolean isOwner; // 대문자는 값을 안 넣으면 null, 소문자는 false private Boolean isLove; private Integer loveCount; private String username; private Timestamp createdAt; private Integer loveId; private List<ReplyDTO> replies; @Data // DetailDTO안에 있기 때문에 외부 클래스가 아닌 내부클래스 public class ReplyDTO { private Integer id; private String content; private String username; private Boolean isOwner; public ReplyDTO(Reply reply, Integer sessionUserId) { this.id = reply.getId(); this.content = reply.getContent(); this.username = reply.getUser().getUsername(); this.isOwner = reply.getUser().getId().equals(sessionUserId); } } // 템플릿엔진이 조건문 비교를 허용해주지 않기 때문에 필요함 public DetailDTO(Board board, Integer sessionUserId, Boolean isLove, Integer loveCount, Integer loveId) { this.id = board.getId(); this.title = board.getTitle(); this.content = board.getContent(); this.isPublic = board.getIsPublic(); this.isOwner = sessionUserId == board.getUser().getId(); // 같으면 true 같지 않으면 false this.username = board.getUser().getUsername(); this.createdAt = board.getCreatedAt(); this.isLove = isLove; this.loveCount = loveCount; this.loveId = loveId; List<ReplyDTO> repliesDTO = new ArrayList<>(); for (Reply reply : board.getReplies()) { ReplyDTO replyDTO = new ReplyDTO(reply, sessionUserId); repliesDTO.add(replyDTO); } this.replies = repliesDTO; } } }
 
BoardController
package shop.mtcoding.blog.board; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpSession; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.validation.Errors; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import shop.mtcoding.blog.user.User; @RequiredArgsConstructor @Controller public class BoardController { private final BoardService boardService; private final HttpSession session; @GetMapping("/") public String list(HttpServletRequest request, @RequestParam(required = false, value = "page", defaultValue = "0") Integer page, @RequestParam(required = false, value = "keyword", defaultValue = "") String keyword) { System.out.println("keyword: " + keyword); User sessionUser = (User) session.getAttribute("sessionUser"); if (sessionUser == null) { request.setAttribute("model", boardService.글목록보기(null, page, keyword)); // 로그인 안 했을 때 } else { request.setAttribute("model", boardService.글목록보기(sessionUser.getId(), page, keyword)); // 로그인 했을 때 } return "board/list"; } @PostMapping("/board/save") public String save(@Valid BoardRequest.SaveDTO saveDTO, Errors errors) { User sessionUser = (User) session.getAttribute("sessionUser"); boardService.글쓰기(saveDTO, sessionUser); return "redirect:/"; } @GetMapping("/board/save-form") public String saveForm() { return "board/save-form"; } @GetMapping("/board/{id}") public String detail(@PathVariable("id") Integer id, HttpServletRequest request) { User sessionUser = (User) session.getAttribute("sessionUser"); // 비로그인 시 상세보기 Integer sessionUserId = (sessionUser == null ? null : sessionUser.getId()); BoardResponse.DetailDTO detailDTO = boardService.글상세보기(id, sessionUserId); request.setAttribute("model", detailDTO); return "board/detail"; } @GetMapping("/board/{id}/update-form") public String updateForm(@PathVariable("id") Integer id, HttpServletRequest request) { User sessionUser = (User) session.getAttribute("sessionUser"); Board board = boardService.업데이트글보기(id, sessionUser.getId()); request.setAttribute("model", board); return "board/update-form"; } @PostMapping("/board/{id}/update") public String update(@PathVariable("id") Integer id, @Valid BoardRequest.UpdateDTO reqDTO, Errors errors) { User sessionUser = (User) session.getAttribute("sessionUser"); boardService.게시글수정(reqDTO, id, sessionUser.getId()); return "redirect:/board/" + id; } @PostMapping("/board/{id}/delete") public String delete(@PathVariable("id") Integer id) { User sessionUser = (User) session.getAttribute("sessionUser"); boardService.게시글삭제(id, sessionUser.getId()); return "redirect:/"; } }
 
BoardService
package shop.mtcoding.blog.board; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import shop.mtcoding.blog._core.error.ex.Exception403; import shop.mtcoding.blog._core.error.ex.Exception404; import shop.mtcoding.blog.love.Love; import shop.mtcoding.blog.love.LoveRepository; import shop.mtcoding.blog.reply.ReplyRepository; import shop.mtcoding.blog.user.User; import java.util.List; @RequiredArgsConstructor @Service public class BoardService { private final BoardRepository boardRepository; private final LoveRepository loveRepository; private final ReplyRepository replyRepository; @Transactional public void 글쓰기(BoardRequest.SaveDTO saveDTO, User sessionUser) { Board board = saveDTO.toEntity(sessionUser); boardRepository.save(board); } public BoardResponse.DTO 글목록보기(Integer userId, Integer page, String keyword) { if (userId == null) { Long totalCount = boardRepository.totalCount(keyword); List<Board> boards = boardRepository.findAll(page, keyword); return new BoardResponse.DTO(boards, page, totalCount.intValue(), keyword); // 로그인 안 했을때 } else { Long totalCount = boardRepository.totalCount(userId, keyword); List<Board> boards = boardRepository.findAll(userId, page, keyword); return new BoardResponse.DTO(boards, page, totalCount.intValue(), keyword); // 로그인 했을 때 } } public BoardResponse.DetailDTO 글상세보기(Integer id, Integer userId) { Board board = boardRepository.findByIdJoinUserAndReplies(id); Love love = loveRepository.findByUserIdAndBoardId(userId, id); Boolean isLove = love == null ? false : true; Integer loveId = love == null ? null : love.getId(); Long loveCount = loveRepository.findByBoardId(board.getId()); BoardResponse.DetailDTO detailDTO = new BoardResponse.DetailDTO(board, userId, isLove, loveCount.intValue(), loveId); return detailDTO; } public Board 업데이트글보기(Integer id, Integer sessionUserId) { Board boardPs = boardRepository.findById(id); if (boardPs == null) throw new Exception404("게시글을 찾을 수 없습니다."); if (!boardPs.getUser().getId().equals(sessionUserId)) throw new Exception403("권한이 없습니다."); return boardPs; } @Transactional public Board 게시글수정(BoardRequest.UpdateDTO reqDTO, Integer id, Integer sessionUserId) { Board board = boardRepository.findById(id); if (board == null) throw new Exception404("게시글을 찾을 수 없습니다"); if (!board.getUser().getId().equals(sessionUserId)) throw new Exception403("권한이 없습니다."); board.update(reqDTO.getTitle(), reqDTO.getContent(), reqDTO.getIsPublic()); return board; } @Transactional public void 게시글삭제(Integer id, Integer sessionUserId) { // 게시글 존재 확인 Board boardPs = boardRepository.findById(id); if (boardPs == null) throw new Exception404("게시글을 찾을 수 없습니다"); if (!boardPs.getUser().getId().equals(sessionUserId)) throw new Exception403("권한이 없습니다."); // 좋아요 삭제 loveRepository.deleteByBoardId(boardPs.getId()); boardRepository.deleteById(id); } }
 
BoardRepository
package shop.mtcoding.blog.board; import jakarta.persistence.EntityManager; import jakarta.persistence.Query; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; import java.util.List; @RequiredArgsConstructor @Repository public class BoardRepository { private final EntityManager em; public void save(Board board) { em.persist(board); } // 그룹함수 -> Long으로 return [test해서 무슨 타입으로 들어오는지 확인 필요] // 1. 로그인 안 했을 때 -> 4개 // 3. 로그인 했을 때 -> ssar이 아니면 -> 4개 public Long totalCount(String keyword) { String sql = ""; if (!(keyword.isBlank())) sql += "select count(b) from Board b where b.isPublic = true and b.title like :keyword"; else sql += "select count(b) from Board b where b.isPublic = true"; Query query = em.createQuery(sql, Long.class); // keyword를 포함 : title like %keyword% if (!(keyword.isBlank())) query.setParameter("keyword", "%" + keyword + "%"); return (Long) query.getSingleResult(); } // 2. 로그인 했을 때 -> ssar -> 5개 public Long totalCount(int userId, String keyword) { String sql = ""; if (!(keyword.isBlank())) sql += "select count(b) from Board b where b.isPublic = true or b.user.id = :userId and b.title like :keyword"; else sql += "select count(b) from Board b where b.isPublic = true or b.user.id = :userId"; Query query = em.createQuery(sql, Long.class); // keyword를 포함 : title like %keyword% if (!(keyword.isBlank())) query.setParameter("keyword", "%" + keyword + "%"); query.setParameter("userId", userId); return (Long) query.getSingleResult(); } // locahost:8080?page=0 -> 로그인 X public List<Board> findAll(int page, String keyword) { String sql; // 동적쿼리 if (keyword.isBlank()) { sql = "select b from Board b where b.isPublic = true order by b.id desc"; } else { sql = "select b from Board b where b.isPublic = true and b.title like :keyword order by b.id desc"; } Query query = em.createQuery(sql, Board.class); if (!keyword.isBlank()) { query.setParameter("keyword", "%" + keyword + "%"); } query.setFirstResult(page * 3); query.setMaxResults(3); return query.getResultList(); } // 로그인 O public List<Board> findAll(Integer userId, int page, String keyword) { String sql; // 동적쿼리 if (keyword.isBlank()) { sql = "select b from Board b where b.isPublic = true or b.user.id = :userId order by b.id desc"; } else { sql = "select b from Board b where b.isPublic = true or b.user.id = :userId and b.title like :keyword order by b.id desc"; } Query query = em.createQuery(sql, Board.class); query.setParameter("userId", userId); if (!keyword.isBlank()) { query.setParameter("keyword", "%" + keyword + "%"); } query.setFirstResult(page * 3); query.setMaxResults(3); return query.getResultList(); } // public List<Board> findAll(Integer userId) { // Integer를 써야 null을 넘길 수 있다 // // 동적 query // String s1 = "select b from Board b where b.isPublic = true or b.user.id = :userId order by b.id desc"; // String s2 = "select b from Board b where b.isPublic = true order by b.id desc"; // // Query query = null; // if (userId == null) { // query = em.createQuery(s2, Board.class); // } else { // query = em.createQuery(s1, Board.class); // query.setParameter("userId", userId); // } // // return query.getResultList(); // } public Board findById(Integer id) { return em.find(Board.class, id); } public Board findByIdJoinUser(Integer id) { // b -> board에 있는 필드만 프로잭션 / fetch를 써야 board안에 있는 user 객체도 같이 프로잭션됨 Query query = em.createQuery("select b from Board b join fetch b.user u where b.id = :id", Board.class); // inner join (on절은 생략가능하다) -> 객체지향 쿼리 query.setParameter("id", id); return (Board) query.getSingleResult(); } public Board findByIdJoinUserAndReplies(Integer id) { Query query = em.createQuery("select b from Board b join fetch b.user u left join fetch b.replies r left join fetch r.user where b.id = :id order by r.id desc", Board.class); // left join (on절은 생략가능하다) -> 객체지향 쿼리 query.setParameter("id", id); return (Board) query.getSingleResult(); } public void deleteById(Integer id) { Board board = em.find(Board.class, id); // 영속성 컨텍스트에 넣기 em.remove(board); // 연관된 Reply들도 함께 삭제됨 (Cascade 동작) } }
 
list
{{> layout/header}} <div class="container p-5"> <div class="mb-3 d-flex justify-content-end"> <form action="/" class="d-flex" method="get"> <input class="form-control me-2" type="text" placeholder="검색..." name="keyword"> <button type="submit" class="btn btn-primary">Search</button> </form> </div> {{#model.boards}} <div class="card mb-3"> <div class="card-body"> <div class="d-flex justify-content-between"> <h4 class="card-title mb-3">{{title}}</h4> {{^isPublic}} <div class="d-flex align-items-start"> <span class="badge bg-secondary">비공개</span> </div> {{/isPublic}} </div> <a href="/board/{{id}}" class="btn btn-primary">상세보기</a> </div> </div> {{/model.boards}} <ul class="pagination d-flex justify-content-center"> <li class="page-item {{#model.isFirst}}disabled{{/model.isFirst}}"><a class="page-link" href="?page={{model.prev}} &keyword={{model.keyword}} ">Previous</a></li> {{#model.numbers}} <li class="page-item"><a class="page-link" href="?page={{.}}&keyword={{model.keyword}}">{{.}}</a></li> {{/model.numbers}} <li class="page-item {{#model.isLast}}disabled{{/model.isLast}}"><a class="page-link" href="?page={{model.next}} &keyword={{model.keyword}} ">Next</a></li> </ul> </div> {{> layout/footer}}
Share article

parangdajavous