Cambridge Technology PartnersCommunity Documentation

Chapter 5. JPA Criteria API Support

5.1. API Usage
5.2. Joins
5.3. Boolean Operators

Beside automatic query generation, the CDI Query module also provides a DSL-like API to create JPA 2 Criteria queries. It takes advantage of the JPA 2 meta model, which helps creating type safe queries.

Tip

The JPA meta model can easily be generated with an annotation processor. Hibernate or Eclipse link provide such a processor, which can be integrated into your compile and build cycle.

Note that this criteria API is not intended to replace the standard criteria API - it's rather a utility API that should make life easier on the most common cases for a custom query. The JPA criteria API's strongest point is certainly its type safety - which comes at the cost of readability. We're trying to provide a middle way here. A less powerful API, but still typesafe and readable.

The API is centered around the Criteria class and is targeted to provide a fluent interface to write criteria queries:

public abstract class PersonDao extends AbstractEntityDao<Person, Long> {

    public List<Person> findAdultFamilyMembers(String name, Integer minAge) {
        return criteria()
                    .like(Person_.name, "%" + name + "%")
                    .gtOrEq(Person_.age, minAge)
                    .eq(Person_.validated, Boolean.TRUE)
                    .orderDesc(Person_.age)
                    .createQuery()
                    .getResultList();
    }
}    

Following comparators are supported by the API:

NameDescription
.eq(..., ...)Property value must be equal to the given value
.in(..., ..., ..., ...)Property value must be in one of the given values.
.notEq(..., ...)Negates equality
.like(..., ...)A SQL 'like' equivalent comparator. Use % on the value.
.notLike(..., ...)Negates the like value
.lt(..., ...)Property value must be less than the given value.
.ltOrEq(..., ...)Property value must be less than or equal to the given value.
.gt(..., ...)Property value must be greater than the given value.
.gtOrEq(..., ...)Property value must be greater than or equal to the given value.
.between(..., ..., ...)Property value must be between the two given values.
.isNull(...)Property must be null
.isNotNull(...)Property must be non-null
.isEmpty(...)Collection property must be empty
.isNotEmpty(...)Collection property must be non-empty

The query result can be modified with the following settings:

NameDescription
.orderAsc(...) Sorts the result ascending by the given property. Note that this can be applied to several properties
.orderDesc(...) Sorts the result descending by the given property. Note that this can be applied to several properties
.distinct() Sets distinct to true on the query.

Once all rs and query options are applied, the createQuery() method is called. This creates a JPA TypedQuery object for the DAO entity. If required, further processing can be applied here.

For simple cases, restricting on the DAO entity only works out fine, but once the data model gets more complicate, the query will have to consider relations to other entities. The CDI Module's criteria API supports therefore joins as shown in the sample below:

public abstract class PersonDao extends AbstractEntityDao<Person, Long> {

    public List<Person> findByCompanyName(String companyName) {
        return criteria()
                    .join(Person_.company
                        where(Company.class)
                            .eq(Company_.name, companyName)
                    )
                    .eq(Person_.validated, Boolean.TRUE)
                    .createQuery()
                    .getResultList();
    }
}    

Beside the inner and outer joins, also fetch joins are supported. Those are slighly simpler as seen in the next sample:

public abstract class PersonDao extends AbstractEntityDao<Person, Long> {

    public Person findBySSN(String ssn) {
        return criteria()
                    .fetch(Person_.familyMembers)
                    .eq(Person_.ssn, ssn)
                    .distinct()
                    .createQuery()
                    .getSingleResult();
    }
}    

By default, all query operators are concatenated as an and conjunction to the query. The CDI Query criteria API also allows to add groups of disjunctions. The current API restricts the number of groups to three. This is due to the restriction that the java compiler generates warnings for varargs with generics (and of course we try to keep your code warning-free). To provide more groups, the groups have to be wrapped in a collection.

public abstract class PersonDao extends AbstractEntityDao<Person, Long> {

    public List<Person> findAdults() {
        return criteria()
                    .or(
                        criteria().
                            .gtOrEq(Person_.age, 18)
                            .eq(Person_.origin, Country.SWITZERLAND),
                        criteria().
                            .gtOrEq(Person_.age, 21)
                            .eq(Person_.origin, Country.USA)
                    )
                    .createQuery()
                    .getResultList();
    }
}