문제
Spring Data JPA를 이용해서 페이지네이션-정렬을 구현하는데,
외부 참조 엔티티의 참조하는 엔티티의 수?를 이용해서 정렬을 하고싶다. (좋아요 수)
이게 방법을 몰라, 일단, 외부 엔티티를 참조하는 멤버변수의 이름으로 정렬을 해보니, 정렬이 되긴 되었다.
하지만, 첫번째 데이터가 한번 더 중복되서 나오는 문제가 발생
심지어 순서대로 중복되는 것도 아님
왜 중복이되서 나오지조차 감이 안잡힘
DomainEntity.class
@Table(name = "post")
public class Post extends Timestamped {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private Long idx;
@Column(nullable = false)
private String title;
@Column(nullable = false)
private String content;
@Column
private String img = "/img/no-pic.png";
@Column(columnDefinition = "int default 0")
private int view;
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
@JsonIgnore
@OneToMany(mappedBy = "post", cascade=CascadeType.ALL)
private List<PostComment> comments = new ArrayList<PostComment>();
@JsonIgnore
@OneToMany(mappedBy = "post", cascade=CascadeType.ALL)
private List<PostLike> likes = new ArrayList<PostLike>();
DomainService.class
//게시글 리스트
public Page<Post> getPostList(int page, int size, String sorting){
Pageable pageable;
if(sorting.equals("descending")) {
pageable = PageRequest.of(page, size, Sort.by("createdAt"));
}
else if(sorting.equals("likes")){
pageable = PageRequest.of(page, size, Sort.by(sorting).descending());
}
else
pageable = PageRequest.of(page, size, Sort.by(sorting).descending());
Page<Post> postList = postrepository.findAll(pageable);
return postList;
}
하고싶은것
1. 정렬 데이터 증복 에러 해결
2, 외부 참조 엔티티의 개수로 정렬하는 법 아기
해결방법
일단 JPA에서 쿼리를 어떻게 실행하는지 확인해 보자
Spring boot에서 JPA 쿼리 파리미터를 확인하는 방법은,
application.properties 에 아래 2줄을 추가해주면 터미널에서 확인할 수 있다.
logging.level.org.hibernate.SQL=debug
spring.jpa.properties.hibernate.format_sql=true
Full Scan 방식의 Offset 페이지네이션 쿼리를 이용하는 JPA
똑같이 Mysql 쿼리문으로 해보았고 결과는 역시 똑같이 데이터가 중복되어 나왔다..
쿼리문이 잘못되었나보다,, 뭐가 잘못됬는지 알아보자
아 문제점은, post id와 post like 의 post id가 같으면 모든 컬럼을 불러오기 때문이였다..
테스트를 위해 다른 아이디를 하나 만들어 좋아요를 마구마구 눌러주었더니...
짜잔,,, 중복중복 히히
문제를 파악했으니 해결해보자!!
문제 해결
고민을 해보다 서브 쿼리 count 가 생각이 나서 찾아보니, JPA로 count를 구현혈려면, JPQL을 사용해야하고, 성능이 굉장히 안나온다고 한다.(모든 쿼리를 계속 호출해서)
그러다 @Formula어노테이션을 찾았다.
코드부터!
DomainEntity.class
//좋아요 수 count 컬럼 - 가상
@Formula("(select count(*) from post_like l where l.post_id = id)")
private int countOfLikes;
DomainService.class
//게시글 리스트
public Page<Post> getPostList(int page, int size, String sorting){
Pageable pageable;
if(sorting.equals("descending")) {
pageable = PageRequest.of(page, size, Sort.by("createdAt"));
}
else if(sorting.equals("likes")){
pageable = PageRequest.of(page, size, Sort.by("countOfLikes").descending());
}
else
pageable = PageRequest.of(page, size, Sort.by(sorting).descending());
Page<Post> postList = postrepository.findAll(pageable);
return postList;
}
Formula 어노테이션
- JPA에 명세에서는 서브쿼리를 지원하지 않는다.
- Formula 어노테이션은 하이버네이트가 제공하는 기능이다.
- Formula로 만들어지는 컬럼은 가상 컬럼으로, 실제 존재하지않는 읽기전용의 컬럼으로 생각 할 수 있을 것 같다.
- Formula 어노테이션은 네이티브 SQL문을 사용한다!
- 보통 JPA에서 조건절 혹은 집계된 데이터가 필요할 때, 포뮬라 어노테이션을 사용한다고 한다.
깔금하게 해결
참고
접근방법 - 외부 참조 엔티티 count
@Folmula 사용법
https://www-swpro-com.tistory.com/24
'Spring > Spring Boot' 카테고리의 다른 글
[Spring] API 문서 자동화 2 - Sping REST Docs (0) | 2021.12.31 |
---|---|
[Spring] API 문서 자동화 1 - Swagger (0) | 2021.12.29 |
[Spring] JPA의 이해(JPA, Hibernate) (0) | 2021.12.29 |
[Spring] 스프링 부트 페이지네이션 (Query, JPA, offset / cursor 페이지네이션) (2) | 2021.12.23 |
[Spring] 스프링 시큐리티 - 카카오 소셜로그인 하기(OAuth) (4) | 2021.12.04 |