Join Fetch en JPA con Spring Data
En un artículo anterior vimos que para evitar el problema de la excepción LazyInitializeException se puede hacer uso de Join Fetch en JPA con Spring Data. El uso de esta opción nos ayudará a mantener un buen rendimiento de nuestra aplicación reduciendo el número de queries.
Para comenzar vamos a comenzar viendo la diferencia entre Join y Join Fetch en JPA.
Diferencia entre Join Fetch y Join en JPA
Ambas sentencias van se encargan de realizar una JOIN sobre otra tabla, es decir, combinar dos tablas con un identificador común. Pero existe una diferencia básica entre ambas vamos a verlo con un ejemplo:
Select * FROM Employee emp JOIN emp.department dep
SELECT * FROM Employee emp JOIN FETCH emp.department dep
La diferencia es que la primera query únicamente va a devolver los Employes, y en la segunda query se van a devolver los Employees y los Departments relacionados.
Por lo que si utilizamos la segunda opción evitaremos hacer queries adicionales para obtener los Departments del Employee.
Al hacer uso del primer ejemplo tendríamos el típico problema de N+1 query.
Vamos a ver un ejemplo completo.
Ejemplo Join Fetch con Spring Data
Para ver el ejemplo completo lo puedes hacer aquí.
Configuración de fichero de propiedades Spring Boot para mostrar las queries en H2
Vamos a añadir dos propiedades para que se muestren las queries por consola:
spring: jpa: show-sql: true properties: hibernate: format_sql: true
Generación de entidades
Vamos a generar dos entidades con una relación de 1 .. N, entre Department y Employee. Para las siguientes entidades vamos a hacer uso de @ElementCollection pero se podría hacer también con una @OnetToMany por ejemplo:
@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; }
Una vez hemos creado las entidades vamos a crear un repositorio en el que crearemos una query con @Query con Join Fetch:
public interface DepartmentRepository extends JpaRepository<Department, String> { @Query("SELECT DISTINCT department FROM Department department " + "JOIN FETCH department.employees employees") List<Department> retrieveAll(); }
Y a continuación crearemos dos test para verificar el número de querys que se crean y comprobar que el uso de Join Fetch creará una única 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()); }); } }
Al ejecutar los test anteriores vamos a ver como al ejecutar el método que se ha realizado con JoinFetch obtendremos una única 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
En cambio al ejecutar el método que hace uso del método findAll() de JPA obtendremos exactamente dos queries, una para department y otra para 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=?
Como podemos ver, el test que no ha hecho uso de Join Fetch va a realizar n+1 query, es decir, una query para el objeto padre y tantas queries como objetos hijos se quieran.
Conclusión
En este artículo hemos visto como funciona Join Fetch en JPA con Spring Data, lo cual es muy importante conocer ya que puede afectar al rendimiento de nuestras aplicaciones.
Otro artículo que te puede interesar es el uso de FetchMode en Hibernate.
Si te quieres descargar el ejemplo completo lo puedes hacer de aquí.
Si necesitas más información puedes escribirnos un comentario o un correo electrónico a refactorizando.web@gmail.com o también nos puedes contactar por nuestras redes sociales Facebook o twitter y te ayudaremos encantados!