JPA/Querydsl

Querydsl에서 컬럼의 존재 유무를 가져오기

비뀨_ 2022. 10. 24. 01:39

현재 프로젝트하면서 본인의 글이 맞는지 유무를 판단하기 위해서 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를 반환해서 글 갯수를 가져오면 될 것 같습니다.