Proxy en el método load () de hibernación

1. Información general

En este tutorial, veremos qué es un proxy en el contexto del método load () de Hibernate .

Para los lectores nuevos en Hibernate, considere familiarizarse primero con los conceptos básicos.

2. Una breve introducción a los proxies y al método load ()

Por definición, un apoderado es “una función autorizada para actuar como suplente o sustituto de otro” .

Esto se aplica a Hibernate cuando llamamos a Session.load () para crear lo que se llama un proxy no inicializado de nuestra clase de entidad deseada.

En pocas palabras, Hibernate subclasifica nuestra clase de entidad, usando la biblioteca CGLib . Aparte del método @Id , la implementación del proxy delega todos los demás métodos de propiedad a la sesión de Hibernate para poblar la instancia, algo así como:

public class HibernateProxy extends MyEntity {     private MyEntity target;     public String getFirstName() {         if (target == null) {             target = readFromDatabase();         }         return target.getFirstName();     } }

Esta subclase será la que se devolverá en lugar de consultar la base de datos directamente.

Una vez que se llama a uno de los métodos de entidad, la entidad se carga y en ese punto se convierte en un proxy inicializado.

3. Proxies y carga diferida

3.1. Una sola entidad

Pensemos en Employee como entidad. Para empezar, asumiremos que no tiene relación con ninguna otra tabla.

Si usamos Session.load () para crear una instancia de un empleado :

Employee albert = session.load(Employee.class, new Long(1));

Luego, Hibernate creará un proxy de Empleado no inicializado . Contendrá la ID que le dimos, pero de lo contrario no tendrá otros valores porque aún no hemos llegado a la base de datos.

Sin embargo, una vez que llamamos a un método en albert :

String firstName = albert.getFirstName();

Luego, Hibernate consultará la tabla de la base de datos de empleados para una entidad con una clave primaria de 1, llenando a Albert con sus propiedades de la fila correspondiente.

Si no encuentra una fila, Hibernate arroja una ObjectNotFoundException .

3.2. Relaciones uno a varios

Ahora, creemos también una entidad de empresa , donde una empresa tiene muchos empleados:

public class Company {     private String name;     private Set employees; }

Si esta vez usamos Session.load () en la empresa:

Company bizco = session.load(Company.class, new Long(1)); String name = bizco.getName();

Luego, las propiedades de la empresa se pueblan como antes, excepto que el conjunto de empleados es un poco diferente.

Mira, solo consultamos la fila de la empresa, pero el proxy dejará al empleado configurado solo hasta que llamemos a getEmployees, según la estrategia de búsqueda.

3.3. Relaciones de varios a uno

El caso es similar en la dirección opuesta:

public class Employee {     private String firstName;     private Company workplace; }

Si usamos load () nuevamente:

Employee bob = session.load(Employee.class, new Long(2)); String firstName = bob.getFirstName();

bob ahora se inicializará y, de hecho, el lugar de trabajo ahora se configurará para ser un proxy no inicializado según la estrategia de búsqueda.

4. Carga perezosa

Ahora, load () no siempre nos dará un proxy sin inicializar. De hecho, el documento de Java Session nos recuerda (énfasis agregado):

Este método puede devolver una instancia proxy que se inicializa a pedido, cuando se accede a un método sin identificador.

Un ejemplo simple de cuando esto puede suceder es con el tamaño del lote.

Digamos que estamos usando @BatchSize en nuestra entidad Employee :

@Entity @BatchSize(size=5) class Employee { // ... }

Y esta vez tenemos tres empleados:

Employee catherine = session.load(Employee.class, new Long(3)); Employee darrell = session.load(Employee.class, new Long(4)); Employee emma = session.load(Employee.class, new Long(5));

Si llamamos a getFirstName en catherine :

String cathy = catherine.getFirstName();

Entonces, en realidad, Hibernate puede decidir cargar a los tres empleados a la vez, convirtiendo a los tres en proxies inicializados.

Y luego, cuando pedimos el nombre de Darrell :

String darrell = darrell.getFirstName();

Entonces Hibernate no llega a la base de datos en absoluto.

5. Carga ansiosa

5.1. Usando get ()

También podemos omitir los proxies por completo y pedirle a Hibernate que cargue lo real usando Session.get () :

Employee finnigan = session.get(Employee.class, new Long(6));

Esto llamará a la base de datos de inmediato, en lugar de devolver un proxy.

Y de hecho, en lugar de una ObjectNotFoundException , devolverá nulo si finnigan no existe.

5.2. Implicaciones de rendimiento

Si bien get () es conveniente, load () puede ser más ligero en la base de datos.

Por ejemplo, digamos que Gerald va a trabajar para una nueva empresa:

Employee gerald = session.get(Employee.class, new Long(7)); Company worldco = (Company) session.load(Company.class, new Long(2)); employee.setCompany(worldco); session.save(employee);

Como sabemos que solo vamos a cambiar el registro del empleado en esta situación , es sensato llamar a load () para Company .

Si llamamos a get () en Company , habríamos cargado todos sus datos innecesariamente desde la base de datos.

6. Conclusión

En este artículo, aprendimos brevemente cómo funcionan los proxies de Hibernate y cómo esto afecta el método de carga con las entidades y sus relaciones.

Además, echamos un vistazo rápido a las diferencias entre load () y get ().

Como de costumbre, el código fuente completo que acompaña al tutorial está disponible en GitHub.