- 참조: 자바 ORM 표준 JPA 프로그래밍
- 검색조건
QueryDSL 에서 검색조건 where 절의 기본 사용법을 알아보자.
JPAQueryFactory query = new JPAQueryFactory(em);
QMember qMember = QMember.member;
List<Member> members = query.select(qMember)
.from(qMember)
.where(qMember.age.gt(20)
.and(qMember.address.city.eq("City#2")))
.orderBy(qMember.age.desc())
.fetch();
members.forEach(member -> {
System.out.println("Member age: " + member.getAge());
});
QueryDSL 환경 설정 편에서 사용하던 코드이다. and 조건을 추가하는 부분을 자세히 살펴보자. Java8 에서 제공하는 Predicate 처럼 and 메소드를 체이닝 메소드 형식으로 이어붙일 수 있어서 간편하다.
Hibernate:
/* select
member1
from
Member member1
where
member1.age > ?1
and member1.address.city = ?2
order by
member1.age desc */ select
member0_.member_id as member_i1_6_,
member0_.insert_datetime as insert_d2_6_,
member0_.update_datetime as update_d3_6_,
member0_.city as city4_6_,
member0_.street as street5_6_,
member0_.zipcode as zipcode6_6_,
member0_.age as age7_6_,
member0_.name as name8_6_
from
member member0_
where
member0_.age>?
and member0_.city=?
order by
member0_.age desc
Member age: 32
Member age: 22
실행해서 생성되는 JPQL 을 보면 위와 같이 age 와 city 에 대한 조건이 잘 형성됨을 알 수 있다. 물론 위의 코드 처럼 간단한 and 뿐만 아니라 like 나 between 도 제공한다.
List<Member> members = query.select(qMember)
.from(qMember)
.where(qMember.age.between(10, 50)
.and(qMember.name.startsWith("Name"))
.and(qMember.address.city.eq("City#2")))
.orderBy(qMember.age.desc())
.fetch();
startWith 와 between 연산을 사용해서 생성된 쿼리는 아래와 같다.
Hibernate:
/* select
member1
from
Member member1
where
member1.age between ?1 and ?2
and member1.name like ?3 escape '!'
and member1.address.city = ?4
order by
member1.age desc */ select
member0_.member_id as member_i1_6_,
member0_.insert_datetime as insert_d2_6_,
member0_.update_datetime as update_d3_6_,
member0_.city as city4_6_,
member0_.street as street5_6_,
member0_.zipcode as zipcode6_6_,
member0_.age as age7_6_,
member0_.name as name8_6_
from
member member0_
where
(
member0_.age between ? and ?
)
and (
member0_.name like ? escape '!'
)
and member0_.city=?
order by
member0_.age desc
- 결과조회
여태까지 예제에서 결과를 조회할 때 fetch() 만 사용했지만 QueryDSL 역시 다양한 메소드를 조회한다.
Mybatis 사용시 selectOne() 처럼 1 건의 QueryDSL 도 1 건의 데이터만 반환하는 fetchOne() 메소드를 지원한다.
Member member = query.select(qMember)
.from(qMember)
.where(qMember.name.startsWith("Name"))
.fetchOne();
.............
Exception in thread "main" com.querydsl.core.NonUniqueResultException: Only one result is allowed for fetchOne calls
at com.querydsl.jpa.impl.AbstractJPAQuery.fetchOne(AbstractJPAQuery.java:258)
at com.example.demo.JpaApplication.find(JpaApplication.java:90)
at com.example.demo.JpaApplication.main(JpaApplication.java:32)
만약 1 건의 조회를 만족 시키지 못하면 위와 같이 Exception 을 내뱉는다. 2 건 이상이 검색되더라도 Exception 을 내지 않고 1 번째 데이터를 반환받고 싶다면 fetchFirst() 메소드를 사용하면 된다.
- 페이징과 정렬
페이징은 offset() 과 limit() 으로 인자를 주어 할 수 있다. limit 과 offset 으로 페이징을 지원하는 DB 에서 SQL 을 사용해봤다면 익숙할것이다. limit 으로는 한번에 몇 건의 데이터를 가져올 지 설정하며, offset 으로는 몇 건을 건너뛸지 정한다.
JPAQueryFactory query = new JPAQueryFactory(em);
QMember qMember = QMember.member;
List<Member> members = query.select(qMember)
.from(qMember)
.where(qMember.name.startsWith("Name"))
.orderBy(qMember.name.asc())
.offset(2).limit(2)
.fetch();
members.forEach(member -> {
System.out.println("Member name: " + member.getName());
});
이름순으로 정렬하여 2 개의 데이터를 건너뒤고 2 개의 데이터를 조회해보자. 우리가 기대한 결과는 Name#1~4 중 1~2를 건너뛰고 3~4를 조회하는것이다.
Hibernate:
/* select
member1
from
Member member1
where
member1.name like ?1 escape '!'
order by
member1.name asc */ select
member0_.member_id as member_i1_6_,
member0_.insert_datetime as insert_d2_6_,
member0_.update_datetime as update_d3_6_,
member0_.city as city4_6_,
member0_.street as street5_6_,
member0_.zipcode as zipcode6_6_,
member0_.age as age7_6_,
member0_.name as name8_6_
from
member member0_
where
member0_.name like ? escape '!'
order by
member0_.name asc limit ? offset ?
Member Name: Name#3
Member Name: Name#4
실행해보면 우리의 예상대로 잘 동작한다.
단순히 데이터를 조회한다면 위와 같이 사용하면 된다. 하지만 시스템을 만들다보면 보통의 경우엔 전체 데이터가 몇 건인지를 알고 싶다는 요구사항이 많다. 페이징 정보를 알고 싶다면 fetchResults() 를 사용하면 된다.
QueryResults<Member> memberResults = query.select(qMember)
.from(qMember)
.where(qMember.name.startsWith("Name"))
.orderBy(qMember.name.asc())
.offset(2).limit(2)
.fetchResults();
long total = memberResults.getTotal();
long limit = memberResults.getLimit();
long offset = memberResults.getOffset();
List<Member> members = memberResults.getResults();
System.out.println("total: " + total +
", limit: " + limit +
", offset: " + offset);
members.forEach(member -> {
System.out.println("Name: " + member.getName());
});
fetchResults 는 QueryResults 형을 반환한다. QueryResults 에는 총 건수 total 과 사용한 offset, limit 정보가 들어있다. 그리고 조회된 데이터정보도 있다.
Hibernate:
/* select
count(member1)
from
Member member1
where
member1.name like ?1 escape '!' */ select
count(member0_.member_id) as col_0_0_
from
member member0_
where
member0_.name like ? escape '!'
Hibernate:
/* select
member1
from
Member member1
where
member1.name like ?1 escape '!'
order by
member1.name asc */ select
member0_.member_id as member_i1_6_,
member0_.insert_datetime as insert_d2_6_,
member0_.update_datetime as update_d3_6_,
member0_.city as city4_6_,
member0_.street as street5_6_,
member0_.zipcode as zipcode6_6_,
member0_.age as age7_6_,
member0_.name as name8_6_
from
member member0_
where
member0_.name like ? escape '!'
order by
member0_.name asc limit ? offset ?
total: 4, limit: 2, offset: 2
Name: Name#3
Name: Name#4
코드를 실행해보면 조회 쿼리가 2번 실행된다. 하나는 Member 를 조회하기 위한 쿼리이며 다른 하나는 조건에 맞는 총 건수를 알기 위한 count() 쿼리가 수행된다.
- 그룹
QueryDSL 에서 Group By 사용법을 알아보자.
List<Tuple> members = query.select(qMember.address.city, qMember.count())
.from(qMember)
.where(qMember.name.startsWith("Name"))
.groupBy(qMember.address.city)
.fetch();
members.forEach(member -> {
System.out.println("City: " + member.get(qMember.address.city) +
", count: " + member.get(qMember.count()));
});
groupBy 사용법은 Criteria 와 비교해서 크게 특별한 점은 없다. 대신 Criteria 에서는 CriteriaBuilder 로 count 표현을 얻어야 하는것과는 달리 Q-Type 에서 qMember 에 대해 count() 를 곧바로 지원해준다.
'Framework and Tool > JPA' 카테고리의 다른 글
JPA - 객체지향 쿼리 언어 - QueryDSL 프로젝션 (0) | 2021.08.21 |
---|---|
JPA - 객체지향 쿼리 언어 - QueryDSL Join, 서브쿼리 (0) | 2021.08.18 |
JPA - 객체지향 쿼리 언어 - QueryDSL 시작 (0) | 2021.08.16 |
JPA - 객체지향 쿼리 언어 - Criteria 메타 모델 API (0) | 2021.08.13 |
JPA - 객체지향 쿼리 언어 - Criteria 파라미터, 네이티브 함수 (0) | 2021.08.12 |
댓글