Hibernate.orgCommunity Documentation

Capítulo 20. Aumentando o desempenho

20.1. Estratégias de Busca
20.1.1. Trabalhando com associações preguiçosas (lazy)
20.1.2. Personalizando as estratégias de busca
20.1.3. Proxies de associação final único
20.1.4. Inicializando coleções e proxies
20.1.5. Usando busca em lote
20.1.6. Usando busca de subseleção
20.1.7. Perfis de Busca
20.1.8. Usando busca preguiçosa de propriedade
20.2. O Cachê de Segundo Nível
20.2.1. Mapeamento de Cache
20.2.2. Estratégia: somente leitura
20.2.3. Estratégia: leitura/escrita
20.2.4. Estratégia: leitura/escrita não estrita
20.2.5. Estratégia: transacional
20.2.6. Compatibilidade de Estratégia de Concorrência de Cache Provedor
20.3. Gerenciando os caches
20.4. O Cache de Consulta
20.4.1. Ativação do cache de consulta
20.4.2. Regiões de cache de consulta
20.5. Entendendo o desempenho da Coleção
20.5.1. Taxonomia
20.5.2. Listas, mapas, bags de id e conjuntos são coleções mais eficientes para atualizar
20.5.3. As Bags e listas são as coleções de inversão mais eficientes.
20.5.4. Deletar uma vez
20.6. Monitorando desempenho
20.6.1. Monitorando uma SessionFactory
20.6.2. Métricas

Uma estratégia de busca é a estratégia que o Hibernate irá usar para recuperar objetos associados se a aplicação precisar navegar pela associação. Estratégias de Busca podem ser declaradas nos metadados de mapeamento O/R, ou sobrescritos por uma consulta HQL ou consulta com Criteria.

Hibernate3 define as seguintes estratégias de busca:

O Hibernate distingue também entre:

Nós temos aqui duas noções ortogonais: quando a associação é buscada e como ela é buscada. É importante que você não os confuda. Nós usamos fetch para ajustar o desempenho. Podemos usar lazy para definir um contrato para qual dado é sempre disponível em qualquer instância desconectada de uma classe particular.

Por padrão, o Hibernate3 usa busca preguiçosa para coleções e busca preguiçosa com proxy para associações de um valor. Esses padrões fazem sentido para quase todas as associações em quase todas a aplicações.

Se você ajustar hibernate. default_batch_fetch_size, o Hibernate irá usar otimização de busca em lote para a busca preguiçosa. Essa otimização pode ser também habilitada em um nível mais fino.

Perceba que o acesso a associações preguiçosas fora do contexto de uma sessão aberta do Hibernate irá resultar numa exceção. Por exemplo:

= 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!

Como a coleção de permissões não foi inicializada quando a Session for fechada, a coleção não poderá carregar o seu estado. O Hibernate não suporta inicialização preguiçosa para objetos desconectados. Para consertar isso, é necessário mover o código que carrega a coleção para logo antes da transação ser submetida.

Alternativamente, nós podemos usar uma coleção ou associação não preguiçosa, especificando lazy="false" para o mapeamento da associação. Porém, é pretendido que a inicialização preguiçosa seja usada por quase todas as coleções e associações. Se você definir muitas associações não preguiçosas em seu modelo de objetos, o Hibernate irá precisar buscar no banco de dados inteiro da memória em cada transação.

Por outro lado, nós geralmente escolhemos a busca de união (que não é preguiçosa por natureza) ao invés do selecionar busca em uma transação particular. Nós agora veremos como customizar a estratégia de busca. No Hibernate3, os mecanismos para escolher a estratégia de busca são idênticos para as associações de valor único e para coleções.

O padrão selecionar busca, é extremamente vunerável aos problemas de seleção N+1, então habilitaremos a busca de união no documento de mapeamento:


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

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

A estratégia de fetch definida no documento de mapeamento afeta:

Independentemente da estratégia de busca que você usar, o gráfico não preguiçoso definido será certamente carregado na memória. Note que isso irá resultar em diversas seleções imediatas sendo usadas para rodar uma consulta HQL em particular.

Geralmente, não usamos documentos de mapeamento para customizar as buscas. Ao invés disso, nós deixamos o comportamento padrão e sobrescrevemos isso em uma transação em particular, usando left join fetch no HQL. Isso diz ao Hibernate para buscar a associação inteira no primeiro select, usando uma união externa. Na API de busca Criteria, você irá usar setFetchMode(FetchMode.JOIN).

Se você quiser mudar a estratégia de busca usada pelo get() ou load(), simplesmente use uma consulta por Criteria, por exemplo:

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

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

Isto é o equivalente do Hibernate para o que algumas soluções ORM chamam de "plano de busca".

Um meio totalmente diferente de evitar problemas com selects N+1 é usar um cache de segundo nível.

A recuperação preguiçosa para coleções é implementada usando uma implementação própria do Hibernate para coleções persistentes. Porém, é necessário um mecanismo diferente para comportamento preguiçoso em associações de final único. A entidade alvo da associação precisa usar um proxy. O Hibernate implementa proxies para inicialização preguiçosa em objetos persistentes usando manipulação de bytecode, através da excelente biblioteca CGLIB.

Por padrão, o Hibernate3 gera proxies (na inicialização) para todas as classes persistentes que os usem para habilitar recuperação preguiçosa de associações many-to-one e one-to-one.

O arquivo de mapeamento deve declarar uma interface para usar como interface de proxy para aquela classe, com a função proxy. Por padrão, o Hibernate usa uma subclasse dessa classe. Note que a classe a ser usada via proxy precisa implementar o construtor padrão com pelo menos visibilidade de package. Nós recomendamos esse construtor para todas as classes persistentes.

Existe alguns truques que você deve saber quando estender esse comportamento para classes polimórficas. Por exemplo:


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

Primeiramente, instâncias de Cat nunca serão convertidas para DomesticCat, mesmo que a instância em questão seja uma instância 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!
    ....
}

E, segundo, é possível quebrar o 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

Porém a situação não é tão ruim como parece. Mesmo quando temos duas referências para objetos proxies diferentes, a instância adjacente será do mesmo objeto:

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

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

E por terceiro, você não pode usar um proxy CGLIB em uma classe final ou com quaisquer métodos final.

Finalmente, se o seu objeto persistente adquirir qualquer recurso durante a instanciação (ex. em inicializadores ou construtor padrão), então esses recursos serão adquiridos pelo proxy também. A classe de proxy é uma subclasse da classe persistente.

Esses problemas se dão devido à limitação originária do modelo de herança simples do Java. Se você quiser evitar esses problemas em suas classes persistentes você deve implementar uma interface que declare seus métodos comerciais. Você deve especificar essas interfaces no arquivo de mapeamento onde CatImpl implementa a interface Cat e DomesticCatImpl implementa a interface DomesticCat. Por exemplo:


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

Então, os proxies para instâncias de Cat e DomesticCat podem ser retornadas pelo load() ou 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();

Relacionamentos são também inicializados de forma preguiçosa. Isso significa que você precisa declarar qualquer propriedade como sendo do tipo Cat, e não CatImpl.

Algumas operações não requerem inicialização por proxy:

O Hibernate irá detectar classes persistentes que sobrescrevem equals() ou hashCode().

Escolhendo lazy="no-proxy" ao invés do padrão lazy="proxy", podemos evitar problemas associados com typecasting. Porém, iremos precisar de instrumentação de bytecode em tempo de compilação e todas as operações irão resultar em inicializações de proxy imediatas.

Será lançada uma LazyInitializationException se uma coleção não inicializada ou proxy for acessado fora do escopo da Session, isto é, quando a entidade que contém a coleção ou que possua a referência ao proxy estiver no estado desanexado.

Algumas vezes precisamos garantir que o proxy ou coleção é inicializado antes de fechar a Session. Claro que sempre podemos forçar a inicialização chamando cat.getSex() ou cat.getKittens().size(), por exemplo. Mas isto parece confuso para quem lê o código e não é conveniente para códigos genéricos.

Os métodos estáticos Hibernate.initialize() e Hibernate.isInitialized() favorecem a aplicação para trabalhar com coleções ou proxies inicializados de forma preguiçosa. O Hibernate.initialize(cat) irá forçar a inicialização de um proxy, cat, contanto que a Session esteja ainda aberta. Hibernate.initialize (cat.getKittens() ) tem um efeito similar para a coleção de kittens.

Uma outra opção é manter a Session aberta até que todas as coleções e os proxies necessários sejam carregados. Em algumas arquiteturas de aplicações, particularmente onde o código que acessa os dados usando Hibernate e o código que os usa, se encontram em diferentes camadas da aplicação ou diferentes processos físicos, será um problema garantir que a Session esteja aberta quando uma coleção for inicializada. Existem dois caminhos básicos para lidar com esse problema:

Às vezes você não quer inicializar uma coleção muito grande, mas precisa de algumas informações, como o mesmo tamanho, ou um subconjunto de seus dados.

Você pode usar um filtro de coleção para saber seu tamanho sem inicializá-la:

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

O método createFilter() é usado também para retornar algus dados de uma coleção eficientemente sem precisar inicializar a coleção inteira:

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

O Hibernate pode fazer uso eficiente de busca em lote, ou seja o Hibernate pode carregar diversos proxies não inicializados, se um proxy for acessado (ou coleções). A busca em lote é uma otimização da estratégia da busca de seleção lazy. Existem duas maneiras para você usar a busca em lote: no nível da classe ou no nível da coleção.

A recuperação em lote para classes/entidades é mais fácil de entender. Imagine que você tem a seguinte situação em tempo de execução: você tem 25 instâncias de Cat carregadas em uma Session, cada Cat possui uma referência ao seu owner, que é da classe Person. A classe Person é mapeada com um proxy, lazy="true". Se você interar sobre todos os Cat's e chamar getOwner() em cada, o Hibernate irá por padrão executar 25 comandos SELECT(), para buscar os proxies de owners. Você pode melhorar esse comportamento especificando um batch-size no mapeamento da classe Person:


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

O Hibernate irá executar agora apenas três consultas; o padrão é 10, 10, 5.

Você também pode habilitar busca em lote de uma coleção. Por exemplo, se cada Person tem uma coleção preguiçosa de Cats e 10 persons estão já carregadas em uma Session, serão gerados 10 SELECTs ao se interar todas as persons, um para cada chamada de getCats(). Se você habilitar busca em lote para a coleção de cats no mapeamento da classe Person, o Hibernate pode fazer uma pré carga das coleções:


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

Com um batch-size de 3, o Hibernate irá carregar 3, 3, 3, 1 coleções em 4 SELECTs. Novamente, o valor da função depende do número esperado de coleções não inicializadas em determinada Session.

A busca em lote de coleções é particularmente útil quando você tem uma árvore encadeada de ítens, ex.: o típico padrão bill-of-materials (Se bem que um conjunto encadeado ou caminho materializado pode ser uma opção melhor para árvores com mais leitura.

Outra forma de afetar a estratégia de busca para o carregamento de objetos associados é através do chamado perfil de busca, que é uma associação de configuração de nomeada com o org.hibernate.SessionFactory, porém ativado pelo nome no org.hibernate.Session. Uma vez ativado no org.hibernate.Session, o perfil de busca será afetado pelo org.hibernate.Session até que o mesmo seja completamente desativado.

O que isto significa? A explicação será através de um exemplo. Vamos dizer que nós temos os seguintes mapeamentos:


<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
>

Normalmente, quando você recebe uma referência para um cliente em particular, o conjunto do cliente de pedidos será lento, significando que nós ainda não baixamos estes pedidos a partir do banco de dados. Na maioria das vezes isto é bom. Agora vamos imaginar que você possui um determinado caso de uso, onde é mais eficiente carregar o cliente e outros pedidos juntos. Uma maneira correta é utilizar as estratégias de "busca dinâmica" através de um HQL ou consultas de critério. Entretanto, outra opção é usar um perfil de busca para atingir o mesmo objeto. Apenas adicione o seguinte a seu mapeamento:


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

ou ainda:


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

Agora que o código seguinte irá carregar ambos cliente e outros pedidos:



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

Apenas os perfis de busca em estilo são suportados, mas planeja-se o suporte de estilos adicionais. Consulte HHH-3414 para maiores detalhes.

O Hibernate3 suporta a busca lazy de propriedades individuais. Essa técnica de otimização é também conhecida como grupos de busca. Veja que esta é mais uma característica de marketing já que na prática, é mais importante a otimização nas leituras dos registros do que na leitura das colunas. Porém, carregar apenas algumas propriedades de uma classe pode ser útil em casos extremos, onde tabelas legadas podem ter centenas de colunas e o modelo de dados não pode ser melhorado.

Para habilitar a carga de propriedade lazy, é preciso ajustar a função lazy no seu mapeamento de propriedade:


<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
>

A carga de propriedades lazy requer instrumentação de bytecode. Se suas classes persistentes não forem melhoradas, o Hibernate irá ignorar silenciosamente essa configuração e usará a busca imediata.

Para instrumentação de bytecode, use a seguinte tarefa do 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
>

Uma forma diferente de evitar leitura de coluna desnecessária, ao menos para transações de somente leitura, deve-se usar os recursos de projeção do HQL ou consultas por Critério. Isto evita a necessidade de processamento de bytecode em build-time e é certamente uma melhor solução.

Você pode forçar a busca antecipada comum de propriedades usando buscar todas as propriedades no HQL.

Uma Session do Hibernate é um cache de nível transacional de dados persistentes. É possível configurar um cluster ou um cache de nível JVM (nível SessionFactory) em uma estrutura classe por classe e coleção por coleção. Você pode até mesmo plugar em um cache em cluster. Tenha cuidado, pois os caches nunca sabem das mudanças feitas em armazenamento persistente por um outro aplicativo. No entanto, eles podem ser configurados para dados em cache vencido regularmente.

Você tem a opção de informar o Hibernate sobre qual implementação de cache utilizar, especificando o nome de uma classe que implementa org.hibernate.cache.CacheProvider usando a propriedade hibernate.cache.provider_class. O Hibernate vem envolvido com um número de integrações construídas com provedores de cache de fonte aberta (listados abaixo). Além disso, você pode implementar seu próprio e plugá-lo como mencionado acima. Note que as versões anteriores ao padrão 3.2 utilizam EhCache como provedor de cache padrão.


O elemento <cache> de uma classe ou mapeamento de coleção possui a seguinte forma:

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

1

uso (solicitado) especifica a estratégia de cache: transacional, leitura-escrita, leitura-escrita não estrito ou somente leitura

2

region (opcional: padrão à classe ou nome papel da coleção): especifica o nome da região do cache de segundo nível

3

include (opcional: padrão para all) non-lazy: especifica que a propriedade da entidade mapeada com lazy="true" pode não estar em cache quando o nível da função busca lazy for habilitada

De forma alternativa, você poderá especificar os elementos <class-cache> e <collection-cache> em hibernate.cfg.xml.

A função uso especifica uma estratégia de concorrência de cache.

Quando passar um objeto para save(), update() ou saveOrUpdate() e quando recuperar um objeto usando um load(), get(), list(), iterate() ou scroll(), este objeto será adicionado ao cache interno da Session.

Quando o flush() for subsequentemente chamado, o estado deste objeto será sincronizado com o banco de dados. Se você não desejar que esta sincronização aconteça ou se você estiver processando uma grande quantidade de objetos e precisar gerenciar a memória de forma eficiente, o método evict() pode ser usado para remover o objeto de suas coleções de cache de primeiro nível.

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);
}

A Session também oferece um métodocontains() para determinar se uma instância pertence ao cache de sessão.

Para despejar completamente todos os objetos do cache de Sessão, chame Session.clear()

Para o cache de segundo nível, existem métodos definidos na SessionFactory para despejar o estado de cache de uma instância, classe inteira, instância de coleção ou papel de coleção inteiro.

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

O CacheMode controla como uma sessão em particular interage com o cache de segundo nível:

Para navegar o conteúdo do segundo nível ou região de cache de consulta, use oStatistics API:

Map cacheEntries = sessionFactory.getStatistics()

        .getSecondLevelCacheStatistics(regionName)
        .getEntries();

Você precisará habilitar estatísticas e, opcionalmente, forçar o Hibernate a manter as entradas de cache em um formato mais compreensível:

hibernate.generate_statistics true
hibernate.cache.use_structured_entries true

O conjunto de resultado de consulta pode também estar em cache. Isto é útil, somente para consultas que são rodadas freqüentemente com os mesmos parâmetros.

A aplicação do cache nos resultados de consulta introduz alguns resultados referentes o seu processamento transacional normal de aplicações. Por exemplo, se você realizar o cache nos resultados de uma consulta do Person Hibernate, você precisará acompanhar quando estes resultados deverão ser inválidos devido alterações salvas no Person. Tudo isto, acompanhado com o fato de que a maioria dos aplicativos não recebem benefício algum ao realizar o cache nos resultados da consulta, levando o Hibernate a desativar o cache de resultados de consulta por padrão. Para uso do cache de consulta, você primeiro precisa ativar o cache de consulta:

hibernate.cache.use_query_cache true

Esta configuração cria duas novas regiões de cache:

Conforme mencionado acima, a maioria das consultas não se beneficiam do cache ou de seus resultados. Portanto por padrão, as consultas individuais não estão em cache mesmo depois de ativar o cache de consulta. Para habilitar o caching de resultados, chame org.hibernate.Query.setCacheable(true). Esta chamada permite que a consulta procure por resultados de caches existentes ou adicione seus resultados ao cache quando for executado.

Nas seções anteriores nós descrevemos as coleções e seus aplicativos. Nesta seção nós exploraremos mais problemas em relação às coleções no período de execução.

O Hibernate define três tipos básicos de coleções:

A classificação distingue as diversas tabelas e relacionamento de chave externa, mas não nos diz tudo que precisamos saber sobre o modelo relacional. Para entender completamente a estrutura relacional e as características de desempenho, devemos também considerar a estrutura da chave primária que é usada pelo Hibernate para atualizar ou deletar linhas de coleções. Isto sugere a seguinte classificação:

Todas as coleções indexadas (mapas, listas, matrizes) possuem uma chave primária, que consiste em colunas <key> e <index>. Neste caso, as atualizações de coleção são geralmente muito eficientes. A chave primária pode ser indexada de forma eficiente e uma linha em particular pode ser localizada de forma eficiente quando o Hibernate tentar atualizar ou deletá-la.

Os conjuntos possuem uma chave primária que consiste em <key> e colunas de elemento. Isto pode ser menos eficiente para alguns tipos de elementos de coleções, especialmente elementos compostos ou textos grandes ou ainda campos binários. O banco de dados pode não ser capaz de indexar uma chave primária complexa de forma tão eficiente. Por um outro lado, para associações um-para-muitos ou muitos-para-muitos, especialmente no caso de identificadores sintáticos, é bem provável que seja tão eficiente quanto. Se você quiser que o SchemaExport crie para você uma chave primária de um <set> você deverá declarar todas as colunas como not-null="true".

Os mapeamentos <idbag> definem uma chave substituta, para que elas sejam sempre muito eficientes ao atualizar. Na verdade, este é o melhor caso.

As Bags são os piores casos. Como uma bag permite duplicar valores de elementos e não possui coluna de índice, não se deve definir nenhuma chave primária. O Hibernate não tem como distinguir entre linhas duplicadas. O Hibernate resolve este problema, removendo completamente em um único DELETE e recria a coleção quando mudar. Isto pode ser bastante ineficiente.

Note que para uma associação um-para-muitos, a chave primária pode não ser a chave primária física da tabela do banco de dados, mas mesmo neste caso, a classificação acima é ainda útil. Isto reflete como o Hibernate "localiza" linhas individuais da coleção.

A otimização não é muito usada sem o monitoramento e acesso ao número de desempenho. O Hibernate oferece uma grande variedade de números sobre suas operações internas. Estatísticas em Hibernate estão disponíveis através do SessionFactory.

Você poderá acessar as métricas da SessionFactory de duas formas. Sua primeira opção é chamar a sessionFactory.getStatistics() e ler ou dispôr as Estatísticas você mesmo.

O Hibernate também usa o JMX para publicar métricas se você habilitar o MBean de StatisticsService. Você deve habiliar um MBean único para todas as suas SessionFactory ou uma por factory. Veja o seguinte código para exemplos de configurações minimalísticos:

// 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

Você pode (des)ativar o monitoramento para uma SessionFactory:

As estatísticas podem ser reajsutadas de forma programática, usando o método clear(). Um resumo pode ser enviado para o usuário (nível de info) usando o método logSummary().

O Hibernate oferece um número de métricas, desde informações bem básicas até especializadas, somente relevantes a certos cenários. Todos os contadores disponíveis estão descritos na API da interface Statistics, em três categorias:

Por exemplo, você pode verificar a coincidência de um cache, perder e colocar a relação entre as entidades, colações e consultas e tempo médio que uma consulta precisa. Esteja ciente de que o número de milisegundos é sujeito a aproximação em Java. O Hibernate é preso à precisão do JVM, em algumas plataformas a precisão chega a ser de 10 segundos.

Os Getters simples são usados para acessar métricas globais (ou seja, não presos à uma entidade em particular, coleção, região de cache, etc.) Você pode acessar as métricas de uma entidade em particular, coleção ou região de cache através de seu nome e através de sua representação de HQL ou SQL para consultas. Por favor consulte a Javadoc API Statistics, EntityStatistics, CollectionStatistics, SecondLevelCacheStatistics, e QueryStatistics para maiores informações. O seguinte código mostra um exemplo simples:

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 trabalhar em todas as entidades, coleções, consultas e caches regionais, você poderá recuperar os nomes de lista de entidades, coleções, consultas e caches regionais com os seguintes métodos: getQueries(), getEntityNames(), getCollectionRoleNames(), e getSecondLevelCacheRegionNames().