Select

select allows us to control the properties that are fetched at the root level of the graph. We can specify to only select some properties to fetch. The result are beans that are partially populated.

// "alias" bean that can be used in select and fetch clauses
QCustomer cust = QCustomer.alias();

List<Customer> customers =
  new QCustomer()
    // only fetch some properties of customer (partial objects)
    .select(cust.name, cust.version, cust.whenCreated)
    .name.istartsWith("Rob")
    .findList();

With the above we specify the properties we want to fetch. We still get Customer entity beans returned but they are partially populated.

The SQL generated by the above query has:

select t0.id, t0.name, t0.registered, t0.version from customer t0

Note that the @Id property is automatically included for most queries and it is automatically exludeded for distinct, findSingleAttribute and aggregation queries.

Using query beans to define the select properties provides a type safe way to define what part of the object graph to fetch.

The same query written using the query expression API is:

List<Customer> customers =
  database.find(Customer.class)
    .select("name, registered, version")
    .findList();

alias() - specify properties

Each query bean has a static alias() method that returns a query bean that we use to specify properties for fetch in select() and fetch().

// customer "alias"
QCustomer cust = QCustomer.alias();

// contact "alias"
QCustomer con = QContact.alias();

List<Customer> customers =
  new QCustomer()

    .select(cust.name, cust.version, cust.whenCreated)
    .contacts.fetch(con.firstName, con.LastName, con.email)

    .name.istartsWith("Rob")
    .findList();

In the above we have alias beans for customer and contact. We use those to specify which specific properties we want to fetch.

Note that we can assign alias() to a static final field.

private static final QCustomer CUST = QCustomer.alias();
...
List<Customer> customers =
  new QCustomer()
    .select(CUST.name)
    .name.istartsWith("Rob")
    .findList();

Alias for static imports

As an alternative to the alias() method each query bean also provides a static Alias class for the case where we rather use static imports.

For example, we can use QCustomer.Alias to specify properties of customer like the below:

List<Customer> customers =
  new QCustomer()

    .select(QCustomer.Alias.name, QCustomer.Alias.whenCreated)
    .findList();

We can then change these to use static imports to get:

import static org.domain.query.QCustomer.Alias.name;
import static org.domain.query.QCustomer.Alias.whenCreated;
...


List<Customer> customers =
  new QCustomer()

    .select(name, whenCreated) // using static imports of QCustomer.Alias
    .findList();

The limitation of using Alias like this is that if we do this for multiple query bean types (like QCustomer.Alias and QContact.Alias) then we might get a name clash. For example, if both had a property called whenCreated we can only static import one of them.

import static ...query.QCustomer.Alias.name;
import static ...query.QCustomer.Alias.version;
import static ...query.QCustomer.Alias.whenCreated;
import static ...query.QContact.Alias.firstName;
import static ...query.QContact.Alias.lastName;

// Use explicit QContact.Alias.whenCreated (as clash with QCustomer.Alias.whenCreated)

List<Customer> customers =
  new QCustomer()
    .select(name, version, whenCreated)
    .contacts.fetch(firstName, lastName, QContact.Alias.whenCreated)

    .name.istartsWith("Rob")
    .findList();

Formula

We need to use this string select clause when we want to specify a formula.

// java
List<String> names =
  new QContact()
    .select("concat(lastName,', ',firstName)")
    .lastName.startsWith("A")
    .findSingleAttributeList();
// kotlin
var names: List<String> =
  QContact()
    .select("concat(lastName,' ',firstName)")
    .lastName.startsWith("A")
    .findSingleAttributeList()
Example - Postgis ST_Distance formula

With this example we explicitly cast to a Java BigDecimal type using the ::BigDecimal cast at the end of the formula.

// given route is a Postgis geometry(linestring,4326)
// return the distance between the start and end points

BigDecimal routeDistance = query()
  .select("ST_Distance(ST_StartPoint(route), ST_EndPoint(route))::BigDecimal")
  .where()
  .idEq(tripId)
  .findSingleAttribute();

asDto

When we use a select formula we sometimes want to return the result into a plain DTO bean rather than an entity bean. We do this using .asDto(Class<D>) to turn the ORM query into a DtoQuery.

For more details refer to DTO queries.

// ContactDto is a plain bean with email and fullName properties

List<ContactDto> contacts =
  new QContact()
    .select("email, concat(lastName, ', ', firstName) as fullName")
    .lastName.startsWith("A")
    .orderBy()
       .lastName.asc()
    .setMaxRows(10)
    .asDto(ContactDto.class)
    .findList();

Aggregation

Similar to formula queries we can use the standard aggregations of SUM, MAX, MIN, AVG and COUNT in the select clause.

For more details refer to Aggregation queries.

Single aggregation queries
// java
Timestamp maxWhen  =
  new QContact()
    .select("max(whenModified)")
    .findSingleAttribute()
// kotlin
var maxWhen: Timestamp =
  QContact()
    .select("max(whenModified)")
    .findSingleAttribute()
select max(t0.when_modified) from contact t0
Group by aggregation queries

When multiple column aggregation query we use the string select clause.

List<MachineStats> result =
  new QMachineStats()
  .select("machine, date, max(rate)")
  .date.gt(LocalDate.now().minusDays(10))
  .query().having().gt("max(rate)", 4)
  .findList();

The above query produces the following sql:

select t0.machine_id, t0.date, max(t0.rate)
from d_machine_stats t0
where t0.date > ?
group by t0.machine_id, t0.date
having max(t0.rate) > ?