In a previous article, we saw that to avoid the LazyInitializationException issue, we can use Join Fetch in JPA with Spring Data. Using this option helps us maintain good application performance by reducing the number of queries.
Let’s start by understanding the difference between Join and Join Fetch in JPA.
Difference between Join Fetch and Join in JPA
Both statements are used to perform a JOIN on another table, combining two tables with a common identifier. However, there is a fundamental difference between the two, as shown in the following example:
Select * FROM Employee emp JOIN emp.department dep
SELECT * FROM Employee emp JOIN FETCH emp.department dep
The difference is that the first query will only return the Employees, while the second query will return both the Employees and the related Departments.
By using the second option, we avoid making additional queries to obtain the Departments of each Employee.
When using the first example, we would encounter the typical N+1 query problem.
Let’s see a complete example.
Example of Join Fetch with Spring Data
To see the complete example, you can check it here.
Configuration of Spring Boot properties file to display queries in H2
Let’s add two properties to display queries in the console:
spring: jpa: show-sql: true properties: hibernate: format_sql: true
Entity generation
Let’s generate two entities with a 1 .. N relationship between Department and Employee. We’ll use @ElementCollection for the following entities, but it could also be done with @OneToMany, for example:
@Getter @Setter @Entity public class Department { @Id @GeneratedValue(generator = "uuid") @GenericGenerator(name = "uuid", strategy = "uuid2") private String id; private String name; @ElementCollection private List<Employee> employees; }
@Embeddable @Getter @Setter public class Employee { private String name; private String surname; }
Once we’ve created the entities, let’s create a repository where we’ll define a query using @Query with Join Fetch:
public interface DepartmentRepository extends JpaRepository<Department, String> { @Query("SELECT DISTINCT department FROM Department department " + "JOIN FETCH department.employees employees") List<Department> retrieveAll(); }
Next, we’ll create two tests to verify the number of queries generated and to check that using Join Fetch will result in a single query.
@SpringBootTest @Slf4j public class DepartmentRepositoryIT { @Autowired private DepartmentRepository departmentRepository; @Test @Transactional public void withoutJoinFetch() { List<Department> departments = departmentRepository.findAll(); departments.forEach(department -> { log.info("Employees -> {} ", department.getEmployees()); }); } @Test @Transactional public void withJoinFetch() { List<Department> departments = departmentRepository.retrieveAll(); departments.forEach(department -> { log.info("Employees -> {} ", department.getEmployees()); }); } }
When running the above tests, we’ll see that executing the method that uses Join Fetch results in a single query:
Hibernate: select distinct d1_0.id, e1_0.department_id, e1_0.name, e1_0.surname, d1_0.name from department d1_0 join department_employees e1_0 on d1_0.id=e1_0.department_id
On the other hand, executing the method that uses the findAll() method of JPA will result in exactly two queries, one for department and one for employee:
Hibernate: select d1_0.id, d1_0.name from department d1_0 Hibernate: select e1_0.department_id, e1_0.name, e1_0.surname from department_employees e1_0 where e1_0.department_id=?
As we can see, the test that did not use Join Fetch will perform an N+1 query, meaning one query for the parent object and as many queries as there are child objects.
Conclusion
In this article, we have seen how Join Fetch works in JPA with Spring Data, which is important to know as it can affect the performance of our applications.
Another article that might interest you is the use of FetchMode in Hibernate.
If you want to download the complete example, you can do so from here.
If you need more information, you can leave us a comment or send an email to refactorizando.web@gmail.com You can also contact us through our social media channels on Facebook or twitter and we will be happy to assist you!!