3.2. 결과 처리

Querydsl은 결과 처리를 커스터마이징 하기 위해 행 기반 변환을 위한 FactoryExpressions과 집합을 위한 ResultTransformer를 제공하고 있다.

com.mysema.query.types.FactoryExpression 인터페이스는 빈 생성, 생성자 호출 그리고 더 복잡한 객체를 생성하기 위해 사용된다. com.mysema.query.types.Projections 클래스를 이용해서 FactoryExpression 구현체 기능에 접근할 수 있다.

com.mysema.query.ResultTransformer 인터페이스의 주요 구현체는 GroupBy 클래스이다.

3.2.1. 다중 컬럼 리턴

Querydsl 3.0 부터 다중 컬럼 결과를 위한 기본 타입은 com.mysema.query.Tuple 이다. Tuple은 타입에 안전한 Map을 제공하고, 이를 통해 Tuple 행 객체로부터 컬럼 데이터에 접근할 수 있다.

List<Tuple> result = query.from(employee).list(employee.firstName, employee.lastName);
for (Tuple row : result) {
     System.out.println("firstName " + row.get(employee.firstName));
     System.out.println("lastName " + row.get(employee.lastName));
}}

위 예제를 QTuple 클래스를 이용하면 다음과 같이 작성할 수 있다.

List<Tuple> result = query.from(employee).list(new QTuple(employee.firstName, employee.lastName));
for (Tuple row : result) {
     System.out.println("firstName " + row.get(employee.firstName));
     System.out.println("lastName " + row.get(employee.lastName));
}}

3.2.2. 빈 생성(population)

쿼리 결과로부터 빈을 생성하고 싶다면, Bean 프로젝션을 사용하면 된다.

List<UserDTO> dtos = query.list(
    Projections.bean(UserDTO.class, user.firstName, user.lastName));

setter 메서드 대신 필드에 직접 접근해야 한다면 다음 코드를 사용하면 된다.

List<UserDTO> dtos = query.list(
    Projections.fields(UserDTO.class, user.firstName, user.lastName));

3.2.3. 생성자 사용

생성자 기반의 행 변환을 하는 방법은 다음과 같다.

List<UserDTO> dtos = query.list(
    Projections.bean(UserDTO.class, user.firstName, user.lastName));

지네릭 생성자 표현식을 사용하는 대신, QueryProjection 어노테이션을 적용한 생성자를 사용할 수도 있다.

class CustomerDTO {

  @QueryProjection
  public CustomerDTO(long id, String name) {
     ...
  }

}

그리고, 이 클래스를 다음과 같이 쿼리에서 사용 가능하다.

QCustomer customer = QCustomer.customer;
JPQLQuery query = new HibernateQuery(session);
List<CustomerDTO> dtos = query.from(customer).list(new QCustomerDTO(customer.id, customer.name));

이 예제가 Hibernate용 코드이긴 하지만, 다른 모든 모듈에서도 이 기능을 사용할 수 있다.

만약 QueryProjection 어노테이션이 적용된 타입이 엔티티(@Entity) 타입이 아니라면, 예제처럼 생성자 프로젝션을 사용할 수 있다. 하지만, 어노테이션이 적용된 타입이 엔티티(@Entity) 타입이라면 쿼리 타입의 정적 create 메서드를 실행해서 생성자 프로젝션을 만들 필요가 있다.

@Entity
class Customer {

  @QueryProjection
  public Customer(long id, String name) {
     ...
  }

}
QCustomer customer = QCustomer.customer;
JPQLQuery query = new HibernateQuery(session);
List<Customer> dtos = query.from(customer).list(QCustomer.create(customer.id, customer.name));

코드 생성을 할 수 없다면, 다음과 같이 생성자 프로젝션을 생성할 수 있다.

List<Customer> dtos = query.from(customer)
    .list(ConstructorExpression.create(Customer.class, customer.id, customer.name));

3.2.4. 결과 집합(aggregation)

com.mysema.query.group.GroupBy 클래스는 메모리에서 쿼리 결과에 대한 집합 연산을 수행하는 집합 함수를 제공한다. 다음은 사용 예이다.

부모 자식 관계에 대한 집합 연산

import static com.mysema.query.group.GroupBy.*;

Map<Integer, List<Comment>> results = query.from(post, comment)
    .where(comment.post.id.eq(post.id))
    .transform(groupBy(post.id).as(list(comment)));

이 코드는 post id와 관련된 커멘트를 매핑한다.

다중 결과 컬럼

Map<Integer, Group> results = query.from(post, comment)
    .where(comment.post.id.eq(post.id))
    .transform(groupBy(post.id).as(post.name, set(comment.id)));

이 코드는 post id와 Group을 매핑한다. Group은 post name과 comment id를 갖는다.

Group은 GroupBy에 대해 Tuple 인터페이스와 같은 역할을 한다.

더 많은 예제는 여기를 참고한다.