Aprendiendo Hibernate 9 - Estrategias de Recuperación

En este capítulo, utilizaremos una relación de uno a muchos entre EntidadClase y Estudiante;

Estudiante.java:

package com.cy.modelo;

public class Estudiante {
    private int identificador;
    private String nombre;
    private EntidadClase clase;
    
    public int getIdentificador() {
        return identificador;
    }
    public void setIdentificador(int identificador) {
        this.identificador = identificador;
    }
    public String getNombre() {
        return nombre;
    }
    public void setNombre(String nombre) {
        this.nombre = nombre;
    }
    public EntidadClase getClase() {
        return clase;
    }
    public void setClase(EntidadClase clase) {
        this.clase = clase;
    }
    
    
}

EntidadClase.java:

package com.cy.modelo;

import java.util.HashSet;
import java.util.Set;

public class EntidadClase {
    private int identificador;
    private String nombre;
    private Set<Estudiante> estudiantes = new HashSet<Estudiante>();
    
    
    public int getIdentificador() {
        return identificador;
    }
    public void setIdentificador(int identificador) {
        this.identificador = identificador;
    }
    public String getNombre() {
        return nombre;
    }
    public void setNombre(String nombre) {
        this.nombre = nombre;
    }
    public Set<Estudiante> getEstudiantes() {
        return estudiantes;
    }
    public void setEstudiantes(Set<Estudiante> estudiantes) {
        this.estudiantes = estudiantes;
    }
    
    
}

Estudiante.hbm.xml:

<hibernate-mapping package="com.cy.modelo">

    <class name="Estudiante" table="t_estudiante">
        <id name="identificador" column="stuId">
            <generator class="native"></generator>
        </id>
        <property name="nombre" column="stuNombre"></property>
        
        <many-to-one name="clase" column="classId" class="com.cy.modelo.EntidadClase" cascade="save-update" lazy="proxy"></many-to-one>
        
    </class>

</hibernate-mapping>

EntidadClase.hbm.xml:

<hibernate-mapping package="com.cy.modelo">

    <class name="EntidadClase" table="t_clase">
        <id name="identificador" column="classId">
            <generator class="native"></generator>
        </id>
        <property name="nombre" column="className"></property>
        
        <set name="estudiantes" cascade="save-update" inverse="true" lazy="false" batch-size="3" fetch="join">
            <key column="classId"></key>
            <one-to-many class="com.cy.modelo.Estudiante" />
        </set>
    </class>

</hibernate-mapping>

Sección 1: Atributo de estrategia de recuperación Lazy

Lazy:true: Recuperación diferida por defecto: se carga cuando se utiliza

    @Test
    public void testLazy1(){
        //En el archivo clase.hbm, en el lado set (uno a muchos), se configura lazy=true, 
        //por lo que al cargar la clase, los estudiantes no se cargan por defecto.
        EntidadClase clase = (EntidadClase) session.get(EntidadClase.class, 1);    
        
        //¿Cuándo se consultan los datos de los estudiantes? - cuando se necesitan
        //Se recupera solo cuando se utiliza, esto es recuperación diferida
        Set<Estudiante> estudiantes = clase.getEstudiantes();
        
        System.out.println(estudiantes.size());
        
//        Iterator it = estudiantes.iterator();
        
    }

lazy:false: Al consultar la clase, si se va a utilizar seguro la información del estudiante, se puede recuperar inmediatamente; también se puede configurar lazy=false lazy:extra: También es diferido, pero mejorado;Por ejemplo, si se configura lazy=true y se desea saber la cantidad de estudiantes consultados:EntidadClase c = (EntidadClase) session.get(EntidadClase.class, 1);Set<Estudiante> estudiantes = c.getEstudiantes();System.out.println(estudiantes.size());El SQL emitido consulta todos los estudiantes y luego se obtienen 3 registros;Pero en realidad solo se necesita select count(*) from estudiante, esto es optimización de SQL. Configurando lazy=extra se puede lograr;Sin embargo, en el desarrollo real no se utiliza mucho, porque rara vez se consulta solo el tamaño; lazy:proxy:Al obtener información del Estudiante, la EntidadClase interna es una clase proxy, que no contiene datos; pero cuando se utiliza el contenido de EntidadClase, se consutla la base de datos a través de la clase proxy.``` @Test public void testLazy2(){ //En el lado many-to-one, lazy es proxy por defecto. La EntidadClase en el estudiante obtenido es una clase proxy. Sin datos reales. Estudiante estudiante = (Estudiante) session.get(Estudiante.class, 1);

    //Cuando se utiliza el nombre de la clase, la clase proxy emite una consulta SQL para obtener los datos.
    estudiante.getClase().getNombre();
}


lazy:no-proxy:No hay clase proxy, al obtener estudiante, la clase es un valor nulo, hasta que se utiliza el contenido de la clase, se consulta. Rara vez se utiliza.......Sección 2: Atributo de estrategia de recuperación batch-size

1, Recuperación diferida por lotes;

La configuración correspondiente en clase.hbm: lazy="true" batch-size="3"


/** * Recuperación diferida por lotes, lazy=true */ @Test public void testBatch1(){ List<EntidadClase> listaClases = session.createQuery("from EntidadClase").list(); Iterator<EntidadClase> it = listaClases.iterator(); EntidadClase c1 = it.next(); EntidadClase c2 = it.next(); EntidadClase c3 = it.next(); c1.getEstudiantes().iterator(); c2.getEstudiantes().iterator(); c3.getEstudiantes().iterator();

    //Primero se obtienen todas las clases, porque en el lado set de estudiantes se configuró lazy=false
    //Cuando c1, c2, c3 necesitan estudiantes, es cuando se busca. Cada uno emite una sentencia SQL.
    //Si listaClases es muy muy grande, recorrer cada clase y luego obtener los estudiantes de cada clase, 
    //cualquier emite una sentencia SQL, la eficiencia sería muy baja.
    //Por eso se introduce batch-size, recuperación por lotes. El valor de batch-size es el tamaño del lote
    
    //En el archivo clase.hbm, en el set de estudiantes, configurando batch-size=3, la sentencia SQL emitida sería así:
    /**
     * SELECT 
            t_estudiante.classId , 
            t_estudiante.stuId , 
            t_estudiante.stuNombre
        FROM t_estudiante
        WHERE t_estudiante.classId IN (1, 2, 3)
     */
}


Mientras que sin configurar recuperación por lotes batch-size, la sentencia SQL es así, una por una:

Después de configurar recuperación por lotes, batch-size=3, al consultar información de estudiantes, se consultan 3 de una vez;

2, Recuperación inmediata por lotes;


@Test public void testBatch2(){ List<EntidadClase> listaClases = session.createQuery("from EntidadClase").list(); //Aquí en el archivo clase.hbm.xml, en el set de estudiantes se configura lazy=false, batch-size=3, //lo que significa recuperación inmediata por lotes. //La santencia SQL por lotes es la misma que arriba.

}


Sección 3: Atributo de estrategia de recuperación Fetch

Fetch también se configura en el lado de la clase; fetch:subselect, cuando los datos son muy grandes, usar subconsulta puede optimizar el rendimiento. ```
@Test
    public void testFetch1(){
        List<EntidadClase> listaClases = session.createQuery("from EntidadClase").list();
        Iterator<EntidadClase> it = listaClases.iterator();
        EntidadClase c1 = it.next();
        EntidadClase c2 = it.next();
        EntidadClase c3 = it.next();
        c1.getEstudiantes().iterator();
        c2.getEstudiantes().iterator();
        c3.getEstudiantes().iterator();
        /**
         * En el archivo clase.hbm, configuración del set de estudiantes: fetch="select", es el valor por defecto, la sentencia SQL emitida:
         * SELECT 
                t_estudiante.classId , 
                t_estudiante.stuId , 
                t_estudiante.stuNombre
            FROM t_estudiante
            WHERE t_estudiante.classId IN (1, 2, 3)
            
            
            Ahora cambiamos a fetch=subselect: la sentencia SQL se convierte en:
            SELECT 
                t_estudiante.classId , 
                t_estudiante.stuId , 
                t_estudiante.stuNombre
            FROM t_estudiante
            WHERE t_estudiante.classId IN
            (SELECT classId FROM t_clase )
         * 
         */
    }

fetch:join:

@Test
    public void testFetch2(){
        EntidadClase c = (EntidadClase) session.get(EntidadClase.class, 1);
        /**
         * Aquí en el archivo clase.hbm se configura:<set name="estudiantes" lazy="false" fetch="join">
         * Al consultar la clase, también se recuperan los estudiantes que pertenecen a esta clase, aquí fetch=join, SQL con left outer join:
         * La sentencia SQL emitida:
         * SELECT
                t_clase.classId,
                t_clase.className,
                t_estudiante.stuId,
                t_estudiante.stuNombre,
                t_estudiante.classId
            FROM t_clase
            LEFT OUTER JOIN t_estudiante ON t_clase.classId = t_estudiante.classId
            WHERE t_clase.classId = 1
            
         * Se ve que solo se emite una sentencia SQL, las tablas t_clase y t_estudiante se关联, 
         * la información de la clase y del estudiante se obtienen juntos;
         * 
         * Mientras que si fetch es select, primero se obtiene la información de la clase; luego se obtiene la información del estudiante
         */
    }

Etiquetas: Hibernate ORM java JPA persistencia de datos

Publicado el 7-13 05:55