Today I Learned
(24.07.02)[12주차] QueryDSL의 Wildcard 와 Count
count를 사용하여 총 갯수를 구하려는 쿼리문을 QueryDSL로 작성을 해야할 필요가 있다.
하지만, 어떤 상황에 따라서는 Join 에 따라서 count가 실행되지 않을 수 있다.
QueryDSL 의 Wildcard 사용 이슈
public ProfileResponseDto getUserProfile(String userName) {
QUser qUser = QUser.user;
QLike qLike = QLike.like;
...
Long aptLikesCount = jpaQueryFactory.select(Wildcard.count)
.from(qLike)
.leftJoin(qLike.user, qUser).fetchJoin()
.where(qLike.user.userName.eq(user.getUserName())
.and(qLike.apart.isNotNull()))
.fetchOne();
...
이슈
구현 목적
- User정보와 1:N 관계를 맺고 있고, 해당 User의 Apart 게시글과 Qna 댓글의 아이디를 필드로 각각 연관관계를 가지고 있는, Like 좋아요 엔터티가 있는 코드 = 사용자가 로그인 후, 게시글과 댓글에 좋아요를 요청할 때 마다, DB에 해당 좋아요가 어느 게시물 또는 어느 좋아요에 누가 추가를 했는지 ID를 저장하는 로직
- 정보를 가지고 있는 Like 엔터티에서, 해당 사용자의 Username이 Apart에 관하여 좋아요 누른 정보를 취합하여 몇개의 좋아요를 눌렀는지 조회를 해야하는 것이 목적
- 로그인을 한 후, Header에 담겨있는 Access Token으로 사용자 인가 인증을 맞췄기 때문에, Controller단에서 UserDetails에서 User 객체를 받아오기 때문에, 비밀번호, DB의 PK인 ID 등이 아닌 userName 만받아 올 수 있어 userName으로 찾는것이 일단 목적
구체적 이슈
- Like엔터티에는 User 엔터티가 user_id로 매핑되어 연관관계가 설정되어 있기 때문에, Likes와 Users 테이블을 JOIN 한 테이블에서 user_name을 WHERE 조건을 걸고 Wildecard를 사용해서 그 데이터의 갯수를 알고자 함
- 따라서, 한번에 모든 데이터를 가지고와서 JOIN 시키기위해서 fetchJoin() 메서드를 사용한 후, Wildcard.count 를 사용한다면, 클라이언트에게 500 서버 에러코드로 응답
원인
- QueryDSL의 count 관련 쿼리들의 특징을 이해를 하지 못한 상태
- 원래는 한번에 FETCH JOIN 으로 EAGER 타입으로 모든 연관관계의 데이터들을 불러와서 그 데이터들의 갯수를 세는 것이 의도였으나, count 쿼리들은 그렇게 작동을 하지 X
- fetchJoin 은 Hibernate 가 연관 엔터티 데이터 모두를 한번에 로드하라고 지시를 내림 (FetchType도 무시) -> count는 이러한 로드가 필요하지 않고 곧바로 레코드 수를 세려고 시작함 -> Hibernate는 연속적인 지시를 계속해서 추가적으로 수행하면서 Server에 500 에러 코드를 날리게됨
해결
Long aptLikesCount = jpaQueryFactory.select(Wildcard.count)
.from(qLike)
.leftJoin(qLike.user, qUser)
.where(qLike.user.userName.eq(user.getUserName())
.and(qLike.apart.isNotNull()))
.fetchOne();
- count 관련 쿼리들을 JPA 환경에서 사용을 하려면 EAGER Loading하지 않는 상태에서 count 를 시켜줘야하므로, fetchJoin 자체를 쓸 필요가 X
- 그냥 JOIN 만 하면 데이터 즉시 로딩 작업 없이, 전체적인 테이블의 row 데이터 값의 갯수만 세서 Long타입으로 반환
- 그냥, fetchJoin() 없이 Join 관련 쿼리만 사용하면 됨!
- 매우 단순한 해결이지만, 이해가 부족할 수 있다는 점
- SQL 의 접근과 Hibernate가 지시를 수행한다는 점에 있어서 다른 부분이므로 인지를 하고 있어야
Wildcard
- QueryDSL에서만 사용하는 특별한 표현
- 카운트 쿼리에서 사용할 수 있는 공통적인 상수
- 즉, QueryDSL 처럼 사용할 수 있는 어떠한 상수라고 이해할 수 있음
Wildcard.all
QEmployee employee = QEmployee.employee;
List<Employee> employees = queryFactory.select(Wildcard.all)
.from(employee)
.fetch();
- 모든 데이터를 선택한다는 와일드카드
- 사실상 그냥 조회하고 싶은 엔터티를 넣으면 됨
- 명시적으로 써주는걸 많이 사용함
Wildcard.count
QEmployee employee = QEmployee.employee;
long employeeCount = queryFactory.select(Wildcard.count)
.from(employee)
.fetchOne();
- 모든 데이터의 갯수 = row 갯수를 Long 타입으로 반환
- fetchOne() 을 반드시 사용을 해줘야함
- count() 와 동일하지만 명시적으로 모든 결과 값들의 갯수를 세갰다는 것을 보여줄 수 있음
'Today I Learned' 카테고리의 다른 글
(24.07.04)[12주차] Redis 강의 학습 (0) | 2024.07.04 |
---|---|
(24.07.03)[12주차] QueryDSL사용을 위한 Repository 분리 (0) | 2024.07.03 |
(24.06.28)[11주차] QueryDSL과 SQL의 비교와 정리 (0) | 2024.06.28 |
(24.06.27)[11주차] JPA 심화 강의 02 (마무리) (0) | 2024.06.27 |
(24.06.26)[11주차] JPA 심화 강의 01 (0) | 2024.06.26 |