Join Fetch en JPA con Spring Data

Join Fetch con JPA y Spring Data

Join Fetch con JPA y 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!


Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *