[JPA] Formula

SW고구마·2021년 11월 24일
0
post-custom-banner

Spring-data-JPA 를 활용해 게시판 프로젝트를 진행하면서 게시글(Board)을 불러오면서 댓글의 개수를 가져오는 법에 대해 고민하다가 Formula라는 것을 접하게 되었다.
그러면서 Formula를 왜 사용했는 지 기록으로 남기면 좋을 것 같아 기록으로 남긴다.

고민사항

위의 사진과 같이 게시글을 불러오는 과정에서 댓글의 개수를 보여주고 싶었고 이를 어떤식으로 처리하면 좋을 지 고민하게 됐다.

방법1) List.size()로 댓글 개수 구하기

우선 게시글(Board) Entity은 이러하다

public class Board {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) //auto_increment
    private int id;

    //게시글 제목
    @Column(nullable = false, length = 100)
    private String title;

    //게시글 내용
    @Lob //대용량 데이터
    private String content;

    //게시글 조회수
    @Column(name = "viewCnt")
    private int viewCount;

    //작성자
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "userId")
    private User user;
  
    //댓글
    @OneToMany(mappedBy = "board", fetch=FetchType.LAZY, cascade = CascadeType.ALL) 
    @JsonIgnoreProperties({"board"})
    @OrderBy("id desc")
    private List<Reply> replies;
    
    
    //생성일
    @CreationTimestamp
    private Timestamp createDate;
}

게시글(Board)와 연관된 게시글을 모두 불러 list에 담고 해당 list의 사이즈를 반환하는 것으로 댓글 개수를 구했다.

내가 생각하는 방법1) 의 문제

게시글에 연관된 댓글의 정보가 아닌 댓글의 개수만 필요한 경우 List로 담게 된다면 메모리 낭비가 생기고 ResultSet을 매핑하는데까지 시간이 더 필요하기 때문에 댓글 수가 많다면 조회 시간이 길어질 것이다.

보통 SQL 문으로 쿼리를 작성한다면 아래와 같이 가능할 것이다.

Select b.*, (select count(1) from Reply where boardId = b.id)
from Board b
order by id desc

이런 식으로 쿼리를 날려가져온다면 더 효율적이지 않을까? 라는 생각과 함께 Formula를 사용하게 되었다.

방법2) @Formula 어노테이션 사용

@Formula 란?

JPA 의 명세는 아니지만 Hiberante에서 제공하는 @Formula 어노테이션을 사용하면 가상 컬럼을 매핑할 수 있다. 가상컬럼이라는 것은 JPA 상에는 존재하지만 DB에는 생성되지 않는 칼럼을 말한다. 주의해야 할 점은 하이버네이트 문서에도 언급되어 있지만 네이티브 SQL을 사용한다는 것이다.

가상칼럼은 @Formula 안의 정의 된 내용을 서브쿼리로 질의해 결과를 가져와 준다.
JBoss hibernate docs @Formula

이를 바탕으로 구현한 게시글(Board) Entity

public class Board {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) //auto_increment
    private int id;

    //게시글 제목
    @Column(nullable = false, length = 100)
    private String title;

    //게시글 내용
    @Lob //대용량 데이터
    private String content;

    //게시글 조회수
    @Column(name = "viewCnt")
    private int viewCount;

    //작성자
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "userId")
    private User user;

    //댓글 개수
    @Formula("(SELECT count(1) FROM reply r WHERE r.boardId = id)")
    private int replyCount;

    @CreationTimestamp
    private Timestamp createDate;

}

Formula는 기본적으로 fetchType.EAGER의 전략으로 가져온다. 하지만 게시글을 불러올 때 댓글의 개수 정보가 항상 필요로 했기에 Lazy로 수정하지 않았다

방법3) JPQL이나 QueryDSL 으로 쿼리 정의

원하는 결과를 얻기 위해 쿼리문을 직접 작성해주는 방법도 있지만 @Formula를 사용함으로 해결해 시도해보지는 않았다.

profile
하루하루 조금씩이라도
post-custom-banner

0개의 댓글