댓글 목록을 볼 때 순수하게 댓글 목록(댓글 데이터)만 보는게 아니다. Board정보(1), User정보(1), Reply정보(N)가 다 나와야함 -> 화면 확인하면 바로 알 수 있음
전략 1) ONE관계는 JOIN하고, MANY관계는 조회를 한 번 더 한 후 DTO 담기
보드와 유저는 ONE 관계 (JOIN 필요)
보드와 댓글은 MANY 관계 (SELECT를 한 번 더 함)
-> 담을 DTO 필요
1번 전략 설명
테스트 하려는데.... findByBoardId는 JpaRepository에 존재하지 않기 때문에 (기본 CRUD 아님) ReplyJPARepository에 만들어줘야 함
[ ReplyJPARepository ]
쿼리 메소드 아니니 @Query 적어주고
[ 테스트 하자 ]
조회 잘 됨! 이제 여기서 댓글만 JOIN하면 끝!
[ 문제 발생! return을 board, reply 이렇게 담을 수 없으니 DTO 필요! ]
[ BoardService ]
// 한 번에 캡쳐 할 수 없어서 나눠서 캡쳐함
board랑 replyList 둘 다 return 해줘야 하는데… 둘 다 return 하는 건 불가능!
때문에 둘을 담고 있는 DTO를 생성해서 한 번에 넘겨줘야 한다.
[ 쿼리문 조회 ]
이렇게 하면 밑의 결과가 나오는지 실습으로 확인 필요!
ONE관계는 join하고, MANY관계는 select 하고.. select가 2번 일어남 !
1번 전략은 쌤이 좋아하는 방식이다. 현재 댓글은 페이징 안 되어있는 상태. (실제 서비스에서는 댓글도 페이징이 되어야함) join으로 해서 댓글을 페이징하는 쿼리가 엄청 힘들다. join-join-join (3단)으로 페이징하는게 너무 어렵다!! 때문에 select 2번해서 List<Reply> replyList = replyJPARe~~~.findByBoardId(boardId) 를 페이징 하는게 낫다.
전략 2) MANY관계를 ‘양방향 매핑’ 하기 → @OneToMany 사용
[ Board테이블 ]
컬렉션은 필드로 받을 수 없어서 오류
[ @OneToMany 달아주자 ]
replies는 조회할 때, 담는 용도로만 사용. -> 애가 DB 필드가 되면 안됨! -> @OneToMany 걸기 이 List<Reply> replies는 Board랑 뭘 관계하고 있지? -> 변수명을 적어서 알려줘야 함 즉, FK의 주인이 누구인지 mappedBy로 설정해줘야 함 이때, Entity객체의 변수명을 적어주면 됨!
@Builder는 컬렉션은 허용하지 않기 때문에 List<Reply> replies는 빌더로 만들 수 없다!
@ManyToOne - EAGER 이 디폴트 전략
@OneToMany - LAZY가 디폴트 전략
쌤은 컬렉션 관계는 이렇게 조회를 한 번 더 해서 합치는 걸 선호한다. (1번 방법)
전략 3) 한방 JOIN
package shop.mtcoding.blog.board; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import java.util.Optional; public interface BoardJPARepository extends JpaRepository<Board, Integer> { @Query("select b from Board b join fetch b.user u where b.id = :id") Optional<Board> findByIdJoinUser(@Param("id") int id); //board에 다 담을 것 @Query("select b from Board b join fetch b.user u join fetch b.replies r where b.id = :id") Optional<Board> findByIdJoinUserAndReplies(@Param("id") int id); }
join 쿼리 작성
[ 테스트 ]
@DataJpaTest public class BoardJPARepositoryTest { @Autowired private BoardJPARepository boardJPARepository; @Autowired private EntityManager em; @Test public void findByIdJoinUserAndReplies_test() { //given int id = 4; //when //옵셔널이라 오류가 나니까 .get 붙여줌. get는 있으니까 가져와! 라는 말 Board boards = boardJPARepository.findByIdJoinUserAndReplies(id).get(); //then }
[ 댓글 목록 보기 3가지 전략 요약 ]
1. One 관계는 join하고, Many 관계는 Lazy Loading 한다. 그리고 DTO에 담는다. select 칠 필요 없음. 자기가 쳐줌. 애를 디폴트로 사용하자!~! (통신 1번 / 12회 조회) 2. One 관계는 join하고, Many 관계를 페이징 (or 복잡한 무언가) 해야 된다면 select를 직접 쿼리를 만들어서 2번 조회하자 (Lazy Loading으로 페이징 안됨!) (통신 2회 / 12회 조회) 3. One 관계와 Many 관계를 한 방에 JOIN 한다. (28회 조회)
현재 프로젝트할 때에는 1번을 써라.
근데 댓글을 페이징 해야한다던지.. 그러면 2번을 사용
→ 사실 이걸 써라! 라는 답이 없긴 함.
어떤 전략을 선택해야 할 지 결정할 수 있는 게 중요! (상황에 따라 다르기 때문)
속도, 성능 고려 … 데이터에 따라 고려, 페이징 되었을 때 뭐 이런 것도 고려…해서 공부를 하자^^
Share article