QueryDSL 쿼리 최적화

보트·2023년 9월 26일
0

Spring

목록 보기
27/27

기존 UserRepositoryQueryImpl.java 이용해 검색했을 때

@Component
@RequiredArgsConstructor
public class UserRepositoryQueryImpl implements UserRepositoryQuery {

    private final JPAQueryFactory jpaQueryFactory;


    @Override
    public List<User> searchUserByKeyword(UserSearchCond cond) {
        return searchByKeyword(cond, user.username);
    }

    @Override
    public List<User> searchNickByKeyword(UserSearchCond cond) {
        return searchByKeyword(cond, user.nickname);
    }

    private List<User> searchByKeyword(UserSearchCond cond, StringPath field) {
        var query = jpaQueryFactory.selectFrom(user)
                .where(containsKeyword(field, cond.getKeyword()));
        query.setHint(AvailableHints.HINT_READ_ONLY, true);
        return query.fetch();
    }

    private BooleanExpression containsKeyword(StringPath field, String keyword) {
        return StringUtils.hasText(keyword) ? field.containsIgnoreCase(keyword) : null;
    }
}

Jpa 쿼리 메서드를 이용해 검색했을 때

UserService.java

    @Transactional(readOnly = true)
    public SearchUserResponseDto searchUserByKeyword(UserSearchCond userSearchCond) {
            List<User> u1 = userRepository.findAllByUsernameContaining(userSearchCond.getKeyword());
        	List<User> u2 = userRepository.findAllByNicknameContaining(userSearchCond.getKeyword());

        	List<SimpleUserInfoDto> result = mergeUserResultLists(u1, u2)
                .stream()
                .map(SimpleUserInfoDto::new)
                .toList();

        return new SearchUserResponseDto(result);
    }

문제

기존 코드는 QueryDSL을 사용하지 않아도 된다.
(간단한 검색이고 동적인 쿼리를 만들 필요가 없기 때문)
오히려 Jpa 쿼리 메서드를 사용하는 쪽이 빠르다.

시도

  1. QueryDSL의 쿼리를 하나로 합친다.

UserRepositoryQueryImpl.java

@Component
@RequiredArgsConstructor
public class UserRepositoryQueryImpl implements UserRepositoryQuery {

    private final JPAQueryFactory jpaQueryFactory;

    @Override
    public List<User> search(UserSearchCond cond) {
        var query = jpaQueryFactory.selectFrom(user)
                .where(
                        Objects.requireNonNull(containsKeyword(user.username, cond.getKeyword()))
                                .or(containsKeyword(user.nickname, cond.getKeyword()))
                );
        query.setHint(AvailableHints.HINT_READ_ONLY, true);
        return query.fetch();
    }

    private BooleanExpression containsKeyword(StringPath field, String keyword) {
        return StringUtils.hasText(keyword) ? field.containsIgnoreCase(keyword) : null;
    }
}

훨씬 빠르게 동작하는 것을 확인할 수 있다.

  1. query select문에 필요한 정보만 가져오도록 수정한다.

UserProfile.java
필요한 정보만 담을 dto

@Component
@NoArgsConstructor
@Getter
public class UserProfile {
    private Long userId;
    private String username;
    private String nickname;

    public UserProfile(Long id, String username, String nickname) {
        this.userId = id;
        this.username = username;
        this.nickname = nickname;
    }
}

더 빠르게 동작하는 것을 확인할 수 있다.
사용자를 검색하는 경우는 채팅방에 초대할 때 뿐이므로 다른 정보를 조회하지 않는 편이 낫다고 판단해 위와 같이 수정했다.

profile
일주일에 한 번

0개의 댓글