Framework & Library/SpringDataAccess

JPA ) JPQL DTO Mapping 및 interface Mapping 방법

ruu++ 2024. 5. 21. 21:45

JQPL 맵핑하기

  • JPQL에서 DTO Mapping과 interface를 이용한 매핑 방법입니다.
  • Interface 매핑시 주의 사항
    1. as(별칭)을 사용하여 PostMapping클래스의 필드명과 맞추는 작업이 필요합니다. 별칭이 맞지 않다면 null을 반환합니다.
  • DTO 매핑시 주의사항
    1. 데이터를 받을 생성자가 꼭 필요하고 정의해야 합니다.
    2. 필드명과 맞출 필요 x, 별칭 사용할 이유 x, but 생성자 파라미터의 데이터 타입을 엔티티의 타입과 반드시 맞춰 사용해야 합니다!.

//Repository
public interface PostRepository extends JpaRepository<Post, Long> {  

    //interface를 사용한 매핑 방법 
    @Query("select p.id as id, p.content as content, p.title as title, p.writer as writer, p.updatedAt as updatedAt, " +  
            "(select count(cp.comment.id) from CommentPostAssociation cp where cp.post.id = p.id) as commentCount " +  
            "from Post p join fetch Board b on b.id = p.board.id " +  
            "where b.url = :boardUrl " +  
            "order by p.id DESC")  
    Page<PostMapping> findPostByBoardUrlWithPagination(String boardUrl, Pageable pageable);  

    @Query("select new org.ruu.developerkorea.domain.board.model.dto.post.PostDTO(p.id, p.title, p.content, p.writer, p.updatedAt, " +  
            "(select count(cp) from CommentPostAssociation cp where cp.post.id = p.id) ) " +  
            "from Post p join fetch Board b on b.id = p.board.id " +  
            "where b.url = :boardUrl " +  
            "order by p.id DESC")  
    Page<PostDTO> findPostByBoardUrlWithPagination2(String boardUrl, Pageable pageable);  
}

//PostDTO
@Builder  
@Getter  
@ToString  
public class PostDTO {  

    private Long id;  
    private String title;  
    private String content;  
    private String writer;  
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy.MM.dd")  
    private LocalDateTime updatedAt;  
    private Long commentCount;  

    public PostDTO(Long id, String title, String content, String writer, LocalDateTime updatedAt, Long commentCount) {  

        this.id = id;  
        this.title = title;  
        this.content = content;  
        this.writer = writer;  
        this.updatedAt = updatedAt;  
        this.commentCount = commentCount;  
    }  

    public PostDTO() {  
    }  
}

//PostMapping

public interface PostMapping {  

    Long getId();  
    String getContent();  
    String getTitle();  
    String getWriter();  
    LocalDateTime getUpdatedAt();  
    long getCommentCount();  

}

두가지 매핑 방식 사용하기

  • Interface 방식은 Mapping 인터페이스가 존재하여 mapper용 파일이라는 것을 명확히 할 수 있습니다.
  • 하지만, 한번 더 변환하거나 별칭을 맞춰야 하는 불편함이 존재합니다.
  • 두 방식 모두DTO 및 PostMapping의 필드 이름의 변경이 일어나더라도 타입이 같다면 코드가 정상 동작할 수 있습니다. 하지만 그 경우데이터가 NULL을 반환할 수 있기 때문에 수정 이전에 어디까지 DTO 및 Interface까지 영향을 끼치는지 확인하고 null 체크 테스트를 반드시 해야 합니다.
@Component  
@RequiredArgsConstructor  
@Slf4j  
public class PostRetriever {  

    private final PostRepository postRepository;  

    //Interface 방식
    public List<PostDTO> retrievePostByBoardUrl(String boardUrl) { 

        Pageable pageable = PageRequest.of(0, 10);  
        Page<PostMapping> PostList = postRepository.findPostByBoardUrlWithPagination(boardUrl, pageable);   

        return PostList.getContent()  
                .stream()
                .map(post  ->  
                        PostDTO.builder()  
                                .id(post.getId())  
                                .title(post.getTitle())  
                                .writer(post.getWriter())  
                                .content(post.getContent())  
                                .updatedAt(post.getUpdatedAt())  
                                .commentCount(post.getCommentCount())  
                                .build()  
                )  
                .toList();  
    }  
    //DTO 방식
    public List<PostDTO> retrievePostByBoardUrl2(String boardUrl) {  

        Pageable pageable = PageRequest.of(0, 10);  
        Page<PostDTO> PostList = postRepository.findPostByBoardUrlWithPagination2(boardUrl, pageable);  

        return postList.getContent();
}