1. Introducción
En este breve tutorial, veremos diferentes valores de FetchMode que podemos usar en la anotación @ org.hibernate.annotations.Fetch .
2. Configurar el ejemplo
Como ejemplo, usaremos la siguiente entidad Cliente con solo dos propiedades: una identificación y un conjunto de pedidos:
@Entity public class Customer { @Id @GeneratedValue private Long id; @OneToMany(mappedBy = "customer") @Fetch(value = FetchMode.SELECT) private Set orders = new HashSet(); // getters and setters }
Además, crearemos una entidad de Pedido que consta de una identificación, un nombre y una referencia al Cliente .
@Entity public class Order { @Id @GeneratedValue private Long id; private String name; @ManyToOne @JoinColumn(name = "customer_id") private Customer customer; // getters and setters }
En cada una de las siguientes secciones, buscaremos al cliente de la base de datos y obtendremos todos sus pedidos:
Customer customer = customerRepository.findById(id).get(); Set orders = customer.getOrders();
3. FetchMode.SELECT
En nuestra entidad Cliente , hemos anotado la propiedad de los pedidos con una anotación @Fetch :
@OneToMany @Fetch(FetchMode.SELECT) private Set orders;
Usamos @Fetch para describir cómo Hibernate debería recuperar la propiedad cuando buscamos un Cliente.
El uso de SELECT indica que la propiedad debe cargarse de forma perezosa.
Esto significa que para la primera línea:
Customer customer = customerRepository.findById(id).get();
No veremos una combinación con la tabla de pedidos:
Hibernate: select ...from customer where customer0_.id=?
Y eso para la siguiente línea:
Customer customer = customerRepository.findById(id).get();
Veremos consultas posteriores para los pedidos relacionados:
Hibernate: select ...from order where order0_.customer_id=?
El FetchMode.SELECT de Hibernate genera una consulta separada para cada Orden que necesita ser cargada.
En nuestro ejemplo, eso da una consulta para cargar los Clientes y cinco consultas adicionales para cargar la colección de pedidos.
Esto se conoce como el problema de selección n + 1. La ejecución de una consulta desencadenará n consultas adicionales.
3.1. @ BatchSize
FetchMode.SELECT tiene una anotación de configuración opcional usando la anotación @BatchSize :
@OneToMany @Fetch(FetchMode.SELECT) @BatchSize(size=10) private Set orders;
Hibernate intentará cargar la colección de pedidos en lotes definidos por el parámetro de tamaño .
En nuestro ejemplo, solo tenemos cinco pedidos, por lo que una consulta es suficiente.
Seguiremos usando la misma consulta:
Hibernate: select ...from order where order0_.customer_id=?
Pero solo se ejecutará una vez. Ahora solo tenemos dos consultas: Una para cargar el Cliente y otra para cargar la colección de pedidos.
4. FetchMode.JOIN
Mientras FetchMode.SELECT carga las relaciones de forma perezosa, FetchMode.JOIN las carga con entusiasmo, digamos a través de una combinación:
@OneToMany @Fetch(FetchMode.JOIN) private Set orders;
Esto da como resultado una sola consulta tanto para el Cliente como para sus Pedidos :
Hibernate: select ... from customer customer0_ left outer join order order1 on customer.id=order.customer_id where customer.id=?
5. FetchMode.SUBSELECT
Debido a que la propiedad orders es una colección, también podríamos usar FetchMode.SUBSELECT :
@OneToMany @Fetch(FetchMode.SUBSELECT) private Set orders;
Solo podemos usar SUBSELECT con colecciones.
Con esta configuración, volvemos a una consulta para el Cliente:
Hibernate: select ... from customer customer0_
Y una consulta para los pedidos , usando una sub-selección esta vez:
Hibernate: select ... from order order0_ where order0_.customer_id in ( select customer0_.id from customer customer0_ )
6. FetchMode frente a FetchType
En general, FetchMode define cómo Hibernate buscará los datos (mediante selección, unión o subselección). FetchType , por otro lado, define si Hibernate cargará datos con entusiasmo o con pereza.
Las reglas exactas entre estos dos son las siguientes:
- si el código no establece FetchMode , el predeterminado es JOIN y FetchType funciona como se define
- con FetchMode.SELECT o FetchMode.SUBSELECT establecido, FetchType también funciona como se define
- con FetchMode.JOIN establecido, FetchType se ignora y una consulta siempre está ansiosa
Para obtener más información, consulte Carga ansiosa / diferida en hibernación.
7. Conclusión
En este tutorial, hemos aprendido sobre los diferentes valores de FetchMode y también cómo se relacionan con FetchType .
Como siempre, todo el código fuente está disponible en GitHub.