Hibernate.orgCommunity Documentation

Capítulo 20. Mejoramiento del rendimiento

20.1. Estrategias de recuperación
20.1.1. Trabajo con asociaciones perezosas
20.1.2. Afinación de las estrategias de recuperación
20.1.3. Proxies de asociaciones de un sólo extremo
20.1.4. Inicialización de colecciones y proxies
20.1.5. Utilización de recuperación de lotes
20.1.6. Utilización de la recuperación por subselección
20.1.7. Perfiles de recuperación
20.1.8. Utilización de la recuperación perezosa de propiedades
20.2. El Caché de Segundo Nivel
20.2.1. Mapeos de caché
20.2.2. Estrategia: sólo lectura
20.2.3. Estrategia: lectura/escritura (read/write)
20.2.4. Estrategia: lectura/escritura no estricta
20.2.5. Estrategia: transaccional
20.2.6. Compatibilidad de proveedor de caché/estrategia de concurrencia
20.3. Gestión de cachés
20.4. El Caché de Consultas
20.4.1. Habilitación del caché de peticiones
20.4.2. Regiones de caché de consultas
20.5. Comprensión del rendimiento de Colecciones
20.5.1. Taxonomía
20.5.2. Las listas, mapas, idbags y conjuntos son las colecciones más eficientes de actualizar
20.5.3. Los Bags y las listas son las colecciones inversas más eficientes
20.5.4. Borrado de un sólo tiro
20.6. Control del rendimiento
20.6.1. Control de una SessionFactory
20.6.2. Métricas

Hibernate utiliza una estrategia de recuperación para recuperar los objetos asociados cuando la aplicación necesita navegar la asociación. Las estrategias de recuperación se pueden declarar en los metadatos de mapeo O/R, o se pueden sobrescribir por medio de una HQL particular o una petición Criteria.

Hibernate3 define las siguientes estrategias de recuperación:

Hibernate también distingue entre:

Aquí tenemos dos nociones ortogonales: cuándo se recupera la aplicación, y cómo se recupera. Es importante que no las confunda. Utilizamos fetch para afinar el rendimiento. Podemos usar lazy para definir un contrato sobre qué datos están siempre disponibles en cualquier instancia separada de una clase en particular.

Por defecto, Hibernate3 usa una recuperación perezosa por selección para colecciones y una recuperación por proxy perezosa para asociaciones monovaluadas. Estas políticas predeterminadas tienen sentido para casi todas las asociaciones en la mayoría de las aplicaciones.

Si configura hibernate.default_batch_fetch_size, Hibernate utilizará la optimización de recuperación en lotes para recuperación perezosa. Esta optimización también se puede habilitar en un nivel más detallado.

Note que el acceder a una asociación perezosa fuera del contexto de una sesión de Hibernate abierta resultará en una excepción. Por ejemplo:

= sessions.openSession();

Transaction tx = s.beginTransaction();
            
User u = (User) s.createQuery("from User u where u.name=:userName")
    .setString("userName", userName).uniqueResult();
Map permissions = u.getPermissions();
tx.commit();
s.close();
Integer accessLevel = (Integer) permissions.get("accounts");  // Error!

Ya que la colección de permisos no fue inicializada cuando se cerró la Session, la colección no será capaz de cargar su estado. Hibernate no soporta la inicialización perezosa de objetos separados. La solución es mover el código que lee de la colección a justo antes de que se guarde la transacción.

Opcionalmente puede utilizar una colección no perezosa o asociación, especificando lazy="false" para el mapeo de asociación. Sin embargo, el propósito de la inicialización perezosa es que se utilice para casi todas las colecciones y asociaciones. ¡Si define demasiadas asociaciones no perezosas en su modelo de objetos, Hibernate recuperará la base de datos entera en toda transacción.

Por otro lado, puede utilizar la recuperación por unión, la cual no es perezosa por naturaleza, en lugar de la recuperación por selección en una transacción en particular. Veremos ahora cómo personalizar la estrategia de recuperación. En Hibernate3, los mecanismos para elegir una estrategia de recuperación son idénticas para las de las asociaciones monovaluadas y las colecciones.

La recuperación por selección (la preestablecida) es extremadamente vulnerable a problemas de selección N+1, de modo que puede que queramos habilitar la recuperación por unión (join fetching) en el documento de mapeo:


<set name="permissions"
            fetch="join">
    <key column="userId"/>
    <one-to-many class="Permission"/>
</set

<many-to-one name="mother" class="Cat" fetch="join"/>

La estrategia de recuperación definida en el documento de mapeo afecta a:

Sin importar que estrategia de recuperación utilice, se garantiza que la gráfica no-perezoza definida será cargada en la memoria. Sin embargo, esto puede causar la utilización de varias selecciones inmediatas para ejecutar una consulta HQL en particular.

Usualmente, no utilizamos el documento de mapeo para personalizar la recuperación. En cambio, mantenemos el comportamiento por defecto y lo sobrescribimos para una transacción en particular, utilizando left join fetch en HQL. Esto le dice a Hibernate que recupere la asociación tempranamente en la primera selección, usando una unión externa. En la API de consulta de Criteria, usted utilizaría setFetchMode(FetchMode.JOIN).

Si quiere cambiar la estrategia de recuperación utilizada por get() o load(); utilice una consulta Criteria. Por ejemplo:

User user = (User) session.createCriteria(User.class)

                .setFetchMode("permissions", FetchMode.JOIN)
                .add( Restrictions.idEq(userId) )
                .uniqueResult();

Esto es el equivalente de Hibernate de lo que otras soluciones ORM denominan un "plan de recuperación".

Un enfoque completamente diferente de evitar problemas con selecciones N+1 es usar el caché de segundo nivel.

La recuperación perezosa de colecciones está implementada utilizando la implementación de colecciones persistentes propia de Hibernate. Sin embargo, se necesita un mecanismo diferente para un comportamiento perezoso en las asociaciones de un sólo extremo. La entidad destino de la asociación se debe tratar con proxies. Hibernate implementa proxies de inicialización perezosa para objetos persistentes utilizando la mejora del código byte en tiempo de ejecución por medio de la biblioteca CGLIB).

En el arranque, Hibernate3 genera proxies por defecto para todas las clases persistentes y los usa para habilitar la recuperación perezosa de asociaciones muchos-a-uno y uno-a-uno.

El archivo de mapeo puede declarar una interfaz a utilizar como interfaz de proxy para esa clase, con el atributo proxy. Por defecto, Hibernate usa una subclase de la clase. La clase tratada con proxies debe implementar un constructor por defecto con al menos visibilidad de paquete. Recomendamos este constructor para todas las clases persistentes.

Hay problemas potenciales que se deben tener en cuenta al extender este enfoque a las clases polimórficas. Por ejemplo:


<class name="Cat" proxy="Cat">
    ......
    <subclass name="DomesticCat">
        .....
    </subclass>
</class
>

Primero, las instancias de Cat nunca serán objeto de un cast a DomesticCat, incluso aunque la instancia subyacente sea una instancia de DomesticCat:

Cat cat = (Cat) session.load(Cat.class, id);  // instantiate a proxy (does not hit the db)

if ( cat.isDomesticCat() ) {                  // hit the db to initialize the proxy
    DomesticCat dc = (DomesticCat) cat;       // Error!
    ....
}

Segundo, es posible romper el proxy ==:

Cat cat = (Cat) session.load(Cat.class, id);            // instantiate a Cat proxy

DomesticCat dc = 
        (DomesticCat) session.load(DomesticCat.class, id);  // acquire new DomesticCat proxy!
System.out.println(cat==dc);                            // false

Sin embargo, la situación no es en absoluto tan mala como parece. Aunque tenemos ahora dos referencias a objetos proxy diferentes, la instancia subyacente será aún el mismo objeto:

cat.setWeight(11.0);  // hit the db to initialize the proxy

System.out.println( dc.getWeight() );  // 11.0

Tercero, no puede usar un proxy CGLIB para una clase final o una clase con algún método final.

Finalmente, si su objeto persistente adquiere cualquier recurso bajo instanciación (por ejemplo, en inicializadores o constructores por defecto), entonces esos recursos serán adquiridos también por el proxy. La clase del proxy es una subclase real de la clase persistente.

Estos problemas se deben a limitaciones fundamentales en el modelo de herencia única de Java. Si desea evitar estos problemas cada una de sus clases persistentes deben implementar una interfaz que declare sus métodos de negocio. Debe especificar estas interfaces en el archivo de mapeo en donde CatImpl implementa la interfaz Cat y DomesticCatImpl implementa la interfaz DomesticCat. Por ejemplo:


<class name="CatImpl" proxy="Cat">
    ......
    <subclass name="DomesticCatImpl" proxy="DomesticCat">
        .....
    </subclass>
</class
>

Entonces los proxies para las instancias de Cat y DomesticCat pueden ser retornadas por load() o iterate().

Cat cat = (Cat) session.load(CatImpl.class, catid);

Iterator iter = session.createQuery("from CatImpl as cat where cat.name='fritz'").iterate();
Cat fritz = (Cat) iter.next();

Las relaciones también son inicializadas perezosamente. Esto significa que debe declarar cualquier propiedad como de tipo Cat, no CatImpl.

Ciertas operaciones no requieren inicialización de proxies:

Hibernate detectará las clases persistentes que sobrescriban equals() o hashCode().

Al escoger lazy="no-proxy" en vez del lazy="proxy" predeterminado, podemos evitar los problemas asociados con conversión de tipos (typecasting). Sin embargo, requiere la instrumentación de código byte en tiempo estimado de construcción y todas las operaciones resultarán en una inicialización de proxies inmediata.

Hibernate lanzará una LazyInitializationException si se accede a una colección o proxy sin acceder fuera del ámbito de la Session, por ejemplo, cuando la entidad que posee la colección o que tiene la referencia al proxy esté en el estado separado.

A veces es necesario inicializar un proxy o una colección antes de cerrar la Session. Puede forzar la inicialización llamando a cat.getSex() o cat.getKittens().size(), por ejemplo. Sin embargo, esto puede ser confuso para los lectores del código y no es conveniente para el código genérico.

Los métodos estáticos Hibernate.initialize() y Hibernate.isInitialized() proporcionan a la aplicación una forma conveniente de trabajar con colecciones o proxies inicializados perezosamente. Hibernate.initialize(cat) forzará la inicialización de un proxy, cat, en tanto su Session esté todavía abierta. Hibernate.initialize( cat.getKittens() ) tiene un efecto similar para la colección de gatitos.

Otra opción es mantener la Session abierta hasta que todas las colecciones y proxies necesarios hayan sido cargados. En algunas arquitecturas de aplicación, particularmente en aquellas donde el código que accede a los datos usando Hibernate, y el código que los utiliza están en capas de aplicación diferentes o procesos físicos diferentes, puede ser un problema asegurar que la Session esté abierta cuando se inicializa una colección. Existen dos formas básicas para abordar este tema:

A veces no quiere inicializar una colección grande, pero todavía necesita alguna información sobre ella como por ejemplo, su tamaño o un subconjunto de los datos.

Puede utilizar un filtro de colecciones para obtener el tamaño de una colección sin inicializarla:

( (Integer) s.createFilter( collection, "select count(*)" ).list().get(0) ).intValue()

El método createFilter() también se utiliza para recuperar eficientemente subconjuntos de una colección sin necesidad de inicializar toda la colección:

s.createFilter( lazyCollection, "").setFirstResult(0).setMaxResults(10).list();

Usando la recuperación por lotes, Hibernate puede cargar varios proxies sin inicializar si se accede a un proxy. La recuperación en lotes es una optimización de la estrategia de recuperación por selección perezosa. Hay dos formas en que puede configurar la recuperación en lotes: a nivel de la clase y a nivel de colección.

La recuperación en lotes para clases/entidades es más fácil de entender. Considere el siguiente ejemplo: en tiempo de ejecución tiene 25 instancias de Cat cargadas en una Session y cada Cat tiene una referencia a su owner, una Person. La clase Person está mapeada con un proxy, lazy="true". Si ahora itera a través de todos los cats y llama a getOwner() para cada uno, Hibernate por defecto, ejecutará 25 declaraciones SELECT para recuperar los dueños proxies. Puede afinar este comportamiento especificando un batch-size en el mapeo de Person:


<class name="Person" batch-size="10"
>...</class
>

Hibernate ahora ejecutará sólamente tres consultas: el patrón es 10, 10, 5.

También puede habilitar la recuperación en lotes para colecciones. Por ejemplo, si cada Person tiene una colección perezosa de Cats y hay 10 personas actualmente cargadas en la Session, iterar a través de las 10 personas generará 10 SELECTs, uno para cada llamada a getCats(). Si habilita la recuperación en lotes para la colección de cats en el mapeo de Person, Hibernate puede recuperar por adelantado las colecciones:


<class name="Person">
    <set name="cats" batch-size="3">
        ...
    </set>
</class
>

Con un batch-size de 3, Hibernate cargará las colecciones 3, 3, 3, 1 en cuatro SELECTs. Una vez más, el valor del atributo depende del número esperado de colecciones sin inicializar en una Session en particular.

La recuperación de colecciones en lotes es particularmente útil si tiene un árbol anidado de ítems, por ejemplo, el típico patrón de cuenta de materiales. Sin embargo, un conjunto anidado o una ruta materializada podría ser una mejor opción para árboles que sean de lectura en la mayoría de los casos.

Otra manera de modificar la estrategia de recuperación para cargar objetos asociados es por medio de algo llamado un perfil de recuperación, el cual es una configuración nombrada asociada con la org.hibernate.SessionFactory pero habilitada por nombre en la org.hibernate.Session. Una vez habilitada en una org.hibernate.Session, el perfil de recuperación estará en efecto para esa org.hibernate.Session hasta que se deshabilite explícitamente.

¿Entonces qué significa? Vamos a explicarlo por medio de un ejemplo. Digamos que tenemos los siguientes mapeos:


<hibernate-mapping>
    <class name="Customer">
        ...
        <set name="orders" inverse="true">
            <key column="cust_id"/>
            <one-to-many class="Order"/>
        </set>
    </class>
    <class name="Order">
        ...
    </class>
</hibernate-mapping
>

Ahora normalmente cuando reciba una referencia a un cliente en particular, el grupo de pedidos será perezoso lo que significa que aún no habremos cargado esos pedidos de la base de datos. Usualmente esto está bien. Ahora digamos que tiene un ejemplo en donde es más eficiente el cargar el cliente y sus pedidos juntos. Una manera es utilizar las estrategias de "recuperación dinámica" por medio de peticiones de criterio o un HQL. Pero otra opción es utilizar un perfi de recuperación para lograr esto. Simplemente agregue lo siguiente a su mapeo:


<hibernate-mapping>
    ...
    <fetch-profile name="customer-with-orders">
        <fetch entity="Customer" association="orders" style="join"/>
    </fetch-profile>
</hibernate-mapping
>

o incluso:


<hibernate-mapping>
    <class name="Customer">
        ...
        <fetch-profile name="customer-with-orders">
            <fetch association="orders" style="join"/>
        </fetch-profile>
    </class>
    ...
</hibernate-mapping
>

Ahora el siguiente código de hecho cargará el cliente y sus órdenes:



    Session session = ...;
    session.enableFetchProfile( "customer-with-orders" );  // name matches from mapping
    Customer customer = (Customer) session.get( Customer.class, customerId );

Actualmente solo se soportan los perfiles de recuperación de estilo unido pero se planear soportar estilos adicionales. Consulte HHH-3414 para obtener mayores detalles.

Hibernate3 soporta la recuperación perezosa de propiedades individuales. Esta técnica de optimización también es conocida como grupos de recuperación (fetch groups). Por favor, note que éste es principalmente un aspecto de marketing, ya que en la práctica, optimizar las lecturas de filas es mucho más importante que la optimización de lectura de columnas. Sin embargo, cargar sólo algunas propiedades de una clase podría ser útil en casos extremos. Por ejemplo, cuando las tablas heredadas tienen cientos de columnas y el modelo de datos no puede ser mejorado.

Para habilitar la carga perezosa de propiedades, establezca el atributo lazy en sus mapeos de propiedades:


<class name="Document">
       <id name="id">
        <generator class="native"/>
    </id>
    <property name="name" not-null="true" length="50"/>
    <property name="summary" not-null="true" length="200" lazy="true"/>
    <property name="text" not-null="true" length="2000" lazy="true"/>
</class
>

La carga perezosa de propiedades requiere la instrumentación del código byte en tiempo de construcción. Si sus clases persistentes no se mejoran, Hibernate ignorará la configuración perezosa de propiedades y retornará a la recuperación inmediata.

Para la instrumentación del código byte, utilice la siguiente tarea Ant:


<target name="instrument" depends="compile">
    <taskdef name="instrument" classname="org.hibernate.tool.instrument.InstrumentTask">
        <classpath path="${jar.path}"/>
        <classpath path="${classes.dir}"/>
        <classpath refid="lib.class.path"/>
    </taskdef>

    <instrument verbose="true">
        <fileset dir="${testclasses.dir}/org/hibernate/auction/model">
            <include name="*.class"/>
        </fileset>
    </instrument>
</target
>

Una forma diferente de evitar lecturas innecesarias de columnas, al menos para transacciones de sólo lectura es utilizar las funcionalidades de proyección de consultas HQL o Criteria. Esto evita la necesidad de procesar el código byte en tiempo de construcción y ciertamente es la solución preferida.

Puede forzar la usual recuperación temprana de propiedades utilizando fetch all properties en HQL.

Una Session de Hibernate es un caché de datos persistentes a nivel de transacción. Es posible configurar un clúster o caché a nivel de MVJ (a nivel de SessionFactory) sobre una base de clase-por-clase o colección-por-colección. Incluso puede enchufar un caché en clúster. Tenga en cuenta de que los cachés nunca están al tanto de los cambios que otra aplicación haya realizado al almacén persistente. Sin embargo, se pueden configurar para que los datos en caché expiren regularmente.

Tiene la opción de decirle a Hibernate cual implementación de caché utilizar al especificar el nombre de una clase que implemente org.hibernate.cache.CacheProvider utilizando la propiedad hibernate.cache.provider_class. Hibernate viene vinculada con un número de integraciones incorporadas con los proveedores caché de código abierto enumerados a continuación. Además puede implementar el suyo y enchufarlo como se explicó anteriormente. Observe que las versiones anteriores a 3.2 por defecto utilizan EhCache como proveedor de caché.


El elemento <cache> de un mapeo de clase o colección tiene la siguiente forma:

<cache
    usage="tra(1)nsactional|read-write|nonstrict-read-write|read-only"
    region="Re(2)gionName"
    include="a(3)ll|non-lazy"
/>

1

usage especifica la estrategia de caché: transactional, read-write, nonstrict-read-write o read-only

2

region (opcional: por defecto es el nombre del rol de la clase o colección): especifica el nombre de la región de caché de segundo nivel.

3

include (opcional: por defecto es all) non-lazy: especifica que las propiedades de la entidad mapeadas con lazy="true" no se pueden poner en caché cuando se habilita la recuperación perezoza a nivel de atributos.

Opcionalmente, puede especificar los elementos <class-cache> y <collection-cache> en hibernate.cfg.xml.

El atributo usage especifica una estrategia de concurrencia al caché.

Siempre que pase un objeto a save(), update() o saveOrUpdate() y siempre que recupere un objeto utilizando load(), get(), list(), iterate() o scroll(), ese objeto se agrega al caché interno de la Session.

Cuando luego se llame a flush(), el estado de ese objeto será sincronizado con la base de datos. Si no quiere que ocurra esta sincronización o si está procesando un número enorme de objetos y necesita gestionar la memoria eficientemente, puede utilizar el método evict() para quitar el objeto y sus colecciones del caché de primer nivel.

ScrollableResult cats = sess.createQuery("from Cat as cat").scroll(); //a huge result set

while ( cats.next() ) {
    Cat cat = (Cat) cats.get(0);
    doSomethingWithACat(cat);
    sess.evict(cat);
}

La Session también proporciona un método contains() para determinar si una instancia pertenece al caché de la sesión.

Para expulsar todos los objetos del caché de sesión, llame a Session.clear().

Para el caché de segundo nivel, hay métodos definidos en SessionFactory para explusar el estado en caché de una instancia, clase entera, instancia de colección o rol entero de colección.

sessionFactory.evict(Cat.class, catId); //evict a particular Cat

sessionFactory.evict(Cat.class);  //evict all Cats
sessionFactory.evictCollection("Cat.kittens", catId); //evict a particular collection of kittens
sessionFactory.evictCollection("Cat.kittens"); //evict all kitten collections

El CacheMode controla la manera en que interactúa una sesión en particular con el caché de segundo nivel:

Para navegar por los contenidos de una región de caché de segundo nivel o de consultas, use la API de Statistics:

Map cacheEntries = sessionFactory.getStatistics()

        .getSecondLevelCacheStatistics(regionName)
        .getEntries();

Necesitará habilitar las estadísticas y, opcionalmente, forzar a Hibernate para que guarde las entradas del caché en un formato más fácil de entender para humanos:

hibernate.generate_statistics true
hibernate.cache.use_structured_entries true

Los conjuntos de resultados de peticiones también pueden ponerse en caché. Esto sólamente es útil para consultas que se ejecutan frecuentemente con los mismos parámetros.

El poner en caché los resultados de una petición introduce algunos sobrecostos en términos del procesamiento transaccional normal de sus aplicaciones. Por ejemplo, si pone en caché los resultados de una petición frente a Person, Hibernate necesitará rastrear cuando se deben invalidar esos resultados debido a los cambios que se han guardado en Person. Eso más el hecho de que la mayoría de las aplicaciones simplemente no ganan beneficio de poner los resultados en caché, lleva a Hibernate a deshabilitar el caché de los resultados de una petición por defecto. Para utilizar el caché de peticiones primero necesita habilitar el caché de peticiones:

hibernate.cache.use_query_cache true

Esta configuración crea dos nuevas regiones de caché:

Como lo mencionamos anteriormente, la mayoría de las consultas no se benefician del caché o de sus resultados; de modo que por defecto las consultas individuales no se ponen en caché incluso después de habilitar el caché para peticiones. Para habilitar el caché de resultados para una petición en particular, llame a org.hibernate.Query.setCacheable(true). Esta llamada permite que la consulta busque resultados existentes en caché o que agregue sus resultados al caché cuando se ejecuta.

En las secciones anteriores hemos abordado las colecciones y sus aplicaciones. En esta sección exploramos algunos puntos en relación con las colecciones en tiempo de ejecución.

Hibernate define tres tipos básicos de colecciones:

Esta clasificación distingue las varias tablas y relaciones de clave foránea pero no nos dice absolutamente todo lo que necesitamos saber sobre el modelo relacional. Para entender completamente la estructura relacional y las características de rendimiento, debemos considerar la estructura de la clave primaria que Hibernate utiliza para actualizar o borrar filas de colección. Esto sugiere la siguiente clasificación:

Todas las colecciones indexadas (mapas, listas y arrays) tienen una clave principal que consiste de las columnas <key> e <index>. En este caso las actualizaciones de colecciones son extremadamente eficientes. La clave principal puede ser indexada eficientemente y una fila en particular puede ser localizada cuando Hibernate intenta actualizarla o borrarla.

Los conjuntos tienen una clave principal que consiste de <key> y columnas de elementos. Esto puede ser menos eficiente para algunos tipos de elementos de colección, particularmente elementos compuestos o texto largo o campos binarios ya que la base de datos puede no ser capaz de indexar una clave principal compleja eficientemente. Sin embargo, para asociaciones uno a muchos o muchos a muchos, particularmente en el caso de los identificadores sintéticos, es probable que sólo sea igual de eficiente. Si quiere que SchemaExport realmente cree la clave principal de un <set>, tiene que declarar todas las columnas como not-null="true".

Los mapeos de <idbag> definen una clave delegada, de modo que siempre resulten eficientes de actualizar. De hecho, son el mejor caso.

Los bags son el peor caso ya que un bag permite valores de elementos duplicados y no tiene ninguna columna índice, no puede definirse ninguna clave principal. Hibernate no tiene forma de distinguir entre filas duplicadas. Hibernate resuelve este problema quitando por completo con un sólo DELETE y recreando la colección siempre que cambia. Esto puede ser muy ineficiente.

Para una asociación uno-a-muchos, la "clave principal" puede no ser la clave principal física de la tabla de la base de datos. Incluso en este caso, la clasificación anterior es útil todavía. Refleja cómo Hibernate "localiza" filas individuales de la colección.

De la discusión anterior, debe quedar claro que las colecciones indexadas y los conjuntos permiten una operación más eficiente en términos de agregar, quitar y actualizar elementos.

Discutiblemente, hay una ventaja más de las colecciones indexadas sobre otros conjuntos para las asociaciones muchos a muchos o colecciones de valores. Debido a la estructura de un Set, Hibernate ni siquiera actualiza una fila con UPDATE cuando se "cambia" un elemento. Los cambios a un Set siempre funcionan por medio de INSERT y DELETE de filas individuales. Una vez más, esta consideración no se aplica a las asociaciones uno a muchos.

Después de observar que los arrays no pueden ser perezosos, podríamos concluir que las listas, mapas e idbags son los tipos más eficientes de colecciones (no inversas), con los conjuntos (sets) no muy atrás. Se espera que los sets sean el tipo más común de colección en las aplicaciones de Hibernate. Esto se debe a que la semántica de los sets es la más natural en el modelo relacional.

Sin embargo, en modelos de dominio de Hibernate bien dieñados, usualmente vemos que la mayoría de las colecciones son de hecho asociaciones uno-a-muchos con inverse="true". Para estas asociaciones, la actualización es manejada por el extremo muchos-a-uno de la asociación, y las consideraciones de este tipo sobre el rendimiento de la actualización de las colecciones simplemente no se aplican.

La optimización no es de mucho uso sin el monitoreo y el acceso a números de rendimiento. Hibernate brinda un rango completo de números sobre sus operaciones internas. Las estadísticas en Hibernate están disponibles por SessionFactory.

Puede acceder a las métricas de SessionFactory de dos formas. Su primera opción es llamar a sessionFactory.getStatistics() y leer o mostrar por pantalla la Statistics por sí mismo.

Hibernate también puede utilizar JMX para publicar las métricas si habilita el MBean StatisticsService. Puede habilitar un sólo MBean para todas sus SessionFactory o una por fábrica. Véa el siguiente código para ver ejemplos de configuración minimalistas:

// MBean service registration for a specific SessionFactory

Hashtable tb = new Hashtable();
tb.put("type", "statistics");
tb.put("sessionFactory", "myFinancialApp");
ObjectName on = new ObjectName("hibernate", tb); // MBean object name
StatisticsService stats = new StatisticsService(); // MBean implementation
stats.setSessionFactory(sessionFactory); // Bind the stats to a SessionFactory
server.registerMBean(stats, on); // Register the Mbean on the server
// MBean service registration for all SessionFactory's

Hashtable tb = new Hashtable();
tb.put("type", "statistics");
tb.put("sessionFactory", "all");
ObjectName on = new ObjectName("hibernate", tb); // MBean object name
StatisticsService stats = new StatisticsService(); // MBean implementation
server.registerMBean(stats, on); // Register the MBean on the server

Puede activar y desactivar el monitoreo de una SessionFactory

Las estadísticas pueden ser reajustadas programáticamente utilizando el método clear(). Puede enviarse un resumen a un registro (a nivel de información) utilizando el método logSummary().

Hibernate proporciona un número de métricas, desde información muy básica hasta la más especializada sólamente relevante en ciertos escenarios. Todos los contadores disponibles se describen en la API de la interfaz Statistics, en tres categorías:

Por ejemplo, puede comprobar el acceso, pérdida y radio de colecciones de entidades y consultas en el caché, y el tiempo promedio que necesita una consulta. Tenga en cuenta que el número de milisegundos está sujeto a una aproximación en Java. Hibernate está vinculado a la precisión de la MVJ, en algunas plataformas esto podría tener incluso una exactitud de 10 segundos.

Se usan getters simples para acceder a la métrica global (por ejemplo, no vinculadas en particular a una entidad, colección, región de caché, etc). Puede acceder a las métricas de una entidad, colección, región de caché en particular a través de su nombre y a través de su representación HQL o SQL para las consultas. Por favor refiérase al Javadoc de la API de Statistics, EntityStatistics, CollectionStatistics, SecondLevelCacheStatistics, y QueryStatistics para obtener más información. El siguiente código es un ejemplo sencillo:

Statistics stats = HibernateUtil.sessionFactory.getStatistics();


double queryCacheHitCount  = stats.getQueryCacheHitCount();
double queryCacheMissCount = stats.getQueryCacheMissCount();
double queryCacheHitRatio =
  queryCacheHitCount / (queryCacheHitCount + queryCacheMissCount);
log.info("Query Hit ratio:" + queryCacheHitRatio);
EntityStatistics entityStats =
  stats.getEntityStatistics( Cat.class.getName() );
long changes =
        entityStats.getInsertCount()
        + entityStats.getUpdateCount()
        + entityStats.getDeleteCount();
log.info(Cat.class.getName() + " changed " + changes + "times"  );

Para trabajar sobre todas las entidades, colecciones, consultas y regiones de cachés, recuperando la lista de nombres de entidades, colecciones, consultas y regiones de cachés con los siguientes métodos: getQueries(), getEntityNames(), getCollectionRoleNames() y getSecondLevelCacheRegionNames().