Close

JPA Criteria API - Downcasting with CriteriaBuilder.treat() methods

[Last Updated: Dec 12, 2018]

In Criteria API, downcasting an entity to a subclass entity can be performed by using one of the CriteriaBuilder.treat() methods:

package javax.persistence.criteria;
 .....
public interface CriteriaBuilder {
 .....
    // methods for downcasting:
    <X, T, V extends T> Join<X, V> treat(Join<X, T> join, Class<V> type);
    <X, T, E extends T> CollectionJoin<X, E> treat(CollectionJoin<X, T> join, Class<E> type);
    <X, T, E extends T> SetJoin<X, E> treat(SetJoin<X, T> join, Class<E> type);
    <X, T, E extends T> ListJoin<X, E> treat(ListJoin<X, T> join, Class<E> type);
    <X, K, T, V extends T> MapJoin<X, K, V> treat(MapJoin<X, K, T> join, Class<V> type);
    <X, T extends X> Path<T> treat(Path<X> path, Class<T> type);
    <X, T extends X> Root<T> treat(Root<X> root, Class<T> type);
}

Quick Example (where PartTimeEmployee and ContractEmployee are the subclasses of Employee):

    CriteriaQuery<Employee> query = criteriaBuilder.createQuery(Employee.class);
    Root<Employee> employee = query.from(Employee.class);
    Root<PartTimeEmployee> partTimeEmployee = criteriaBuilder.treat(employee, PartTimeEmployee.class);
    Root<ContractEmployee> contractEmployee = criteriaBuilder.treat(employee, ContractEmployee.class);
    query.select(partTimeEmployee)
         .distinct(true)
         .where(criteriaBuilder.or(
                     criteriaBuilder.greaterThan(partTimeEmployee.get(PartTimeEmployee_.weeklySalary), 1000),
                     criteriaBuilder.lessThan(contractEmployee.get(ContractEmployee_.hourlyRate), 75)
          ));
    TypedQuery<Employee> typedQuery = entityManager.createQuery(query);
    List<Employee> employees = typedQuery.getResultList();

Example

Entities

@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@Entity
@DiscriminatorColumn(name = "EMP_TYPE")
public class Employee {
    @Id
    @GeneratedValue
    private long id;
    private String name;
    .............
}
@Entity
@DiscriminatorValue("F")
public class FullTimeEmployee extends Employee {
    private int annualSalary;
    .............
}
@Entity
@DiscriminatorValue("P")
public class PartTimeEmployee extends Employee {
    private int weeklySalary;
    .............
}
@Entity
@DiscriminatorValue("C")
public class ContractEmployee extends Employee {
    private int hourlyRate;
    .............
}
@Entity
public class Project {
    @Id
    @GeneratedValue
    private long id;
    private String name;
    @OneToMany(cascade = CascadeType.ALL)
    private List<Employee> employees;
    @OneToOne(cascade = CascadeType.ALL)
    private Employee supervisor;
    .............
}

Using CriteriaBuilder.treat() methods

public class ExampleMain {

    public static void main(String[] args) throws Exception {
        EntityManagerFactory emf =
                Persistence.createEntityManagerFactory("example-unit");
        try {
            persistEntities(emf);
            getByPartTimeEmployeesWeeklySalary(emf);
            getByPartTimeAndContractEmployeesBySalary(emf);
            getProjectByFullTimeEmployeesSalary(emf);
            getProjectBySupervisorPartTimeSalary(emf);
        } finally {
            emf.close();
        }
    }

    //using method: Root<T> treat(Root<X> root, Class<T> type);
    private static void getByPartTimeEmployeesWeeklySalary(EntityManagerFactory emf) {
        System.out.println("-- employees where PartTimeEmployee.weeklySalary < 1000 --");
        EntityManager entityManager = emf.createEntityManager();
        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
        CriteriaQuery<Employee> query = criteriaBuilder.createQuery(Employee.class);
        Root<Employee> employee = query.from(Employee.class);
        Root<PartTimeEmployee> partTimeEmployee = criteriaBuilder.treat(employee, PartTimeEmployee.class);
        query.select(partTimeEmployee)
             .distinct(true)
             .where(criteriaBuilder.lessThan(partTimeEmployee.get(PartTimeEmployee_.weeklySalary), 1000));
        TypedQuery<Employee> typedQuery = entityManager.createQuery(query);
        List<Employee> employees = typedQuery.getResultList();
        employees.forEach(System.out::println);
        entityManager.close();
    }

    //using method: Root<T> treat(Root<X> root, Class<T> type);
    private static void getByPartTimeAndContractEmployeesBySalary(EntityManagerFactory emf) {
        System.out.println("-- employees where PartTimeEmployee.weeklySalary > 1000 or "
                + "ContractEmployee#hourlyRate < 75 --");
        EntityManager entityManager = emf.createEntityManager();
        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
        CriteriaQuery<Employee> query = criteriaBuilder.createQuery(Employee.class);
        Root<Employee> employee = query.from(Employee.class);
        Root<PartTimeEmployee> partTimeEmployee = criteriaBuilder.treat(employee, PartTimeEmployee.class);
        Root<ContractEmployee> contractEmployee = criteriaBuilder.treat(employee, ContractEmployee.class);
        query.select(partTimeEmployee)
             .distinct(true)
             .where(criteriaBuilder.or(
                     criteriaBuilder.greaterThan(partTimeEmployee.get(PartTimeEmployee_.weeklySalary), 1000),
                     criteriaBuilder.lessThan(contractEmployee.get(ContractEmployee_.hourlyRate), 75)
             ));
        TypedQuery<Employee> typedQuery = entityManager.createQuery(query);
        List<Employee> employees = typedQuery.getResultList();
        employees.forEach(System.out::println);
        entityManager.close();
    }

    //using method: ListJoin<X, E> treat(ListJoin<X, T> join, Class<E> type)
    private static void getProjectByFullTimeEmployeesSalary(EntityManagerFactory emf) {
        System.out.println("-- getting projects where any FullTimeEmployee.annualSalary > 100000 by using join --");
        EntityManager entityManager = emf.createEntityManager();
        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
        CriteriaQuery<Project> query = criteriaBuilder.createQuery(Project.class);
        Root<Project> project = query.from(Project.class);
        ListJoin<Project, Employee> employees = project.join(Project_.employees);
        ListJoin<Project, FullTimeEmployee> fullTimeEmployee = criteriaBuilder.treat(employees, FullTimeEmployee.class);
        query.select(project)
             .distinct(true)
             .where(criteriaBuilder.greaterThan(fullTimeEmployee.get(FullTimeEmployee_.annualSalary), 100000));
        TypedQuery<Project> typedQuery = entityManager.createQuery(query);
        List<Project> projects = typedQuery.getResultList();
        projects.forEach(System.out::println);
        entityManager.close();
    }

    //using method:  Path<T> treat(Path<X> path, Class<T> type);
    private static void getProjectBySupervisorPartTimeSalary(EntityManagerFactory emf) {
        System.out.println("-- Projects where ((PartTimeEmployee)Project.supervisor).weeklySalary > 1000 --");
        EntityManager entityManager = emf.createEntityManager();
        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
        CriteriaQuery<Project> query = criteriaBuilder.createQuery(Project.class);
        Root<Project> project = query.from(Project.class);
        Path<Employee> supervisor = project.get(Project_.supervisor);
        Path<PartTimeEmployee> partTimeSupervisor = criteriaBuilder.treat(supervisor, PartTimeEmployee.class);
        query.select(project)
             .where(criteriaBuilder.greaterThan(partTimeSupervisor.get(PartTimeEmployee_.weeklySalary), 1000));
        TypedQuery<Project> typedQuery = entityManager.createQuery(query);
        List<Project> projects = typedQuery.getResultList();
        projects.forEach(System.out::println);
        entityManager.close();
    }

    private static void persistEntities(EntityManagerFactory emf) throws Exception {
        System.out.println("-- Persisting entities --");
        EntityManager em = emf.createEntityManager();
        em.getTransaction().begin();
        for (Project project : createProjects()) {
            em.persist(project);
            System.out.println(project);
        }
        em.getTransaction().commit();
    }

    private static List<Project> createProjects() {
        List<Project> projects = new ArrayList<>();

        FullTimeEmployee e1 = new FullTimeEmployee();
        e1.setName("Sara");
        e1.setAnnualSalary(120000);

        PartTimeEmployee e2 = new PartTimeEmployee();
        e2.setName("Jon");
        e2.setWeeklySalary(900);

        ContractEmployee e3 = new ContractEmployee();
        e3.setName("Tom");
        e3.setHourlyRate(60);

        PartTimeEmployee supervisor1 = new PartTimeEmployee();
        supervisor1.setName("Tina");
        supervisor1.setWeeklySalary(1300);

        projects.add(Project.create(supervisor1, "Trade UI", e1, e2, e3));

        FullTimeEmployee e4 = new FullTimeEmployee();
        e4.setName("Mike");
        e4.setAnnualSalary(80000);

        PartTimeEmployee e5 = new PartTimeEmployee();
        e5.setName("Jackie");
        e5.setWeeklySalary(1200);

        ContractEmployee e6 = new ContractEmployee();
        e6.setName("Aly");
        e6.setHourlyRate(90);

        PartTimeEmployee supervisor2 = new PartTimeEmployee();
        supervisor2.setName("Trish");
        supervisor2.setWeeklySalary(900);

        projects.add(Project.create(supervisor2, "Broker UI", e4, e5, e6));

        return projects;
    }
}
-- Persisting entities --
Project{id=1, name='Trade UI', supervisor= PartTimeEmployee{id=2, name='Tina', weeklySalary=1300}, employees=[FullTimeEmployee{id=3, name='Sara', annualSalary=120000}, PartTimeEmployee{id=4, name='Jon', weeklySalary=900}, ContractEmployee{id=5, name='Tom', hourlyRate='60'}]}
Project{id=6, name='Broker UI', supervisor= PartTimeEmployee{id=7, name='Trish', weeklySalary=900}, employees=[FullTimeEmployee{id=8, name='Mike', annualSalary=80000}, PartTimeEmployee{id=9, name='Jackie', weeklySalary=1200}, ContractEmployee{id=10, name='Aly', hourlyRate='90'}]}
-- employees where PartTimeEmployee.weeklySalary < 1000 --
PartTimeEmployee{id=4, name='Jon', weeklySalary=900}
PartTimeEmployee{id=7, name='Trish', weeklySalary=900}
-- employees where PartTimeEmployee.weeklySalary > 1000 or ContractEmployee#hourlyRate < 75 --
PartTimeEmployee{id=2, name='Tina', weeklySalary=1300}
PartTimeEmployee{id=9, name='Jackie', weeklySalary=1200}
ContractEmployee{id=5, name='Tom', hourlyRate='60'}
-- getting projects where any FullTimeEmployee.annualSalary > 100000 by using join --
Project{id=1, name='Trade UI', supervisor= PartTimeEmployee{id=2, name='Tina', weeklySalary=1300}, employees=[FullTimeEmployee{id=3, name='Sara', annualSalary=120000}, PartTimeEmployee{id=4, name='Jon', weeklySalary=900}, ContractEmployee{id=5, name='Tom', hourlyRate='60'}]}
-- Projects where ((PartTimeEmployee)Project.supervisor).weeklySalary > 1000 --
Project{id=1, name='Trade UI', supervisor= PartTimeEmployee{id=2, name='Tina', weeklySalary=1300}, employees=[FullTimeEmployee{id=3, name='Sara', annualSalary=120000}, PartTimeEmployee{id=4, name='Jon', weeklySalary=900}, ContractEmployee{id=5, name='Tom', hourlyRate='60'}]}

Example Project

Dependencies and Technologies Used:

  • hibernate-core 5.3.7.Final: Hibernate's core ORM functionality.
    Implements javax.persistence:javax.persistence-api version 2.2
  • hibernate-jpamodelgen 5.3.7.Final: Annotation Processor to generate JPA 2 static metamodel classes.
  • h2 1.4.197: H2 Database Engine.
  • JDK 1.8
  • Maven 3.5.4

Criteria API - CriteriaBuilder#treat() Example Select All Download
  • jpa-downcasting-with-treat-method
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • ExampleMain.java
          • resources
            • META-INF

    See Also