응애개발자
article thumbnail
728x90

4. QueryDSL

Criteria는 너무 어렵고 복잡하다. 쿼리를 문자가 아닌 코드로 작성해도, 쉽고 간결하며 그 모양도 쿼리와 비슷하게 개발할 수 있는 프로젝트가 바로 QueryDSL이다. QueryDSL도 Criteria처럼 JPQL 빌더 역할을 하는데 JPA Criteria를 대체할 수 있다.

QueryDSL은 오픈소스 프로젝트이고 지금은 JPA, JDO, JDBC, Lucene, Hibernate Search, 몽고 DB, 자바 컬렉션 등을 다양하게 지원한다. 참고로 QueryDSL은 데이터를 조회하는데 기능이 특화되어 있다.

 

 

QueryDSL 설정

- 필요 라이브러리

pom.xml 추가

  • querydsl-jpa : QueryDSL JPA 라이브러리
  • querydsl-apt: 쿼리 타입(Q)을 생성할 때 필요한 라이브러리

- 환경설정

QueryDSL을 사용하려면 Criteria의 메타 모델처럼 엔티티를 기반으로 쿼리 타입이라는 쿼리용 클래스를 생성해야 한다.

쿼리 타입 생성용 pom.xml 추가

콘솔에서 mvn compile을 입력하면 outputDirectory에 지정한 target/generated-sources 위치에 QMember.java처럼 Q로 시작하는 쿼리 타입들이 생성된다. 이제 target/generated-sources를 소스 경로에 추가하면 된다.

 

시작

QueryDSL 시작

QueryDSL을 사용하려면 우선 com.mysema.query.jpa.impl.JPAQuery객체를 생성해야 하는데 이때 엔티티 매니저를 생성자에 넘겨준다. 

다음으로 사용할 쿼리 타입(Q)을 생성하는데 생성자에는 별칭을 주면 된다. 이 별칭을 JPQL에서 별칭으로 사용한다. 그다음 from, where, orderBy등등 코드만 보아도 이해가 가능하다.

- 기본 Q 생성

쿼리 타입(Q)은 사용하기 편리하도록 밑과 같이 기본 인스턴스를 보관하고 있지만 같은 엔티티를 조인하거나 같은 엔티티를 서브쿼리에 사용하면 같은 별칭이 사용되므로 이때는 별칭을 직접 지정해서 사용해야 한다.

Member 쿼리 타입
쿼리 타입 사용
impory static 활용

 

검색 조건 쿼리

QueryDSL 기본 쿼리 기능
실행된 SQL

QueryDSL의 where 절에는 and, or 사용할 수 있다. 

 

where() 에서 사용되는 메서드

이런것 처럼 대부분의 메서드를 명시적으로 제공한다.

 

결과 조회

쿼리 작성이 끝나고 결과 조회 메서드를 호출하면 실제 DB를 조회한다. 보통 uniqueResult() 나 list() 를 사용하고 파라미터로 프로젝션 대상을 넘겨준다. 결과 조회 API는 com.mysema.query.Projectable에 정의되어 있다.

 

- 대표적인 결과 조회 메서드

  • uniqueResult() : 조회 결과가 한 건일 때 사용한다. 결과가 없으면 null을 반환 , 하나 이상이면 com.mysema.query.NonUniqueResultException 예외가 발생
  • singleResult() : 위와 같지만 결과가 하나 이상이면 처음 데이터를 반환한다.
  • list() : 결과가 하나 이상일때 사용한다. 없으면 빈 컬렉션을 반환한다.

페이징 정렬

페이징과 정렬

정렬은 orderBy를 사용하는데 쿼리 타입이 제공하는 asc(), desc()를 사용한다. 페이징은 offset과 limit을 적절히 조합해서 사용하면 된다.

 

페이징은 밑처럼 restrict() 메서드에 com.mysema.query.QueryModifiers를 파라미터로 사용해도 된다.

페이징과 정렬 QueryModifiers 사용

실제 페이징 처리를 하려면 검색된 전체 데이터 수를 알아야 한다. 이때는 list() 대신 listResults()를 사용한다.

페이징과 정렬 listResults() 사용

listResults()를 사용하면 전체 데이터 조회를 위한 count쿼리를 한번 더 실행한다. 그리고 SearchResults를 반환하는데 이객체에서 전체 데이터 수를 조회할 수 있다.

 

그룹

groupBy를 사용하고 그룹화된 결과를 제한하려면 having 사용

groupBy() 사용

 

조인

조인은 innerJoin, leftJoin,rightJoin,fullJoin을 사용할 수 있고 on과 성능 최적화를 위한 fetch도 사용할 수 있다.

첫 번째 파라미터에 조인 대상을 지정하고, 두 번째 파라미터에 별칭으로 사용할 쿼리 타입을 지정하면 된다.

기본 조인
조인 on 사용
페치 조인 사용
from 절에 여러 조건 사용(세타 조인 방법)

 

서브 쿼리

서브 쿼리 결과가 하나면 unique(), 여러 건이면 list()를 사용할 수 있다.

서브 쿼리 예제 - 한건
여러 건

 

프로젝션과 결과 반환

select 절에 조회 대상을 지정하는 것을 프로젝션이라 한다.

 

- 프로젝션 대상이 하나

프로젝션 대상이 하나

- 여러 컬럼 반환과 튜플

프로젝션 대상으로 여러 필드를 선택하면 QueryDSL은 기본으로 com.mysema.query.Tuple이라는 Map과 비슷한 내부 타입을 사용한다. 조회 결과는 tuple.get() 메서드에 조회한 쿼리 타입을 지정하면 된다.

튜플 사용 예제

- 빈 생성

쿼리 결과를 엔티티가 아닌 특정 객체로 받고 싶으면 빈 생성 기능을 사용한다.

  • 프로퍼티 접근
  • 필드 직접 접근
  • 생성자 사용

원하는 방법을 지정하기 위해 com.mysema.query.types.Projections를 사용하면 된다.

예제 ItemDTO
프로퍼티 접근(Setter)

위의 bean()메서드는 수정자를 사용해서 채운다. 위를 보면 쿼리 결과는 name인데 ItemDTO는 username 프로퍼티를 가지고 있다. 이처럼 쿼리 결과와 매핑할 프로퍼티 이름이 다르면 as를 사용해서 별칭을 주면 된다.

 

필드 직접 접근

위처럼 Projections.fields()를 사용하면 필드에 직접 접근해서 값을 채워준다. 필드를 private로 설정해도 동작한다.

생성자 사용

- DISTINCT

distinct는 다음과 같이 사용한다.

 

수정, 삭제 배치 쿼리

JPQL 배치 쿼리와 같이 영속성 컨텍스트를 무시하고 DB를 직접 쿼리한다는 점에 유의하자. JPQL 배치 쿼리는 뒤에서 다룬다.

수정 배치 쿼리

수정 배치 쿼리는 com.mysema.query.jpa.impl.JPAUpdateClause를 사용한다. 위는 상품의 가격을 100원 증가시킨다.

삭제 배치 쿼리

삭제 배치 쿼리는 ~.JPADeleteClause를 사용한다. 위는 이름이 같은 상품을 삭제한다.

 

동적 쿼리

com.mysema.query.BooleanBuilder를 사용하면 특정 조건에 따른 동적 쿼리를 편리하게 생성할 수 있다.

동적 쿼리 예제

위는 상품 이름과 가격 유무에 따라 동적으로 쿼리를 생성한다.

 

메서드 위임

이 기능을 사용하면 쿼리 타입에 검색 조건을 직접 정의할 수 있다.

검색 조건 정의

메서드 위임 기능을 사용하려면 위와 같이 정적 메서드를 만들고(static) @com.mysema.query.annotations.QueryDelegate 어노테이션에 속성으로 이 기능을 적용할 엔티티를 지정한다. 정적 메서드의 첫 번째 파라미터에는 대상 엔티티의 쿼리 타입(Q)을 지정하고 나머지는 필요한 파라미터를 정의한다.

쿼리 타입에 생성된 결과

생성된 쿼리 타입을 보면 기능이 추가된 것을 확인할 수 있다.

이제 메서드 위임 기능을 사용해보자.

필요하다면 String, Date 같은 자바 기본 내장 타입에도 메서드 위임 기능을 사용할 수 있다.

profile

응애개발자

@Eungae-D

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!