Result Handling

Querydsl provides two ways to customize results: FactoryExpression for row-based transformation and ResultTransformer for aggregation.

The com.querydsl.core.types.FactoryExpression interface is used for bean creation, constructor invocation, and for the creation of more complex objects. The FactoryExpression implementations of Querydsl can be accessed via the com.querydsl.core.types.Projections class.

For the com.querydsl.core.ResultTransformer interface, GroupBy is the main implementation.

Returning Multiple Columns

The default type for multi-column results is com.querydsl.core.Tuple. Tuple provides a type-safe Map-like interface to access column data from a Tuple row object.

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

This example could also have been written via the QTuple expression class:

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

Bean Population

In cases where beans need to be populated based on the results of the query, bean projections can be used:

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

When fields should be used directly instead of setters:

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

Constructor Usage

Constructor-based row transformation:

List<UserDTO> dtos = query.select(
    Projections.constructor(UserDTO.class, user.firstName, user.lastName)).fetch();

As an alternative to the generic constructor expression usage, constructors can also be annotated with the @QueryProjection annotation:

class CustomerDTO {

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

}

Then use it in the query:

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

While the example is Hibernate-specific, this feature is available in all modules.

If the type with the @QueryProjection annotation is not an annotated entity type, you can use the constructor projection like in the example. If the annotated type is an entity type, the constructor projection needs to be created via a call to the static create method of the query type:

@Entity
class Customer {

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

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

Alternatively, if code generation is not an option:

List<Customer> dtos = query
    .select(Projections.constructor(Customer.class, customer.id, customer.name))
    .from(customer).fetch();

Result Aggregation

The com.querydsl.core.group.GroupBy class provides aggregation functionality which can be used to aggregate query results in memory.

Aggregating parent-child relations:

import static com.querydsl.core.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)));

This returns a map of post ids to related comments.

Multiple result columns:

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)));

This returns a map of post ids to Group instances with access to post name and comment ids.

Group is the GroupBy equivalent to the Tuple interface.

More examples can be found in the GroupByTest class.