우리가 선택한 전략
One 관계는 조인하고, Many 관계는 Lazy Loading 한다
[ 양방향 매핑 ]
@NoArgsConstructor @Data @Table(name = "board_tb") @Entity public class Board { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String title; private String content; //@JoinColumn(name = "user_id") @ManyToOne(fetch = FetchType.LAZY) private User user; // db -> user_id @CreationTimestamp // pc -> db (날짜주입) private Timestamp createdAt; @OrderBy("id desc") @OneToMany(mappedBy = "board", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE) // Entity 객체의 변수명 == FK의 주인 private List<Reply> replies = new ArrayList<>(); @Transient // 테이블 생성이 안됨 private boolean isBoardOwner; @Builder public Board(Integer id, String title, String content, User user, Timestamp createdAt) { this.id = id; this.title = title; this.content = content; this.user = user; this.createdAt = createdAt; } }
@OneToMany 걸어줬다.
@OrderBy(”id desc”) 하면 댓글도 내림차순 가능!
[ 비어있는데 왜 new를 할까? ]
나중에 댓글 for문 돌릴때 new를 안해놓으면 터짐. 왜냐면 null을 for문 돌리는 거니까... new 해주는게 좋음!! (json은 빈 배열은 replies : [] 이런식으로 만들어줄 것임)
[ BoardService ]
// board, isOwner public Board 글상세보기(int boardId, User sessionUser) { Board board = boardJPARepository.findByIdJoinUser(boardId) .orElseThrow(() -> new Exception404("게시글을 찾을 수 없습니다")); //게시글 주인 여부 boolean isBoardOwner = false; if(sessionUser != null){ if(sessionUser.getId() == board.getUser().getId()){ isBoardOwner = true; } } board.setBoardOwner(isBoardOwner); //레이지 로딩 //댓글은 forEach문 돌리기 (N이니까) board.getReplies().forEach(reply -> { boolean isReplyOwner = false; //댓글 주인 여부 if(sessionUser != null){ if(reply.getUser().getId() == sessionUser.getId()){ isReplyOwner = true; } } reply.setReplyOwner(isReplyOwner); }); return board; }
[ 설명 ]
댓글의 목록을 순회하면서, 각 댓글이 현재 세션 사용자(sessionUser)에 의해 작성되었는지를 확인하고, 그 결과를 바탕으로 댓글이 '소유자'에 의해 작성되었는지 여부(isReplyOwner)를 설정! -> 현재 세션의 사용자가 댓글의 작성자인지 확인해야 함! (삭제나 수정 버튼 표시 때문) -> isReplyOwner 변수에 true 또는 false를 설정 하고, set으로 담는다. 이 모든 처리가 완료된 board 객체를 return 구문을 통해 메소드를 호출한 곳으로 반환. 반환된 board 객체에는 게시글의 상세 정보, 게시글 주인 여부, 각 댓글과 댓글 주인 여부 등의 정보가 포함되어 있으며, 이 정보로 게시글 상세 페이지를 렌더링하는 데 사용함.
[ BoardController ] → Lazy Loading 테스트?
@RequiredArgsConstructor @Controller public class BoardController { private final BoardService boardService; private final BoardRepository boardRepository; private final HttpSession session; @GetMapping("/board/{id}") public String detail(@PathVariable Integer id, HttpServletRequest request) { User sessionUser = (User) session.getAttribute("sessionUser"); Board board = boardService.글상세보기(id, sessionUser); request.setAttribute("board", board); //이 로고가 찍히면서 레이지 로딩이 될 것임 System.out.println("서버 사이드 랜더링 직전에는 Board와 User만 조회된 상태이다~~~~~~"); return "board/detail"; }
join user만 했죠? = join fetch 했죠?
= 게시글(Board)을 데이터베이스에서 조회할 때,
게시글을 작성한 사용자(User)의 정보도 함께 조회(join) 했죠?
[ detail.mustache ]
레이지 로딩이 일어나면서 user가 select 될 것임 이렇게 user.name처럼 머스태치에 뿌리면서 레이지 로딩을 발동시키면 됨! 서비스에서 레이지 로딩 발동시켜도 되지만... 지금은 머스태치에서 발동시키자!
[ 홈페이지의 상세보기에 들어가 보자 ]
댓글 조회 되는 것 확인! (쓰레기통도!)
Hibernate: select b1_0.id, b1_0.content, b1_0.created_at, b1_0.title, u1_0.id, u1_0.created_at, u1_0.email, u1_0.password, u1_0.username from board_tb b1_0 join user_tb u1_0 on u1_0.id=b1_0.user_id where b1_0.id=? Hibernate: select r1_0.board_id, r1_0.id, r1_0.comment, r1_0.created_at, r1_0.user_id from reply_tb r1_0 where r1_0.board_id=? 서버 사이드 랜더링 직전에는 Board와 User만 조회된 상태이다~~~~~~ //댓글을 적은 user Hibernate: select u1_0.id, u1_0.created_at, u1_0.email, u1_0.password, u1_0.username from user_tb u1_0 where u1_0.id in (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) //디폴트 배치 사이즈가 아니었으면 select 2번 (유저 수만큼) 날아감
우리가 선택한 전략은 이런 식으로 쿼리가 발동한다.
[ 그런데 이게 뭐지? open-in-view? ]
→ open-in-view 설명 보러가기!
레이지 로징을 신경 안 쓰려면 조인을 하자!
우리 2차 프로젝트에선 오픈 인 뷰 끄고 할거임
DTO 만들어서 .get ~ 이런식으로… 레이지 로딩 할 것! 제이슨 데이터로!
그래서 원래는 필요 없지만… 일단 지금은 true로 설정하자!
Share article