QueryDSL에 대해 알아보자

ParkIsComing·2023년 1월 31일

Spring

목록 보기
5/21

QueryDSL 사용하기

  • JPAQuery 객체를 생성하여 사용한다.
    • 먼저, 엔티티매니저를 생성자에 넘겨준다.
    • 사용할 쿼리 타입(Q)을 생성하는데 생선자에는 별칠을 준다.

기본 Q 생성

  • 쿼리타입(Q)는 기본적으로 기본 인스턴스를 가지나 같은 엔티티를 조인하거나 같은 엔티티를 서브쿼리에 사용하면 별칭이 중복되므로 별칭을 직접 지정해 사용한다.
QMember qMember = new QMember("m") //직접 별칭 지정
QMember qMember = QMember.member; // 기본 인스턴스
  • 코드 예시
import static jpabook.jpashop.domain.QMember.mebmer;

public void basic(){
	EntitiyManager em = emf.createEntityManager();
    
    JPAQuery query = new JPAQuery(em);
    List<Member> members = query.from(member)
    							.where(member.name.eq("회원1"))
                                .orderBy(member.name.desc())
                                .list(member);
}

결과 조회

  • uniqueResult()
    • 조회 결과가 한 건일 때 사용
    • 조회 결과가 없으면 null 리턴
    • 결과가 하나 이상이면 NonUniqueResultException 예외 발생
  • singleResult()
    • uniqueResult()와 같게 조회 결과가 한 건일 때 사용
    • 결과가 하나 이상이면 null이 아니라 처음 데이터를 리턴
  • list()
    • 결과가 한 건 이상일 때 사용
    • 결과가 없으면 빈 컬렉션 리턴

페이징과 정렬

정렬

  • 정렬은 orderBy
  • asc()desc()를 사용

페이징

  • 페이징은 restrict() 메소드
    • QueryModifiers를 파리미터로 사용
  • 결과는 list() 대신 listResults()를 사용
    • SearchResults를 반환하는데 해당 객체에서 전체 데이터의 수를 조회할 수 있음.

그룹

query.from(item)
	 .groupBy(item.price)
     .having(item.price.gt(1000))
     .list(item);

조인

  • join(조인대상, 별칭으로 사용할 쿼리 타입) 형식으로 사용하면 된다.
  • JPQL의 on과 성능 최적화를 위한 fetch 조인도 사용 가능.
  • innterJoin(join), leftJoin, rightJoin, fullJoin을 사용할 수 있다.

기본 조인

QOrder order = QOrder.order;
QMember member = QMember.member;
QOrderItem orderItem = QorderItem.orderItem;

query.from(odrer)
     .join(order.member, memb

조인 on 사용

query.from(order)
	 .leftJoin(order.orderItems, orderItem)
     .on(orderItem.count.gt(2))
     .list(order);

fetch 조인 사용

query.from(order)
     .innerJoin(order.member, member).fetch()
     .leftJoin(order.orderItem, orderItem).fetch()
     .list(order);

from절에 여러 조건을 사용하는 조인

QOrder order = QOrder.order;
QMember member = QMember.member;

query.from(order,member)
	 .wherer(order.member.eq(member))
     .list(order);

서브 쿼리

  • JPASubQuery를 생성해서 사용
  • 결과가 하나이면 unique(), 여러 건이면 list()를 사용
  • 코드

1. 서브 쿼리가 한 건일 때

QItem item = QItem.item;
QItem itemSub = new QItem("itemSub");

query.from(item)
	 .where(item.price.eq(new JPASubQuery().from(itemSub)
                                           .unique(itemSub.price.max())))
     .list(item);

2. 서브 쿼리가 여러 건일 때

QItem item = QItem.item;
QItem itemSub = new QItem("itemSub");

query.from(item)
     .where(item.in(new JPASubQuery().from(itemSub)
      								 .where(item.name.eq(itemSub.name))
                                     .list(itemSub)
      ))
     .list(item)
     
 

프로젝션 결과 반환

프로젝션이란? : select절에 조회 대상을 지정하는 것

프로젝션 대상이 하나

QItem item = QItem.item;
List<String> result = query.from(item).list(item.name);

여러 컬럼 반환과 튜플

  • 프로젝션 대상으로 여러 컬럼을 선택하면 Tuple로 프로젝션 된다.
  • 조회할 때는 tuple.get() 메소드 활용
  • 코드
QItem item = QItem.item;
List<Tuple> result = query.from(item).list(item.name, item.price);
List<Tuple> result = query.from(item).list(new QTuple(item.name, item.price)); //위와 같은 결과

for(Tuple tuple : result){
	System.out.println("name= " + tuple.get(item.name));
    System.out.println("price= " + tuple.get(item.price));
}

빈 생성

  • 결과를 객체(DTO)로 받고 싶을 때
  • 방법
  1. 프로퍼티 접근
  2. 필드 직접 접근
  3. 생성자 사용

1. 프로퍼티 접근

  • Projections.bean() 메소드를 이용하여 setter 수정자로 값을 채우자!
  • 쿼리 결과와 매핑할 프로퍼티 이름이 다르다면 as()를 이용한다.
  • 코드
//쿼리 결과는 name이지만 .as를 이용하여 ItemDTO의 username 프로퍼티로 설정
QItem item = QItem.item;
List<ItemDTO> result = query.from(item).list(
	Projections.bean(ItemDTO.class, item.name.as("username"), item.price));

2. 필드 직접 접근

  • Projections.fields() 메소드를 사용하면 필드에 직접 접근해 값을 채울 수 있다.
  • 필드를 private으로 설정해도 동작함.
  • 코드
QItem item = QItem.item;
List<ItemDTO> result = query.from(item).list(
	Projections.fields(ItemDTO.class, item.name.as("username"), item.price));

3. 생성자 사용

  • 코드
QItem item = QItem.item;
List<ItemDTO> result = query.from(item).list(
	Projections.constructor(ItemDTO.class, item.name, item.price));

수정, 삭제 배치 쿼리

수정 배치 쿼리

  • JPAUpdateClause를 사용한다.
  • 코드
QItem item = QItem.item;
JPAUpdateClause updateClause = new JPAUpdateClause(em,item);
long count = updateClause.where(item.name.eq("책상"))
						 .set(item.price, item.price.add(100))
                         .execute();

삭제 배치 쿼리

  • JPADeleteClause를 사용한다.
  • 코드
QItem item = QItem.item;
JPADeleteClause deleteClause = new JPADeleteClause(em, item);
long count = deleteClause.where(item.name.eq("책상"))
						 .execute();

동적 쿼리

  • BooleanBuilder 를 사용하여 조건에 따른 동적 쿼리를 생성
SearchParam param = new SearchParam();
params.setName("책상");
params.setPrice(100000);

QItem item = QItem.item;

BooleanBuilder builder = new BooleanBuilder();
if(StringUtils.hasText(param.getName())){
	builder.and(item.name.contains(param.getName()));
}
if(param.getPrice!= null){
	builder.and(item.price.gt(param.getPrice()));
}

List<Item> result = query.from(item).where(builder).list(item);
   

메소드 위임

검색 조건 정의

  • 정적 메소드를 만들고 @QueryDelegate을 이용해 속성으로 기능을 적용할 엔티티를 지정
  • 정적 메소드의 첫 파라미터로는 대상 엔티티의 쿼리타입(Q),
    나머지 파라미터로는 필요한 파라미터를 지정
public class ItemExpression{
	@QueryDelegate(Item.class) //기능을 적용할 엔티티를 Item으로 설정
    public static BooleanExpression isExpensive(QItem item, Integer price){
    	return item.price.gt(price);
    }
}

함수 정리

Expression() 메소드

함수명JPQL
isNull()
isNotNull()
in()IN

조건 함수

함수명
and()
or()
not()
equal(), notEqual()
lt(), lessThan()
le(), LessThanOrEqualTo()
gt(), greaterThan()
ge(), greaterThanOrEqualTo()
between()
like(), notLike()
isTrue(), isFalse()
in(), not(in())
exists(), not(exists())
isNull(), isNotNull()
isEmpty(), isNotEmpty()
isMember(), isNotMember()

스칼라와 기타 함수

함수명함수명
sum()length()
neg(),diff()locate()
prod()concat()
quot() -> /upper()
all()lower()
any()substring()
some()trim()
abs()currentDate()
sqrt()currentTime()
mod()currentTimeStamp()
size()

집합 함수 & 분기 함수

함수명
avg()
max(), greatest()
min(), least()
sum(), sumAsLong(), sumAsDouble()
count()
countDistinct()
nullif()
coalesce()
selectCase()

0개의 댓글