Este artículo describe la tecnología de carga de latencia hibernada. Compártelo para su referencia, como sigue:
La carga perezosa de Hibeberna es una técnica muy común. Los atributos de recolección de las entidades se retrasarán de forma predeterminada, y las entidades asociadas con las entidades también se retrasarán de forma predeterminada. Hibernate utiliza esta carga retrasada para reducir la sobrecarga de memoria del sistema, asegurando así el rendimiento operativo de Hibernate.
Primero analicemos el "secreto" de la carga de retraso de hibernación.
Carga perezosa de propiedades de recolección
Cuando Hibernate inicializa una entidad persistente de la base de datos, ¿el atributo de recopilación de esa entidad se inicializa con la clase persistente? Si el atributo de colección contiene 100,000 o incluso millones de registros, el rastreo de todos los atributos de la recolección mientras inicializa la entidad persistente dará como resultado una fuerte disminución en el rendimiento. Es completamente posible que el sistema solo necesite usar algunos registros en los atributos de la recopilación de la clase persistente, y no todos los atributos de recopilación en absoluto. De esta manera, no hay necesidad de cargar todos los atributos de recolección a la vez.
Las estrategias de carga perezosa generalmente se recomiendan para las propiedades de recolección. La llamada carga retrasada es cargar datos asociados de la base de datos cuando el sistema necesita usar los atributos de recopilación.
Por ejemplo, la siguiente clase de persona contiene un atributo de colección, y el elemento en el atributo de colección tiene la dirección de tipo, y el fragmento de código de la clase de la persona es el siguiente:
Listado 1. Person.java
Persona de clase pública {// Identificar el atributo ID de entero privado; // Nombre de la persona Atributo Nombre de cadena privada; // Mantenga el atributo de edad de la persona private int Age; // use Set para guardar el atributo de colección Set privado <dirección> direcciones = new Hashset <dirección> (); // Los métodos setter y getter de cada atributo se omiten a continuación ...}Para que Hibernate administre las propiedades de recopilación de la clase persistente, el programa proporciona los siguientes archivos de mapeo para la clase persistente:
Listado 2. Person.hbm.xml
<? xml versión = "1.0" encoding = "gbk"?> <! DocType Hibernate-Mapping público "-// Hibernate/Hibernate Mapping Dtd 3.0 // en" http://wwww.hibernate.org/dtd/hibernate-mapping-3.0.dtd "> <hercbernate-mapping paquete = "org.crazyit.app.domain"> <!-Mapeo de la clase de persistencia de la persona-> <class name = "Person" table = "Person_inf"> <!-Mapeo de la propiedad de identificación ID-<id name = "id" columna = "Person_id"> <!-Define la política del generador de clave primaria-> <generador/> <!-Usado para mapear los atributos comunes-> <nombre de propiedad de propiedad "> <! name = "Age" type = "int"/> <!-Atributos de la colección de mapas-> <set name = "direcciones" table = "persona_address" lazy = "true"> <!-Especifique la columna de clave externa asociada-> <key columna = "persona_id"/> <compuesto-ement> <!-map mapa detalles de atributos normales-> <name de propiedad = "detalle"/> <!-mapa de atributo normal-> cebir de apropiación name = "Zip"/> </Composite-Esement> </set> </sclass> </hibernate-mapping>
Del código anterior que mapea el archivo, podemos ver que la clase de dirección en el atributo de colección de persona es solo un POJO normal. La clase de dirección contiene dos atributos: detalle y zip. Dado que el código de clase de dirección es muy simple, el código para esta clase ya no se da aquí.
El código en el elemento <set .../> en el archivo de asignación anterior especifica lazy = "true" (para <set .../> elemento, lazy = "true" es el valor predeterminado), que especifica que Hibernate retrasará la carga del objeto de dirección en el atributo de colección.
Por ejemplo, cargue una entidad de persona con ID 1 siguiendo el siguiente código:
Sesión session = sf.getCurrentSession (); transacción tx = session.begintransaction (); persona p = (persona) session.get (persona.class, 1); // <1> System.out.println (p.getName ());
El código anterior solo necesita acceder a la entidad de la persona con ID 1, y no desea acceder al objeto de dirección asociado con esta entidad de persona. Hay dos situaciones en este momento:
1. Si la carga no se retrasa, Hibernate tomará inmediatamente el objeto de dirección asociado con la entidad de la persona al cargar el registro de datos correspondiente a la entidad de la persona.
2. Si se usa la carga perezosa, Hibernate solo cargará los registros de datos correspondientes a la entidad de la persona.
Es obvio que el segundo enfoque no solo reduce la interacción con la base de datos, sino que también evita la sobrecarga de memoria causada por la carga de entidades de dirección, esta es la razón por la cual Hibernate permite la carga perezosa de forma predeterminada.
La pregunta ahora es, ¿cómo se implementa la carga perezosa? Hibernate ¿Cuál es el valor de la propiedad de las direcciones de la entidad de la persona al cargar una entidad de persona?
Para resolver este problema, establecemos un punto de ruptura en el código <1> y lo depugimos en Eclipse. En este momento, podemos ver que la ventana de la consola Eclipse tiene la salida como se muestra en la Figura 1:
Figura 1. Salida de consola para propiedades de recolección de carga perezosa
Como se muestra en la salida en la Figura 1, Hibernate solo toma datos de la tabla de datos correspondientes a la entidad de la persona, y no toma datos de la tabla de datos correspondiente al objeto de dirección. Esta es una carga perezosa.
Entonces, ¿cuál es la propiedad de las direcciones de la entidad de la persona? En este momento, puede ver los resultados que se muestran en la Figura 2 desde la ventana Variables de Eclipse:
Figura 2. Valores de atributo de colección cargada perezosa
Desde el contenido en el cuadro de la Figura 2, se puede ver que la propiedad de las direcciones no es las clases de implementación familiares como Hashset y Treeset, sino una clase de implementación de PersistentSet, que es una clase de implementación proporcionada por Hibernate para la interfaz SET.
El objeto PersistentSet Collection no captura realmente los datos de la tabla de datos subyacente, por lo que es naturalmente imposible inicializar realmente el objeto de dirección en la recopilación. Sin embargo, la colección PersistentSet tiene un atributo de sesión, que es la sesión de Hibernate. Cuando el programa necesita acceder al elemento de recopilación PersistentSet, PersistentSet usará este atributo de sesión para obtener los registros de datos correspondientes al objeto de dirección real.
Entonces, ¿qué obtiene exactamente los registros de datos correspondientes a esas entidades de dirección? Esto no es difícil para PersistentSetet, porque también hay un atributo del propietario en la colección PersistentSet, lo que indica la entidad de la persona a la que pertenece el objeto de dirección. Hibernate buscará los datos de la tabla de datos correspondiente a la dirección correspondiente a la tabla de datos.
Por ejemplo, hacemos clic en la línea de direcciones en la ventana que se muestra en la Figura 2, lo que significa que le dijimos a ECLIPSE que debuge y genere el atributo de direcciones. Esto es para acceder al atributo de direcciones. En este momento, puede ver las siguientes declaraciones SQL en la ventana de la consola Eclipse:
Seleccione direcciones 0_.person_id como persona1_0_0_, direcciones0_.detail como detalles0_, direcciones0_.zip como zip0_from persona_address direcciones0_where direcciones 0_.person_id =?
Esta es la colección PersistentSet y las declaraciones SQL que capturan registros de dirección específicos de acuerdo con el atributo del propietario. En este momento, puede ver la salida que se muestra en la Figura 3 desde la ventana Variables de Eclipse:
Figura 3. Valores de atributo de colección cargados
Como se puede ver en la Figura 3, el atributo de direcciones en este momento se ha inicializado, y el conjunto contiene 2 objetos de dirección, que son los dos objetos de dirección asociados con la entidad de la persona.
De la introducción anterior, podemos ver que la clave para retrasar la carga de atributos establecidos de Hibernate se encuentra en la clase de implementación de PersistentSet. Durante la carga perezosa, la colección PersistentSet no contiene ningún elemento. Sin embargo, PersistentSet celebrará una sesión de hibernación, lo que puede garantizar que cuando el programa necesite acceder a la recopilación, el registro de datos se carga "inmediatamente" y cargue los elementos de recopilación.
Similar a la clase de implementación de PersistentSet, Hibernate también proporciona listas persistentes, PersistentMap, PersistentSortedMap, PersistentStededset y otras clases de implementación, y sus funciones son más o menos similares a las de PersistentSet.
Los lectores que estén familiarizados con los atributos de la colección Hibernate deben recordar: Hibernate requiere que declarar los atributos de recolección solo se puedan usar con interfaces como Set, List, Map, SortedSet, SortedMap, etc., y no se pueden implementar utilizando Hashset, ArrayList, Hashmap, TreeSet, TreeMap y otras clases de implementación. La razón es que Hibernate necesita retrasar la carga de los atributos de la recolección, y la carga de retraso de Hibernate se basa en PersistentSet, PersistentList, PersistentMap, PersistentStentStedMap y PersistentStedEdset para completar, es decir, la implementación de la colección de la colección, en lugar de la colección de colección, en lugar de la colección de colección. atributos.
Hibernate utiliza la carga perezosa para los atributos de recolección de forma predeterminada. En algunos casos especiales, establezca el atributo perezoso = "falso" para elementos como <set .../>, <list .../>, <map .../> para cancelar la carga perezosa.
Retrasar la carga de entidades asociadas
Por defecto, Hibernate también utilizará la carga perezosa para cargar la entidad asociada. Ya sea que se trate de una asociación de uno a muchos, una asociación individual o una asociación de muchos a muchos, Hibernate usará la carga perezosa de forma predeterminada.
Para las entidades asociadas, se pueden dividir en dos casos:
1. Cuando una entidad asociada es múltiple entidades (incluidas una a gran parte, muchos a muchos): en este momento, la entidad asociada existirá en forma de colección, y Hibernate utilizará el conjunto de persistentes, la lista persistente, el mapa persistente, el mapa de los persistentes, el conjunto de persistentes y otras colecciones para manejar las entidades de carga perezosa. Esta es la situación introducida anteriormente.
2. Cuando una entidad asociada es una entidad única (que incluye uno a uno y muchos a uno): cuando Hibernate carga una entidad, la entidad asociada retrasada será un objeto proxy generado dinámicamente.
Cuando la entidad asociada es una entidad única, es decir, cuando la entidad asociada se asigna utilizando <muchos a uno .../> o <uno a uno .../>, estos dos elementos también pueden especificar la carga perezosa a través del atributo perezoso.
El siguiente ejemplo también asigna la clase de direcciones a una clase persistente. En este momento, la clase de direcciones también se convierte en una clase de entidad, y la entidad persona y la entidad de la dirección forman una asociación bidireccional de uno a muchos. El código de archivo de asignación en este momento es el siguiente:
Listado 3. Person.hbm.xml
<? xml versión = "1.0" encoding = "gbk"?> <!-Especifique la información DTD para Hibernate-> <! DocType Public de mapeo de hibernación "-// Hibernate/Hibernate DTD 3.0 // en "" http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd "> <hibernate-mapping paquete =" org.crazyit.app.domain "> <!-mapeo de la persona de la persona de la persona-> <nombre de clase =" persona "tabla =" persona_inf "> <!-mapeando identificación de propiedad de la persona-" columna = "persona_id"> <!-Define la política del generador de clave primaria-> <generador/> <!-Se usa para asignar atributos comunes-> <propiedad name = "name" type = "string"/> <propiedad name = "edad" type = "int"/> < inverse = "true"> <!-Especifique la columna de clave extranjera asociada-> <key columna = "persona_id"/> <!-se usa para asignar a los atributos de clase asociados-> <One-O-Many/> </set> </class> <!-Map de la dirección persistente-> <class name = "dirección" Tabla = "dirección_inf"> <!-Identificación de mapa de identificación de la dirección-> <d name = "Dirección" Dirección "Dirección" Dirección "COLUNTA" COLUNTULAR "DIRECCIÓN". Specify primary key generator policy--><generator/></id><!-- Map Normal attribute detail --><property name="detail"/><!-- Map Normal attribute zip --><property name="zip"/><!-- The column name must be specified as person_id, which is the same as the column attribute value of the key element in the associated entity--><many-to-one name="person"column="person_id" no-null = "true"/> </class> </hibernate-mapping>
A continuación, el programa carga la entidad de la persona con ID 1 a través del siguiente fragmento de código:
// Abra la sesión de sesiones dependiente del contexto = sf.getCurrentSession (); transacción tx = session.begintransaction (); dirección direccione = (dirección) session.get (direcciones.class, 1); // <1> System.out.println (direcciones.getDetail ());
Para ver el procesamiento de la entidad asociada de Hibernate al cargar la entidad de la dirección, establecemos un punto de ruptura en el código <1> y lo depugemos en Eclipse. En este momento, podemos ver que la ventana de la consola Eclipse genera la siguiente instrucción SQL:
Seleccione Dirección0_.Address_ID como dirección1_1_0_, dirección0_.detail como detall1_0_, direcciones0_.zip como zip1_0_, direcciones0_.person_id como persona4_1_0_ desde direcciones_inf direccionar0_where direccional0_.address_id =?
No es difícil ver en esta declaración SQL que Hibernate carga la tabla de datos correspondiente a la entidad de la dirección para rastrear registros, pero no rastrea los registros de la tabla de datos correspondiente a la entidad de la persona, que es que la carga perezosa juega un papel.
Desde la ventana Variables de Eclipse, vea la salida que se muestra en la Figura 4:
Figura 4. Entidad de carga retrasada
Se puede ver claramente en la Figura 4 que la entidad de la persona asociada con la entidad de la dirección no es un objeto de persona, sino una instancia de la clase de la persona _ $$ _ Javassist_0. Esta clase es una clase proxy generada dinámicamente por Hibernate usando el proyecto Javassist. Cuando los retrasos de hibernación que cargan la entidad asociada, Javassist se utilizará para generar un objeto proxy dinámico, y este objeto proxy será responsable de representar el "no cargado todavía".
Mientras la aplicación necesite usar una entidad asociada que todavía "aún no esté cargada", la persona _ $$ _ Javassist_0 Proxy Object será responsable de cargar la entidad asociada real y devolver la entidad asociada real: este es el patrón proxy más típico.
Haga clic en el atributo de la persona en la ventana Variables que se muestra en la Figura 4 (es decir, obliga al atributo de la persona a usar en modo de depuración), y luego verá la siguiente salida de declaraciones SQL en la ventana de la consola de Eclipse:
Seleccione Person0_.person_id como Person1_0_0_, Person0_.name as name0_0_, Person0_.age como Age0_0_ de Person_inf Person0_where Person0_.person_id =?
La declaración SQL anterior es una declaración que captura la entidad asociada de "carga de retraso". En este momento, puede ver los resultados que se muestran en la Figura 5 de la salida de la ventana de variables:
Figura 5. Entidad cargada
Hibernate adopta el modo de "carga retrasada" para administrar entidades asociadas. De hecho, al cargar la entidad principal, realmente no toma los datos correspondientes de la entidad asociada, sino que solo genera dinámicamente un objeto como proxy de la entidad asociada. Cuando una aplicación realmente necesita usar una entidad asociada, el objeto proxy es responsable de obtener registros de la base de datos subyacente e inicializar la entidad asociada real.
En la carga de retraso de hibernación, lo que el programa del cliente comienza a obtener es un objeto proxy generado dinámicamente, mientras que la entidad real se delega al objeto proxy para la administración; este es el patrón de proxy típico.
Modo de agente
El modo proxy es un modo de diseño con una aplicación muy amplia. Cuando el código del cliente necesita llamar a un objeto, al cliente en realidad no le importa si obtener el objeto con precisión. Solo necesita un objeto que pueda proporcionar la función. En este momento, podemos devolver el proxy (proxy) del objeto.
En este método de diseño, el sistema proporcionará un objeto un objeto proxy, y el objeto proxy controla la referencia al objeto de origen. Un proxy es un objeto Java que actúa en nombre de otro objeto Java. En algunos casos, el código del cliente no quiere o no puede llamar directamente al Callee, y el objeto proxy puede actuar como un intermediario entre el cliente y el objeto de destino.
Para los clientes, no puede distinguir la diferencia entre un objeto proxy y un objeto real, ni debe distinguir la diferencia entre un objeto proxy y un objeto real. El código del cliente no conoce el objeto proxy real. El código del cliente está orientado a la interfaz y solo contiene una interfaz del objeto proxy.
En resumen, siempre que el código del cliente no pueda o no desee acceder directamente al objeto llamado: hay muchas razones para esta situación, como crear un objeto con una sobrecarga de alto sistema, o el objeto llamado está en un host remoto, o la función del objeto de destino no es suficiente para satisfacer las necesidades ..., pero se crea un objeto proxy adicional para devolverlo al cliente para su uso, por lo que este método de diseño es el modo proxy.
Lo siguiente demuestra un modo de proxy simple. El programa primero proporciona una interfaz de imagen, que representa la interfaz implementada por un objeto de imagen grande. El código de la interfaz es el siguiente:
Listado 3. Image.java
Imagen de interfaz pública {void show ();}Esta interfaz proporciona una clase de implementación que simula un objeto de imagen grande, y el constructor de la clase de implementación utiliza el método Thread.sleep () para pausar 3s. A continuación se muestra el código del programa para BigImage.
Listado 4. Bigimage.java
// Use este BigImage para simular una imagen de gran imagen BigImage implementa la imagen {public bigImage () {try {// Program Pausa del sistema de simulación de modo 3S System Subterhead.sleep (3000); System.out.println ("Carga de imagen con éxito ...");} Catch (InterruptedException ex) {ex.printstackTrace ();}} // Implementar el método show () en imagen public void show () {system.out.println ("dibujar la imagen grande real");}}}El código del programa anterior se detiene 3S, lo que indica que se necesitan una sobrecarga de tiempo 3 para crear un objeto BigImage: el programa utiliza este retraso para simular la sobrecarga del sistema causada por la carga de esta imagen. Si no se usa el modo proxy, el sistema generará un retraso 3S cuando se cree BigImage en el programa. Para evitar este retraso, el programa proporciona un objeto proxy para el objeto BigImage, y la clase proxy de la clase BigImage es la siguiente.
Listado 5. ImageProxy.Java
clase pública pública de imagen ImagenProximy implementa imagen {// Combinar una instancia de imagen como la imagen de imagen privada del objeto proxy Object Project; // use entidades abstractas para inicializar el objeto proxy Object Public ImageProxy (Image Image) {this.Image = Image;}/*** Reescribir el método Show () Show () de la interfaz de imagen* Este método se usa para controlar el acceso al objeto proxy proxy,* y es responsable de crear y eliminar el objeto proxy*/se usa este método. show () {// crea el objeto proxy solo si (image == null) {image = new bigImage ();} image.show ();}}La clase de proxy ImageProxy anterior implementa el mismo método show () como BigImage, que permite que el código del cliente use el objeto proxy como bigImage después de obtener el objeto proxy.
La lógica de control se agrega al método show () de la clase ImageProxy. Esta lógica de control se utiliza para controlar que el objeto proxy BigImage se creará solo cuando el sistema realmente llame al programa () de la imagen. El siguiente programa debe usar el objeto BigImage, pero el programa no devuelve directamente la instancia de BigImage, pero primero devuelve el objeto BigImage Proxy, como se muestra en el siguiente programa.
Listado 6. bigimagetest.java
public class bigImageTest {public static void main (string [] args) {long start = system.currentTimemillis (); // El programa devuelve un objeto de imagen, que es solo el objeto proxy de BigImage Image de imagen = nueva ImageProxy (null); System.out.Println ("La hora por encima del sistema que obtiene el objeto de imagen:" +(System.CurrentRentRentRent () - - - - - - - - - - - - - - -// -// -// -// -//// -//////// -//////// -//////////////////////////: El programa realmente creará el objeto proxy cuando realmente se llama el método show () del proxy de la imagen. image.show ();}}El programa anterior inicializa la imagen muy rápidamente porque el programa realmente no crea el objeto BigImage, pero solo obtiene el objeto de proxy ImageProxy: hasta que el programa llame al método Image.show (), el programa debe llamar al método show () del objeto BigImage, y el programa realmente crea el objeto BigImage en este momento. Ejecute el programa anterior y vea los resultados que se muestran en la Figura 6.
Figura 6. Mejore el rendimiento utilizando el modo proxy
Al ver los resultados en ejecución que se muestran en la Figura 6, los lectores deben poder aceptar que el uso del modo proxy mejora el rendimiento del sistema de obtener objetos de imagen. Pero algunos lectores pueden hacer preguntas: cuando un programa llama al método show () del objeto ImageProxy, también necesita crear un objeto BigImage, pero la sobrecarga del sistema no se ha reducido realmente. ¿Es solo que esta sobrecarga del sistema se retrasa?
Podemos responder esta pregunta desde las siguientes dos perspectivas:
Retrasar la creación de BigImage hasta que sea realmente necesaria puede garantizar el funcionamiento sin problemas del programa anterior y reducir el tiempo de supervivencia de BigImage en la memoria, ahorrando el sobrecarga de memoria del sistema desde una perspectiva macro.
En algunos casos, tal vez el programa nunca llame al método show () del objeto ImageProxy, lo que significa que el sistema no necesita crear un objeto BigImage en absoluto. En este caso, el uso del modo proxy puede mejorar significativamente el rendimiento de la operación del sistema.
Totalmente similar, Hibernate también usa el modo proxy para "retrasar" el tiempo para cargar la entidad asociada. Si el programa no necesita acceder a la entidad asociada, el programa no rastreará la entidad asociada. Esto puede guardar la sobrecarga de memoria del sistema y acortar el tiempo cuando Hibernate carga la entidad.
resumen
Hibernate Lazy Load es esencialmente una aplicación del modo proxy. En los últimos años, a menudo hemos utilizado el modo proxy para reducir la sobrecarga de la memoria del sistema y mejorar el rendimiento de la aplicación. Hibernate aprovecha esta ventaja del modo proxy y combina Javassist o CGLIB para generar dinámicamente objetos proxy, lo que agrega flexibilidad al modo proxy. Hibernate le da a este uso un nuevo nombre: Carga perezosa. En cualquier caso, analizar y comprender completamente la implementación de estos marcos de código abierto puede experimentar mejor las ventajas de los modelos de diseño clásicos.
Espero que la descripción en este artículo sea útil para la programación Java de todos en función del marco Hibernate.