현재 프로젝트하면서 본인의 글이 맞는지 유무를 판단하기 위해서 Spring Data JPA와 Querydsl을 사용하고 있는데,
스터디원 중 한 분이 selectOne을 사용해보는게 어떻냐고 의견을 줘서 selectFrom & fetchOne과 selectOne & fetchFirst를 비교하기 위해서 글을 작성합니다.
단순히 비교와 기록을 남기는 것이라 자세한 설명은 https://jojoldu.tistory.com/516 로 가셔서 보시면 될 것 같습니다.
@Override
public boolean existByIdAndUserIdAndPostStatusNormal(Long postId, String userId, PostStatus postStatus) {
return jpaQueryFactory.selectFrom(post)
.where( post.id.eq(postId)
.and(post.userId.eq(userId))
.and(post.postStatus.eq(postStatus))
)
.fetchOne() != null;
}
수정하기 전의 코드인데 간단하게 설명하자면
사용자 A가 10번의 글을 수정하기 위해서 권한을 조회하는 로직을 작성중입니다.
postId와 userId ( 회원의 고유 번호) postStatus (글이 현재 삭제처리가 되었는지, 아니면 정상적인 상태인지)
를 사용하여 결과의 유무를 boolean 형으로 반환하려고 합니다.
짧은 생각으로 'fetchOne을 사용하여 한개의 결과를 가져온다' 고 생각해 fetchOne만 사용하였습니다.
물론 fetchOne은 내부적으로 SingleResult (한개의 결과값) 만을 반환하기 때문에 사용해도 예외가 발생하진 않지만
해당 로직은 메서드를 실행했을 때 Hibernate에 의해서 다음과 같은 SQL 문으로 번역이 됩니다.
Hibernate:
select
post0_.id as id1_0_,
post0_.contact_info as contact_2_0_,
post0_.content as content3_0_,
post0_.create_date as create_d4_0_,
post0_.modify_date as modify_d5_0_,
post0_.post_status as post_sta6_0_,
post0_.post_type as post_typ7_0_,
post0_.progress_type as progress8_0_,
post0_.recruit_status as recruit_9_0_,
post0_.title as title10_0_,
post0_.user_id as user_id11_0_,
post0_.view_count as view_co12_0_
from
post post0_
where
post0_.id=?
and post0_.user_id=?
and post0_.post_status=?
다음은 selecOne으로 조회할 때의 코드입니다.
@Override
public boolean existByIdAndUserIdAndPostStatusNormal(Long postId, String userId, PostStatus postStatus) {
return jpaQueryFactory.selectOne()
.from(post)
.where( post.id.eq(postId)
.and(post.userId.eq(userId))
.and(post.postStatus.eq(postStatus))
)
.fetchFirst() != null;
}
위 메서드를 실행했을때
Hibernate:
select
1 as col_0_0_
from
post post0_
where
post0_.id=?
and post0_.user_id=?
and post0_.post_status=? limit ?
의 구문이 나옵니다.
물론 Querydsl에서는 존재 여부를 알기 위한 exists 메서드가 존재합니다.
위의 링크를 읽어보셨다면 알겠지만 간단하게 얘기하자면
// QuerydslJpaPredicateExecutor.java
@Override
public boolean exists(Predicate predicate) {
return createQuery(predicate).fetchCount() > 0;
}
exists 함수는 전체를 한번 읽어온 다음에 결과를 반환하기 때문에 성능상에 문제가 있기 때문에
fetchFirst를 사용하여 여부를 반환합니다.
fetchFirst는 위 처럼 limit 1 과 fetchOne을 사용하고 있기 때문에 한개의 결과를 찾고나서 멈추게 되고,
결과적으로 반환하는 값이 Integer 형이기 때문에 있다면 1 없다면 null을 반환하게 되어
post가 null인지 조회하는 것보다 반환하는 데이터 양이 적어 DB를 조금 더 효율적으로 사용할 수 있을 것 같습니다.
추가적으로 물론 여기에는 post의 PK 값인 postId를 사용하고 있어 결과가 1 또는 null 뿐이지만,
글 번호 없으면 여러개 결과가 나올 수도 있겠지 ??
난 곧 죽어도 '이 사람이 글을 한개만 작성했는지 여부를 알고 싶어!' 라는 분이 fetchAll을 통해서 결과 값을 조회할 때에는
List<Integer> 형으로 반환이 되기 때문에 만약 결과가 5개라면
[1,1,1,1,1,1] 형식으로 오기 때문에 List의 size를 반환해서 글 갯수를 가져오면 될 것 같습니다.