Notice
Recent Posts
Recent Comments
Link
«   2024/06   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30
Tags
more
Archives
Today
Total
관리 메뉴

지우쓰 개발일기

[JPA] QueryDSL 본문

Spring Boot/JPA

[JPA] QueryDSL

jiwoo-kimm 2020. 9. 22. 03:02

본 포스팅은 자바 ORM 표준 JPA 프로그래밍 (김영한 저)을 읽고 정리한 내용입니다.

 

자바 ORM 표준 JPA 프로그래밍

에이콘 오픈 소스 프로그래밍 시리즈. 이 책은 JPA 기초 이론과 핵심 원리, 그리고 실무에 필요한 성능 최적화 방법까지 JPA에 대한 모든 것을 다룬다.

www.aladin.co.kr


QueryDSL


JPQL을 코드로 쉽고 간결하게 작성할 수 있는 프로젝트

 

 

시작
public void queryDSL() {

    EntityManager em = emf.createEntityManager();
    
    JPAQuery query = new JPAQuery(em);	// 생성자 파라미터로 EntityManager
    QMember qMember = new QMember("m");	// 생성되는 JPQL 별칭 지정
    List<Member> members =
    	query.from(qMember)
            .where(qMember.name.eq("회원1"))
            .orderBy(qMember.name.desc())
            .list(qMember);
}

 

기본 Q(쿼리 타입) 생성
QMember qMember = new QMember("m");	// 직접 지정
QMember qMember = QMember.member;	// 기본 인스턴스 사용
  • 쿼리 타입은 기본 인스턴스를 보관하고 있음

  • 같은 엔티티를 조인하거나 같은 엔티티를 서브쿼리에 사용 시 별칭을 직접 지정해야 함

  • 기본 인스턴스 사용 시 import하여 간결하게 쓸 수 있음

 

검색 조건 쿼리
  • where절에 between, contains, startsWith 등 적절한 메소드 사용

  • 중복 조건(and, or) 가능

 

결과 조회
메소드 결과가 한 건일 때 결과가 한 건 이상일 때 결과가 없을 때
uniqueResult() O NonUniqueResultException null
singleResult() O 첫 데이터 null
list() O O 빈 컬렉션
listResults() O O 빈 컬렉션

 

Paging & 정렬
  • offset(), limit()

query.from(item)
    .orderBy(item.price.desc(), item.stockQuantity.asc())
    .offset(10).limit(20)
    .list(item);
  • QueryModifiers

QueryModifiers queryModifiers = new QueryModifiers(20L, 10L);	// limit, offset

query.from(item)
    .restrict(queryModifiers)
    .list(item);
  • listResults()

SearchResults<Item> result =
    query.from(item)
    	.where(item.price.gt(10000))
        .offset(10).limit(20)
        .listResults(item);
        
long total = result.getTotal();
long limit = result.getLimit();
long offset = result.getOffset();
List<Item> results = result.getResults();

 

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

 

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

  • innerJoin(join), leftJoin, rightJoin, fullJoin

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);
  • fetch join

query.from(order)
    .innerJoin(order.member, member).fetch()
    .list(order);
  • theta join

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

 

서브 쿼리
  • 서브 쿼리 결과가 한 건

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);
  • 서브 쿼리 결과가 여러 건

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

 

프로젝션 결과
  • 프로젝션 대상이 하나

QItem item = QItem item;
List<String> results = query.from(item).list(item.name);
  • 여러 컬럼 반환과 튜플

List<Tuple> results = query.from(item).list(item.name, item.price);

for (Tuple tuple : results) {
    String name = tuple.get(item.name);
    Integer price = tuple.get(item.price);
}
  • 빈 생성

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

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

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

 

수정, 삭제 배치 쿼리
  • Persistence Context를 무시하고 DB에 직접 쿼리함

  • 수정

QItem item = QItem.item;
JPAUpdateClause updateClause = new JPAUpdateClause(em, item);
long count = updateClause.where(item.name.eq("keyword"))
    .set(item.price, item.price.add(100))
    .execute();
  • 삭제

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

 

동적 쿼리
  • com.mysema.query.BooleanBuilder

SearchParam param = new SearchParam();
param.setName("keyword");
param.setPrice(1000);

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> results = query.from(item)
    .where(builder)
    .list(item);

 

'Spring Boot > JPA' 카테고리의 다른 글

[JPA] Spring Data JPA, JpaRepository  (0) 2020.09.25
[JPA] JPQL (5)  (0) 2020.09.24
[JPA] JPQL (4)  (0) 2020.09.21
[JPA] JPQL (3)  (0) 2020.09.21
[JPA] JPQL (2)  (0) 2020.09.20
Comments