Close

Fetch Joins in Criteria API

[Last Updated: Dec 11, 2018]

In JPA Criteria API a FETCH JOIN can be applied by using one of the various fetch() methods of FetchParent interface. For example:

Fetch<X, Y> fetch(PluralAttribute<? super X, ?, Y> attribute, JoinType jt);

FetchParent is a super interface of From interface.

Quick example:

  CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
  CriteriaQuery<Employee> query = criteriaBuilder.createQuery(Employee.class);
  Root<Employee> employee = query.from(Employee.class);
  employee.fetch(Employee_.tasks, JoinType.INNER);
  query.select(employee)
       .distinct(true);
  TypedQuery<Employee> typedQuery = entityManager.createQuery(query);
  List<Employee> resultList = typedQuery.getResultList();

Example

Entities

@Entity
public class Employee {
  @Id
  @GeneratedValue
  private long id;
  private String name;
  @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
  private List<Task> tasks;
    .............
}
@Entity
public class Task {
  @Id
  @GeneratedValue
  private long id;
  private String description;
  private String supervisor;
    .............
}

Inner JOIN without FETCH

Let's see a Criteria query without fetch() first to understand the difference:

public class ExampleMain {
  private static EntityManagerFactory entityManagerFactory =
          Persistence.createEntityManagerFactory("example-unit");

  public static void main(String[] args) {
      try {
          persistEmployees();
          findEmployeesWithTasks();
      } finally {
          entityManagerFactory.close();
      }
  }

  public static void persistEmployees() {
      Task task1 = new Task("Coding", "Denise");
      Task task2 = new Task("Refactoring", "Rose");
      Task task3 = new Task("Designing", "Denise");
      Task task4 = new Task("Documentation", "Mike");

      Employee employee1 = Employee.create("Diana", task1, task3);
      Employee employee2 = Employee.create("Mike", task2, task4);
      Employee employee3 = Employee.create("Tim", task3, task4);
      Employee employee4 = Employee.create("Jack");

      EntityManager em = entityManagerFactory.createEntityManager();
      em.getTransaction().begin();
      em.persist(employee1);
      em.persist(employee2);
      em.persist(employee3);
      em.persist(employee4);
      em.getTransaction().commit();
      em.close();
      System.out.println("-- Employee persisted --");
      System.out.println(employee1);
      System.out.println(employee2);
      System.out.println(employee3);
      System.out.println(employee4);
  }

  private static void findEmployeesWithTasks() {
      System.out.println("-- find employees with tasks --");
      EntityManager em = entityManagerFactory.createEntityManager();
      CriteriaBuilder cb = em.getCriteriaBuilder();
      CriteriaQuery<Employee> query = cb.createQuery(Employee.class);
      Root<Employee> employee = query.from(Employee.class);
      employee.join(Employee_.tasks, JoinType.INNER);
      query.select(employee)
           .distinct(true);
      TypedQuery<Employee> typedQuery = em.createQuery(query);
      List<Employee> resultList = typedQuery.getResultList();
      for (Employee e : resultList) {
          System.out.println(e.getName() + " - " + e.getTasks());
      }

  }
}
-- Employee persisted --
Employee{id=1, name='Diana', tasks=[Task{id=2, description='Coding', supervisor='Denise'}, Task{id=3, description='Designing', supervisor='Denise'}]}
Employee{id=4, name='Mike', tasks=[Task{id=5, description='Refactoring', supervisor='Rose'}, Task{id=6, description='Documentation', supervisor='Mike'}]}
Employee{id=7, name='Tim', tasks=[Task{id=3, description='Designing', supervisor='Denise'}, Task{id=6, description='Documentation', supervisor='Mike'}]}
Employee{id=8, name='Jack', tasks=[]}
-- find employees with tasks --
Hibernate: select distinct employee0_.id as id1_0_, employee0_.name as name2_0_ from Employee employee0_ inner join Employee_Task tasks1_ on employee0_.id=tasks1_.Employee_id inner join Task task2_ on tasks1_.tasks_id=task2_.id
Hibernate: select tasks0_.Employee_id as Employee1_1_0_, tasks0_.tasks_id as tasks_id2_1_0_, task1_.id as id1_2_1_, task1_.description as descript2_2_1_, task1_.supervisor as supervis3_2_1_ from Employee_Task tasks0_ inner join Task task1_ on tasks0_.tasks_id=task1_.id where tasks0_.Employee_id=?
Tim - [Task{id=3, description='Designing', supervisor='Denise'}, Task{id=6, description='Documentation', supervisor='Mike'}]
Hibernate: select tasks0_.Employee_id as Employee1_1_0_, tasks0_.tasks_id as tasks_id2_1_0_, task1_.id as id1_2_1_, task1_.description as descript2_2_1_, task1_.supervisor as supervis3_2_1_ from Employee_Task tasks0_ inner join Task task1_ on tasks0_.tasks_id=task1_.id where tasks0_.Employee_id=?
Mike - [Task{id=5, description='Refactoring', supervisor='Rose'}, Task{id=6, description='Documentation', supervisor='Mike'}]
Hibernate: select tasks0_.Employee_id as Employee1_1_0_, tasks0_.tasks_id as tasks_id2_1_0_, task1_.id as id1_2_1_, task1_.description as descript2_2_1_, task1_.supervisor as supervis3_2_1_ from Employee_Task tasks0_ inner join Task task1_ on tasks0_.tasks_id=task1_.id where tasks0_.Employee_id=?
Diana - [Task{id=2, description='Coding', supervisor='Denise'}, Task{id=3, description='Designing', supervisor='Denise'}]

In above example, for each Employee.getTasks() call, a separate 'select' query was executed.

INNER JOIN with FETCH

public class ExampleMain2 {
  private static EntityManagerFactory entityManagerFactory =
          Persistence.createEntityManagerFactory("example-unit");

  public static void main(String[] args) {
      try {
          persistEmployees();
          findEmployeesWithTasks();
      } finally {
          entityManagerFactory.close();
      }
  }
    .............
  private static void findEmployeesWithTasks() {
      System.out.println("-- find employees with tasks --");
      EntityManager entityManager = entityManagerFactory.createEntityManager();
      CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
      CriteriaQuery<Employee> query = criteriaBuilder.createQuery(Employee.class);
      Root<Employee> employee = query.from(Employee.class);
      employee.fetch(Employee_.tasks, JoinType.INNER);
      query.select(employee)
           .distinct(true);
      TypedQuery<Employee> typedQuery = entityManager.createQuery(query);
      List<Employee> resultList = typedQuery.getResultList();
      for (Employee e : resultList) {
          System.out.println(e.getName() + " - " + e.getTasks());
      }
  }
}
-- Employee persisted --
Employee{id=1, name='Diana', tasks=[Task{id=2, description='Coding', supervisor='Denise'}, Task{id=3, description='Designing', supervisor='Denise'}]}
Employee{id=4, name='Mike', tasks=[Task{id=5, description='Refactoring', supervisor='Rose'}, Task{id=6, description='Documentation', supervisor='Mike'}]}
Employee{id=7, name='Tim', tasks=[Task{id=3, description='Designing', supervisor='Denise'}, Task{id=6, description='Documentation', supervisor='Mike'}]}
Employee{id=8, name='Jack', tasks=[]}
-- find employees with tasks --
Hibernate: select distinct employee0_.id as id1_0_0_, task2_.id as id1_2_1_, employee0_.name as name2_0_0_, task2_.description as descript2_2_1_, task2_.supervisor as supervis3_2_1_, tasks1_.Employee_id as Employee1_1_0__, tasks1_.tasks_id as tasks_id2_1_0__ from Employee employee0_ inner join Employee_Task tasks1_ on employee0_.id=tasks1_.Employee_id inner join Task task2_ on tasks1_.tasks_id=task2_.id
Diana - [Task{id=3, description='Designing', supervisor='Denise'}, Task{id=2, description='Coding', supervisor='Denise'}]
Mike - [Task{id=6, description='Documentation', supervisor='Mike'}, Task{id=5, description='Refactoring', supervisor='Rose'}]
Tim - [Task{id=6, description='Documentation', supervisor='Mike'}, Task{id=3, description='Designing', supervisor='Denise'}]

This time only one 'select' statement for the main query was executed, hence the lazy employee#tasks relations were loaded at the same time.

LEFT OUTER JOIN with FETCH

public class ExampleMain3 {
  private static EntityManagerFactory entityManagerFactory =
          Persistence.createEntityManagerFactory("example-unit");

  public static void main(String[] args) {
      try {
          persistEmployees();
          findEmployeesWithTasks();
      } finally {
          entityManagerFactory.close();
      }
  }
    .............
  private static void findEmployeesWithTasks() {
      System.out.println("-- find employees with tasks --");
      EntityManager em = entityManagerFactory.createEntityManager();
      CriteriaBuilder cb = em.getCriteriaBuilder();
      CriteriaQuery<Employee> query = cb.createQuery(Employee.class);
      Root<Employee> employee = query.from(Employee.class);
      employee.fetch(Employee_.tasks, JoinType.LEFT);
      query.select(employee)
           .distinct(true);
      TypedQuery<Employee> typedQuery = em.createQuery(query);
      List<Employee> resultList = typedQuery.getResultList();
      for (Employee e : resultList) {
          System.out.println(e.getName() + " - " + e.getTasks());
      }
  }
}
-- Employee persisted --
Employee{id=1, name='Diana', tasks=[Task{id=2, description='Coding', supervisor='Denise'}, Task{id=3, description='Designing', supervisor='Denise'}]}
Employee{id=4, name='Mike', tasks=[Task{id=5, description='Refactoring', supervisor='Rose'}, Task{id=6, description='Documentation', supervisor='Mike'}]}
Employee{id=7, name='Tim', tasks=[Task{id=3, description='Designing', supervisor='Denise'}, Task{id=6, description='Documentation', supervisor='Mike'}]}
Employee{id=8, name='Jack', tasks=[]}
-- find employees with tasks --
Hibernate: select distinct employee0_.id as id1_0_0_, task2_.id as id1_2_1_, employee0_.name as name2_0_0_, task2_.description as descript2_2_1_, task2_.supervisor as supervis3_2_1_, tasks1_.Employee_id as Employee1_1_0__, tasks1_.tasks_id as tasks_id2_1_0__ from Employee employee0_ left outer join Employee_Task tasks1_ on employee0_.id=tasks1_.Employee_id left outer join Task task2_ on tasks1_.tasks_id=task2_.id
Diana - [Task{id=3, description='Designing', supervisor='Denise'}, Task{id=2, description='Coding', supervisor='Denise'}]
Mike - [Task{id=6, description='Documentation', supervisor='Mike'}, Task{id=5, description='Refactoring', supervisor='Rose'}]
Tim - [Task{id=6, description='Documentation', supervisor='Mike'}, Task{id=3, description='Designing', supervisor='Denise'}]
Jack - []

This time results also contain the employees who don't have any tasks (because of LEFT JOIN) and also only one select statement is executed to load all employees and their tasks (because of FETCH).

Example Project

Dependencies and Technologies Used:

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

Fetch Joins Example Select All Download
  • jpa-criteria-api-fetch-join
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • ExampleMain2.java
          • resources
            • META-INF

    See Also