Hibernate.orgCommunity Documentation

Capítulo 21. Mejoramiento del rendimiento

21.1. Estrategias de recuperación
21.1.1. Trabajo con asociaciones perezosas
21.1.2. Afinación de las estrategias de recuperación
21.1.3. Proxies de asociaciones de un sólo extremo
21.1.4. Inicialización de colecciones y proxies
21.1.5. Utilización de recuperación de lotes
21.1.6. Utilización de la recuperación por subselección
21.1.7. Perfiles de recuperación
21.1.8. Utilización de la recuperación perezosa de propiedades
21.2. El Caché de Segundo Nivel
21.2.1. Mapeos de caché
21.2.2. Estrategia: sólo lectura
21.2.3. Estrategia: lectura/escritura (read/write)
21.2.4. Estrategia: lectura/escritura no estricta
21.2.5. Estrategia: transaccional
21.2.6. Compatibilidad de proveedor de caché/estrategia de concurrencia
21.3. Gestión de cachés
21.4. El Caché de Consultas
21.4.1. Habilitación del caché de peticiones
21.4.2. Regiones de caché de consultas
21.5. Comprensión del rendimiento de Colecciones
21.5.1. Taxonomía
21.5.2. Las listas, mapas, idbags y conjuntos son las colecciones más eficientes de actualizar
21.5.3. Los Bags y las listas son las colecciones inversas más eficientes
21.5.4. Borrado de un sólo tiro
21.6. Control del rendimiento
21.6.1. Control de una SessionFactory
21.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.

Another way to affect the fetching strategy for loading associated objects is through something called a fetch profile, which is a named configuration associated with the org.hibernate.SessionFactory but enabled, by name, on the org.hibernate.Session. Once enabled on a org.hibernate.Session, the fetch profile will be in affect for that org.hibernate.Session until it is explicitly disabled.

So what does that mean? Well lets explain that by way of an example which show the different available approaches to configure a fetch profile:




Now normally when you get a reference to a particular customer, that customer's set of orders will be lazy meaning we will not yet have loaded those orders from the database. Normally this is a good thing. Now lets say that you have a certain use case where it is more efficient to load the customer and their orders together. One way certainly is to use "dynamic fetching" strategies via an HQL or criteria queries. But another option is to use a fetch profile to achieve that. The following code will load both the customer andtheir orders:


Nota

@FetchProfile definitions are global and it does not matter on which class you place them. You can place the @FetchProfile annotation either onto a class or package (package-info.java). In order to define multiple fetch profiles for the same class or package @FetchProfiles can be used.

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.

You have the option to tell Hibernate which caching implementation to use by specifying the name of a class that implements org.hibernate.cache.CacheProvider using the property hibernate.cache.provider_class. Hibernate is bundled with a number of built-in integrations with the open-source cache providers that are listed in Tabla 21.1, “Proveedores de Caché”. You can also implement your own and plug it in as outlined above. Note that versions prior to Hibernate 3.2 use EhCache as the default cache provider.


As we have done in previous chapters we are looking at the two different possibiltites to configure caching. First configuration via annotations and then via Hibernate mapping files.

By default, entities are not part of the second level cache and we recommend you to stick to this setting. However, you can override this by setting the shared-cache-mode element in your persistence.xml file or by using the javax.persistence.sharedCache.mode property in your configuration. The following values are possible:

The cache concurrency strategy used by default can be set globaly via the hibernate.cache.default_cache_concurrency_strategy configuration property. The values for this property are:


Hibernate also let's you cache the content of a collection or the identifiers if the collection contains other entities. Use the @Cache annotation on the collection property.


Ejemplo 21.7, “@Cache annotation with attributes”shows the @org.hibernate.annotations.Cache annotations with its attributes. It allows you to define the caching strategy and region of a given second level cache.


Let's now take a look at Hibernate mapping files. There the <cache> element of a class or collection mapping is used to configure the second level cache. Looking at Ejemplo 21.8, “The Hibernate <cache> mapping element” the parallels to anotations is obvious.


Alternatively to <cache>, you can use <class-cache> and <collection-cache> elements in hibernate.cfg.xml.

Let's now have a closer look at the different usage strategies

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.


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.


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

  • CacheMode.NORMAL: lee ítems desde y escribe ítems hacia el caché del segundo nivel

  • CacheMode.GET: lee ítems del caché del segundo nivel. No escribe al caché de segundo nivel excepto cuando actualiza datos

  • CacheMode.PUT: escribe ítems al caché de segundo nivel. No lee del caché de segundo nivel

  • CacheMode.REFRESH: escribe ítems al caché de segundo nivel. No lee del caché de segundo nivel, saltándose el efecto de hibernate.cache.use_minimal_puts, forzando la actualización del caché de segundo nivel para todos los ítems leídos de la base de datos

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


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:


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().