[JPA] @Formula, 게시글 좋아요 순으로 정렬하기

olsohee·2023년 8월 23일
0

JPA

목록 보기
15/21
post-custom-banner

게시판 프로젝트를 진행하던 중 좋아요 순으로 게시글을 정렬하려고 했으나, 막히게 되었고 그 해결방법으로 @Formula라는 것을 알게 되어서 기록하고자 한다.

1. 프로젝트 구성

게시글(Article) 엔티티는 다음과 같다.

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Article {

    @Id @GeneratedValue
    @Column(name = "article_id")
    private Long id;

    private String title;

    private String content;

    @Enumerated(value = EnumType.STRING)
    private ArticleCategory category;

    @Column(name = "post_date")
    private LocalDateTime postDate;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User user;

    @OneToMany(mappedBy = "article", cascade = CascadeType.ALL)
    private List<Comment> comments = new ArrayList<>();

    @OneToMany(mappedBy = "article", cascade = CascadeType.ALL)
    private List<ArticleTag> articleTags = new ArrayList<>();

    @OneToMany(mappedBy = "article", cascade = CascadeType.ALL)
    private List<ArticleImage> images = new ArrayList<>();

    @OneToMany(mappedBy = "article")
    private List<LikeArticle> likeArticles = new ArrayList<>();

	...
}

게시글 좋아요와 관련해서 매핑은 다음과 같이 되어있다.

  • article : like_article (일대다)

  • like_article : users (다대일)


2. 좋아요 순으로 정렬

2.1. 문제 발생

게시글 좋아요 순으로 정렬하기 위해 처음에는 다음과 같이 코드를 작성했다.

public interface ArticleRepository extends JpaRepository<Article, Long> {

    @Query(value = "select a from Article a join a.likeArticles la order by la.size desc")
    Page<Article> findAllOrderByLike(Pageable pageable);
}

그런데 다음과 같은 에러가 발생했다.

aused by: java.lang.IllegalArgumentException: org.hibernate.query.SemanticException: Could not resolve attribute 'size' of 'com.example.mypetlife.entity.article.LikeArticle'

Caused by: org.hibernate.query.SemanticException: Could not resolve attribute 'size' of 'com.example.mypetlife.entity.article.LikeArticle'

아마 la.size를 기준으로 정렬을 해야하는데, la.size가 like_article의 칼럼이 아니여서 발생한 오류인 거 같다. 즉 정렬을 하기 위해선 해당 정렬 기준이 특정 테이블의 칼럼이어야 하는 거 같다.

'게시글 좋아요 수를 Article 엔티티 내의 필드로 관리하지 않고, LikeArticle과 같이 다른 엔티티와 연관관계를 맺도록 하였는데, 어떻게 좋아요 수를 Article의 필드로 갖게하지..?' 라고 고민하다가, 구글링을 통해 @Formula라는 것이 있다는 것을 알게 되었다!

2.2. 문제 해결

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

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Article {

    @Id @GeneratedValue
    @Column(name = "article_id")
    private Long id;

    ...

    @OneToMany(mappedBy = "article")
    private List<LikeArticle> likeArticles = new ArrayList<>();

	// 좋아요 갯수를 의미하는 가상 칼럼
    @Formula("(select count(*) from like_article where like_article.article_id = article_id)")
    private int likeCount;
    
	...
}

위와 같이 Article 엔티티에 @Formula를 사용하여 가상 칼럼인 likeCount를 정의해주었다. 가상 칼럼이기 때문에 DB에서는 like_count라는 칼럼을 확인할 수 없다.

그리고 이 likeCount는 select count(*) from like_article where like_article.article_id = article_id 쿼리를 통해, 해당 article과 연관 관계를 맺고 있는 like_article의 수, 즉 게시글 좋아요 수를 가진다.

public interface ArticleRepository extends JpaRepository<Article, Long> {

    @Query(value = "select a from Article a order by a.likeCount desc, a.postDate desc")
    Page<Article> findAllOrderByLike(Pageable pageable);
}

그리고 JPA의 @Query를 통해 쿼리를 생성하는데 이때 a.likeCount를 기준으로 정렬한다. a.likeCount는 JPA 상에 존재하는 article의 칼럼이기 때문에 정상적으로 동작한다!

Reference

https://velog.io/@hahm0726/JPA-Formula

https://steady-coding.tistory.com/479

profile
공부한 것들을 기록합니다.
post-custom-banner

0개의 댓글