Contents
최종 수정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}}


검색하면 잘 되나 페이지 버튼 눌렀을때 쿼리스트링에서 키워드가 날아감
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