개발

QueryDSL

hp0724 2024. 2. 28. 22:32

QueryDsl : 정적 타입을 이용해서 SQL과 같은 쿼리를 생성할 수 있도록 해 주는 오픈소스 프레임워크

build gradle 에 queryDsl 관련 종속성을 설치 한후 Q 클래스를 만들수 있다.

Q클래스 : 엔티티 클래스의 메타 정보를 담고 있는 클래스로 Querydsl 은 이를 이용하여 타입 안전성을 보장하여 쿼리를 작성할수 있게 된다 .

Q클래스를 사용하면 컴파일 시점에 오류를 확인할수 있고 IDE 의 자동완성 기능을 이용해서 간편하게 작성가능하다 .

Qclass 엔티티 속성의 타입을 정확하게 표현하므로 ,타입에 맞지 않은 연산이나 비교를 시도하면 컴파일러 가 오류를 감지할수 있다.

 

관리자 검색에 대해 queryDsl로 구현해보자

public interface AdminRepositoryCustom {
	Page<ReadAdminResponse> searchAdmin(AdminSearchCondition condition, Pageable pageable);

}

 

public interface AdminRepository extends JpaRepository<Admin, Long>, AdminRepositoryCustom {

 

AdminRepository 에는 AdminRepositoryCustom 을 JPA 옆에 상속해주면 된다.

 

public class AdminRepositoryImpl implements AdminRepositoryCustom {
	private final JPAQueryFactory queryFactory;

	public AdminRepositoryImpl(EntityManager em) {
		this.queryFactory = new JPAQueryFactory(em);
	}

	@Override
	public Page<ReadAdminResponse> searchAdmin(AdminSearchCondition condition, Pageable pageable) {
		List<ReadAdminResponse> content = queryFactory
			.select(new QReadAdminResponse(
				admin.adminSeq,
				admin.adminId,
				admin.role,
				admin.nickname,
				admin.name,
				admin.depart

			))
			.from(admin)
			.where(
				adminIdEq(condition.getAdminId()),
				nicknameEq(condition.getNickname()),
				nameEq(condition.getName()),
				departEq(condition.getDepart())
			)
			.offset(pageable.getOffset())
			.limit(pageable.getPageSize())
			.fetch();

		JPAQuery<Admin> countQuery = queryFactory
			.select(admin)
			.from(admin)
			.where(
				adminIdEq(condition.getAdminId()),
				nicknameEq(condition.getNickname()),
				nicknameEq(condition.getName()),
				departEq(condition.getDepart())
			);
		return PageableExecutionUtils.getPage(content, pageable, countQuery::fetchCount);

	}

	private BooleanExpression adminIdEq(String adminId) {
		return hasText(adminId) ? admin.adminId.eq(adminId) : null;
	}

	private BooleanExpression nameEq(String name) {
		return hasText(name) ? admin.name.eq(name) : null;
	}

	private BooleanExpression departEq(String depart) {
		return hasText(depart) ? admin.depart.eq(depart) : null;
	}

	private BooleanExpression nicknameEq(String nickname) {
		return hasText(nickname) ? admin.nickname.eq(nickname) : null;
	}
}

 

 

AdminRepositoryImpl 에 대해 자세히 살펴보자면

queryFactory
    .select(new QReadAdminResponse(
        admin.adminSeq,
        admin.adminId,
        admin.role,
        admin.nickname,
        admin.name,
        admin.depart

    ))

 

JPQL 에서는 프로젝션 관련해서 길게 작성을 했지만 QueryDsl에서는 DTO 부분에 @QueryProjection 을 사용하여서 예쁘게 빼올수 있다.

 

JPAQuery<Admin> countQuery = queryFactory
			.select(admin)
			.from(admin)
			.where(
				adminIdEq(condition.getAdminId()),
				nicknameEq(condition.getNickname()),
				nicknameEq(condition.getName()),
				departEq(condition.getDepart())
			);

 

	private BooleanExpression adminIdEq(String adminId) {
		return hasText(adminId) ? admin.adminId.eq(adminId) : null;
	}

 

where 절의 경우에는 다음과 같은 private 메서드를 이용해서 조건 처리를 해준다.

JPQL 의 경우에는 repository 에서 해당 필터링에 대해서 메서드를 하나씩 다 만들어줘야 하지만

queryDsl의 경우에는 AdminSearchCondition DTO 부분에 필터링 내용을 추가하고

AdminRepositoryImpl 에 관련 내용을 추가해주면 된다.