Close

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

[Updated: Dec 12, 2018, Created: Dec 11, 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