QClass
// import static com.~.~.~.~.QCustomer.*;
QCustomer customer = QCustomer.customer;
QCustomer customer = new QCustomer("myCustomer");
실제 도메인 클래스의 질의 타입으로 정적으로 사용하거나 별칭을 붙여 사용할 수 있다.
쿼리 사용해 보기
JPAQuery<?> query = new JPAQuery<Void>(entityManager);
HibernateQuery<?> query = new HibernateQuery<Void>(session);
JPA와 Hibernate API를 모두 지원하기 때문에 위와 같이 둘 다 사용 가능하고
두 클래스 모두 JPQLQuery 인터페이스의 구현체다.
QCustomer customer = QCustomer.customer;
Customer bob = queryFactory.selectFrom(customer)
.where(customer.firstName.eq("Bob"))
.fetchOne();
두 쿼리 인스턴스 모두 주로 JPAQueryFactory(혹은 Hibernate)를 통해서 생성할 수 있다.
위의 코드의 과정을 간략하게 정리하면
쿼리 팩토리로.(지정한 컬럼과 테이블의 결과를 하나만 반환하는) 쿼리를 만든다.
라고 할 수 있다.
select customer from Customer as customer
where customer.firstName = "Bob" or customer.lastName = "Wilson"
예를 들어 위의 쿼리문을 QueryDSL로 작성하면
queryFactory.selectFrom(customer)
.where(customer.firstName.eq("Bob").or(customer.lastName.eq("Wilson")));
이러한 코드를 작성할 수 있는데 실제 쿼리문과 유사한 형태에 IDE를 통한 자동완성과
문법 체크의 도움을 받아 편리하고 안정적이게 작성할 수 있게 된다.
조인
QueryDSL도 마찬가지로 JPQL에 있는 다양한 조인들을 지원한다.
select cat from Cat as cat
inner join cat.mate as mate
left outer join cat.kittens as kitten
예를 들어 위와 같이 동일한 테이블을 조인한다고 했을 때는
QCat cat = QCat.cat;
QCat mate = new QCat("mate");
QCat kitten = new QCat("kitten");
테이블의 별칭을 다르게 만들고
queryFactory.selectFrom(cat)
.innerJoin(cat.mate, mate)
.leftJoin(cat.kittens, kitten)
.fetch();
서로 같은 컬럼을 기준으로 조인을 하는 경우에는 mySQL의 using처럼
on절을 사용하지 않고도 조인을 할 수 있고
별도의 조건으로 조인을 하는 경우에는 다음과 같이 on절을 사용하면 된다.
queryFactory.selectFrom(cat)
.leftJoin(cat.kittens, kitten)
.on(kitten.bodyWeight.gt(10.0))
.fetch();
일반적인 사용
- select : 쿼리의 프로젝션을 설정한다.
- from : 쿼리의 소스를 추가한다.
- join : innerJoin, join, leftJoin, rightJoin, on을 사용하여 조인 요소를 추가할 수 있고, 조인 메서드의 첫 번째 파라미터는 원본, 두 번째 파라미터는 조인 대상이 된다.
- where : 쿼리의 필터를 추가한다. 쉼표 혹은 and 연산자를 통해 계단식으로 배열한다.
- groupBy : 그룹화 조건으로 사용할 파라미터를 추가한다.
- having : 그룹화 대상이 갖고 있어야 할 파라미터를 추가한다.
- orderBy : 정렬 기준으로 사용할 파라미터를 추가한다.
- limit, offset, restrict : 페이징의 결과를 설정한다.
정렬
QCustomer customer = QCustomer.customer;
queryFactory.selectFrom(customer)
.orderBy(customer.lastName.asc(), customer.firstName.desc())
.fetch();
orderBy 메서드의 파라미터로 정렬 기준이 될 컬럼과 정렬 방식을 전달해 주면 된다.
그룹화
queryFactory.select(customer.lastName).from(customer)
.groupBy(customer.lastName)
.fetch();
그룹화도 마찬가지로 groupBy 메서드의 파라미터로 기준을 전달해 주면 된다.
삭제절
QCustomer customer = QCustomer.customer;
queryFactory.delete(customer).execute();
queryFactory.delete(customer).where(customer.level.lt(3)).execute();
데이터의 삭제는 delete - (where) - execute 형식으로 이루어져 있는데
where 절을 생략하면 해당 테이블의 모든 데이터를 삭제하는
JPA로 치면 deleteAll 메서드에 해당한다.
execute가 호출되면서 실제 데이터가 삭제되고 삭제된 엔티티의 수를 반환한다.
JPA의 DML 절은 JPA 레벨의 영속성 전파 규칙을 따르지 않고, 2차 레벨 캐시와 연동되지 않는다.
수정절
QCustomer customer = QCustomer.customer;
queryFactory.update(customer).where(customer.name.eq("Bob"))
.set(customer.name, "Bobby")
.execute();
수정도 비슷하게 update - (where) - set - execute 형식으로 이루어져 있다.
마찬가지로, JPA의 DML 절은 JPA 레벨의 영속성 전파 규칙을 따르지 않고, 2차 레벨 캐시와 연동되지 않는다.
서브쿼리
QEmployee employee = QEmployee.employee;
QEmployee e = new QEmployee("e");
queryFactory.selectFrom(employee)
.where(employee.weeklyhours.gt(
JPAExpressions.select(e.weeklyhours.avg())
.from(employee.department.employees, e)
.where(e.manager.eq(employee.manager))))
.fetch();
서브쿼리는 정적 팩토리 메서드인 JPAExpression를 통해 사용할 수 있다.
기존 방식과 마찬가지로 select, from, where절 등을 활용하여 서브쿼리를 작성하면 된다.
쿼리 내보내기
Query jpaQuery = queryFactory.selectFrom(employee).createQuery();
// 쿼리 조정 및 최적화 등
List results = jpaQuery.getResultList();
createQuery 메서드를 사용해 쿼리문을 생성할 수 있고, 이 작업은 실제로 쿼리를 날리는 것이 아니라
쿼리문을 만들어두기만 한 상태이기 때문에 추가적인 작업을 거친 후에
getResultList 같은 메서드를 호출하여 완성된 쿼리를 실제로 날려서 데이터를 조회할 수 있다.