JPA

JPA - QueryDSL 2

심나라 2022. 11. 21. 15:42
728x90

 

1. 조인

조인(Join)에는 innerJoin(join), leftJoin, rightJoin, fullJoin을 사용할 수 있고, 추가로 JPQL의 on과 성능 최적화를 위

한 fetch 조인도 사용할 수 있습니다.

join 기본 형식

join(조인 대상, 별칭으로 사용할 쿼리 타입)

기본 조인 방법

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

query.from(order)
    .join(order.member, member)
    .leftJoin(order.orderItems, orderItem)
    .list(order);


조인 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.orderItems, orderItem).fetch()
    .list(order);


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

QOrder order = QOrder.order;
QMember member = QMember.member;
query.from(order, member)
     .where(order.member.eq(member))
     .list(order);

 

 

2. 서브 쿼리

com.mysema.query.jpa.JPASubQuery를 생성해서 사용합니다. 서브 쿼리의 결과가 하나면 unique(), 여러 건이면 list()

를 사용 할 수 있습니다.

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);

 

 

3. 프로젝션과 결과 반환

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

1) 프로젝션 대상이 하나 인경우

Qitem item = Qitem.item;
List<String> result = query.from(item).list(item.name);
for (String name : result) {
    System.out.println("name = " + name);
}

 

2) 여러 컬럼 반환과 튜플

프로젝션 대상으로 여러 필드를 선택하면 QueryDSL은 기본으로 com.mysema.query.Tuple이라는 Map과 비슷한 내부 타

입을 사용 합니다.

조회 결과는 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));
}


3) 빈 생성

쿼리 결과를 엔티티가 아닌 특정 객체로 받고 싶다면 빈 생성 기능을 사용합니다. QueryDSL은 객체를 생성하는 다양한 방

법을 제공합니다.

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

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

@Getter @Setter
public class ItemDTO {
    private String username;
    private int price;

    public ItemDTO() {}

    public ItemDTO(String username, int price) {
        this.username = username;
        this.price = price;
    }
}

 

(1) 프로퍼티 접근(Setter)

QItem item = QItem.item;
List<ItemDTO> result = query.from(item).list(
    Projections.bean(ItemDTO.class, item.name.as("username"), item.price));

Projections.bean() 메소드는 수정자(Setter)를 사용해서 값을 채웁니다.
예제에서 쿼리 결과는 name인데 ItemDTO는 username 프로퍼티를 가지고 있습니다. 이처럼 쿼리 결과와 매핑할 프로퍼티 이름이 다르면 as를 사용해서 별칭을 주면됩니다.

(2) 필드 직접 접근

QItem item = QItem.item;
List<ItemDTO> result = query.from(item).list(
    Projections.fields(ItemDTO.class, item.name.as("username"), item.price));

Projections.fields() 메소드를 사용하면 필드에 직접 접근해서 값을 세팅할 수 있습니다. 필드를 private로 설정해도 값을 세팅 할 수 있습니다.

(3) 생성자 사용

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

Projections.constructor() 메소드는 생성자를 사용합니다. 지정한 프로젝션과 파라미터 순서가 같은 생성자가 필요합니다.


DISTINCT

query.distinct().from(item)....

 

 

4. 수정, 삭제 배치 쿼리

QueryDSL도 수정, 삭제 같은 배치 쿼리를 지원합니다. 

영속성 컨텍스트를 무시하고 데이터베이스를 직접 쿼리 합니다.

1) 수정 배치 쿼리

com.mysema.query.jpa.impl.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();

 

2) 삭제 배치 쿼리

com.mysema.query.jpa.impl.JPADeleteClause 를 사용합니다.

QItem item = QItem.item;
JPADeleteClause deleteClause = new JPADeleteClause(em, item);
long count = deleteClause.where(item.name.eq("상품평"))
    .execute();

 

 

5. 동적 쿼리

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

SearchParam param = new SearchParam();
param.setName("시골개발자");
param.setPrice(10000);

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);

 

 

[참고자료]

  • 자바 ORM 표준 JPA 프로그래밍 (김영한 지음)
728x90