
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() 메소드
- 결과는 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));
}
빈 생성
- 프로퍼티 접근
- 필드 직접 접근
- 생성자 사용
1. 프로퍼티 접근
- Projections.bean() 메소드를 이용하여 setter 수정자로 값을 채우자!
- 쿼리 결과와 매핑할 프로퍼티 이름이 다르다면 as()를 이용한다.
- 코드
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)
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() |