Hibernate.org Community Documentation

- Anterior
- Próxima

## Capítulo 21. Aumentando o desempenho

## 21.1. Estratégias de Busca

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:

- Join fetching - o Hibernate busca o objeto ou coleção associada no mesmo `SELECT`, usando um `OUTER JOIN`.

- Select fetching - um segundo `SELECT` é usado para buscar a entidade ou coleção associada. A menos que você desabilite a busca lazy, especificando `lazy="false"`, esse segundo SELECT será executado apenas quando você acessar a associação.

- Subselect fetching - um segundo `SELECT` será usado para recuperar as coleções associadas de todas as entidades recuperadas em uma consulta ou busca anterior. A menos que você desabilite a busca lazy especificando `lazy="false"`, esse segundo SELECT será executado apenas quando você acessar a associação.

- Batch fetching - uma opção de otimização para selecionar a busca. O Hibernate recupera um lote de instâncias ou entidades usando um único `SELECT`, especificando uma lista de chaves primárias ou chaves externas.

O Hibernate distingue também entre:

- Immediate fetching - uma associação, coleção ou função é imediatamente recuperada, quando o proprietário for carregado.

- Lazy collection fetching - a coleção é recuperada quando a aplicação invoca uma operação sobre aquela coleção. Esse é o padrão para coleções.

- "Extra-lazy" collection fetching - elementos individuais de uma coleção são acessados a partir do banco de dados quando necessário. O Hibernate tenta não buscar a coleção inteira dentro da memória a menos que seja absolutamente necessário. Isto é indicado para coleções muito grandes.

- Proxy fetching: uma associação de um valor é carregada quando um método diferente do getter do identificador é invocado sobre o objeto associado.

- "No-proxy" fetching - uma associação de um único valor é recuperada quando a variável da instância é acessada. Comparada à busca proxy, esse método é menos preguiçoso (lazy); a associação é buscada até mesmo quando somente o identificador é acessado. Ela é mais transparente, já que não há proxies visíveis para a aplicação. Esse método requer instrumentação de bytecodes em build-time e é raramente necessário.

- Lazy attribute fetching: um atributo ou associação de um valor é buscado quanto a varíavel da instância é acessada. Esse método requer instrumentação de bytecodes em build-time e é raramente necessário.

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.

### 21.1.1. Trabalhando com associações preguiçosas (lazy)

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:

    s = 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.

### 21.1.2. Personalizando as estratégias de busca

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:

- recupera via `get()` ou `load()`

- Recuperações que acontecem implicitamente quando navegamos por uma associação

- consultas por `Criteria`

- consultas HQL se a busca por `subselect` for usada

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.

### 21.1.3. Proxies de associação final único

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

## Nota

`list()` normalmente retorna proxies.

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:

- `equals()`: se a classe persistente não sobrescrever `equals()`

- `hashCode()`: se a classe persistente não sobrescrever `hashCode()`

- O método getter do identificador

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.

### 21.1.4. Inicializando coleções e proxies

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:

- Em uma aplicações web, um filtro servlet pode ser usado para fechar a `Session` somente no final da requisição do usuário, quando a renderização da view estiver completa (o modelo Abrir Sessão em View). Claro, que isto demanda uma exatidão no manuseio de exceções na infraestrutura de sua aplicação. É extremamente importante que a `Session` seja fechada e a transação terminada antes de retornar para o usuário, mesmo que uma exceção ocorra durante a renderização da view. Veja o Wiki do Hibernate para exemplos do pattern "Abrir Sessão em View".

- Em uma aplicação com uma camada de negócios separada, a lógica de negócios deve "preparar" todas as coleções que serão usadas pela camada web antes de retornar. Isto sgnifica que a camada de negócios deve carregar todos os dados e retorná-los já inicializados para a camada de apresentação que é representada para um caso de uso particular. Geralmente, a aplicação chama `Hibernate.initialize()` para cada coleção que será usada pela camada web (essa chamada deve ocorrer antes da sessão ser fechada) ou retorna a coleção usando uma consulta Hibernate com uma cláusula `FETCH` ou um `FetchMode.JOIN` na `Criteria`. Fica muito mais fácil se você adotar o modelo Command ao invés do Session Facade.

- Você também pode anexar um objeto previamente carregado em uma nova `Session` `merge()` ou `lock()` antes de acessar coleções não inicializadas (ou outros proxies). O Hibernate não faz e certamente `não deve` fazer isso automaticamente, pois isso introduziria semântica em transações impromptu.

À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();

### 21.1.5. Usando busca em lote

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 `Cat`s e 10 persons estão já carregadas em uma `Session`, serão gerados 10 `SELECT`s 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 `SELECT`s. 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.

### 21.1.6. Usando busca de subseleção

Se uma coleção ou proxy simples precisa ser recuperado, o Hibernate carrega todos eles rodando novamente a consulta original em uma subseleção. Isso funciona da mesma maneira que busca em lote, sem carregar tanto.

### 21.1.7. Perfis de Busca

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:

Exemplo 21.1. Specifying a fetch profile using `@FetchProfile`

    @Entity

    @FetchProfile(name = "customer-with-orders", fetchOverrides = {

       @FetchProfile.FetchOverride(entity = Customer.class, association = "orders", mode = FetchMode.JOIN)

    })

    public class Customer {

       @Id

       @GeneratedValue

       private long id;

       private String name;

       private long customerNumber;

       @OneToMany

       private Set<Order> orders;

       // standard getter/setter

       ...

    }

Exemplo 21.2. Specifying a fetch profile using `<fetch-profile>` outside `<class>` node

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

        <fetch-profile name="customer-with-orders">

            <fetch entity="Customer" association="orders" style="join"/>

        </fetch-profile>

    </hibernate-mapping>

Exemplo 21.3. Specifying a fetch profile using `<fetch-profile>` inside `<class>` node

    <hibernate-mapping>

        <class name="Customer">

            ...

            <set name="orders" inverse="true">

                <key column="cust_id"/>

                <one-to-many class="Order"/>

            </set>

            <fetch-profile name="customer-with-orders">

                <fetch association="orders" style="join"/>

            </fetch-profile>

        </class>

        <class name="Order">

            ...

        </class>

    </hibernate-mapping>

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:

Exemplo 21.4. Activating a fetch profile for a given `Session`

    Session session = ...;

    session.enableFetchProfile( "customer-with-orders" );  // name matches from mapping

    Customer customer = (Customer) session.get( Customer.class, customerId );

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

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

### 21.1.8. Usando busca preguiçosa de propriedade

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.

## 21.2. O Cachê de Segundo Nível

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.

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 Tabela 21.1, “Provedores de Cache ”. 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.

Tabela 21.1. Provedores de Cache

Cache | Classe de provedor | Tipo | Segurança de Cluster | Cache de Consulta Suportado
--- | --- | --- | --- | ---
Hashtable (não recomendado para uso de produção) | `org.hibernate.cache.HashtableCacheProvider` | memória |  | yes
EHCache | `org.hibernate.cache.EhCacheProvider` | memory, disk, transactional, clustered | yes | yes
OSCache | `org.hibernate.cache.OSCacheProvider` | memória, disco |  | yes
SwarmCache | `org.hibernate.cache.SwarmCacheProvider` | clustered (ip multicast) | sim (invalidação em cluster) |
JBoss Cache 1.x | `org.hibernate.cache.TreeCacheProvider` | (ip multicast) em cluster, transacional | sim (replicação) | sim (solicitação de sync. de relógio)
JBoss Cache 2 | `org.hibernate.cache.jbc.JBossCacheRegionFactory` | (ip multicast) em cluster, transacional | sim (invalidação ou replicação) | sim (solicitação de sync. de relógio)

### 21.2.1. Mapeamento de Cache

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:

- `ENABLE_SELECTIVE` (Default and recommended value): entities are not cached unless explicitly marked as cacheable.

- `DISABLE_SELECTIVE`: entities are cached unless explicitly marked as not cacheable.

- `ALL`: all entities are always cached even if marked as non cacheable.

- `NONE`: no entity are cached even if marked as cacheable. This option can make sense to disable second-level cache altogether.

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:

- `read-only`

- `read-write`

- `nonstrict-read-write`

- `transactional`

## Nota

It is recommended to define the cache concurrency strategy per entity rather than using a global one. Use the `@org.hibernate.annotations.Cache` annotation for that.

Exemplo 21.5. Definition of cache concurrency strategy via `@Cache`

    @Entity

    @Cacheable

    @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)

    public class Forest { ... }

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.

Exemplo 21.6. Caching collections using annotations

    @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)

    @JoinColumn(name="CUST_ID")

    @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)

    public SortedSet<Ticket> getTickets() {

        return tickets;

    }

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

Exemplo 21.7. `@Cache` annotation with attributes

    @Cache(
        CacheConcurrencyStrategy usage();
        String region() default "";
        String include() default "all";
    )

| usage: the given cache concurrency strategy (NONE, READ_ONLY, NONSTRICT_READ_WRITE, READ_WRITE, TRANSACTIONAL)
| region (optional): the cache region (default to the fqcn of the class or the fq role name of the collection)
| `include` (optional): all to include all properties, non-lazy to only include non lazy properties (default all).

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 Exemplo 21.8, “The Hibernate <cache> mapping element” the parallels to anotations is obvious.

Exemplo 21.8. The Hibernate `<cache>` mapping element

    <cache
        usage="transactional|read-write|nonstrict-read-write|read-only"
        region="RegionName"
        include="all|non-lazy"
    />

| `uso` (solicitado) especifica a estratégia de cache: `transacional`, `leitura-escrita`, `leitura-escrita não estrito` ou `somente leitura`
| `region` (opcional: padrão à classe ou nome papel da coleção): especifica o nome da região do cache de segundo nível
| `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

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

### 21.2.2. Estratégia: somente leitura

Se sua aplicação precisar ler mas nunca modificar instâncias de uma classe persistente, pode-se utilizar um cache de `read-only`. Esta é a estratégia de desempenho mais simples e melhor. É também perfeitamente seguro para uso em um cluster.

### 21.2.3. Estratégia: leitura/escrita

Se a aplicação precisar atualizar dados, um cache de `read-write` pode ser mais apropriado. Esta estratégia de cache nunca deve ser usada se solicitado um nível de isolação de transação serializável. Se o cache for usado em um ambiente JTA, você deve especificar a propriedade `hibernate.transaction.manager_lookup_class`, nomeando uma estratégia por obter o `TransactionManager` JTA. Em outros ambientes, você deve assegurar que a transação está completa quando a `Session.close()` ou `Session.disconnect()` for chamada. Se desejar utilizar esta estratégia em um cluster, você deve assegurar que a implementação de cache adjacente suporta o bloqueio. Os provedores de cache built-in não suportam o bloqueamento.

### 21.2.4. Estratégia: leitura/escrita não estrita

Se a aplicação somente precisa atualizar dados ocasionalmente (ou seja, se for extremamente improvável que as duas transações tentem atualizar o mesmo ítem simultaneamente) e não for requerido uma isolação de transação estrita, o uso deum cache de `nonstrict-read-write` pode ser mais apropriado. Se um cache é usado em ambiente JTA, você deverá especificar o `hibernate.transaction.manager_lookup_class`. Em outros ambientes, você deve assegurar que a transação está completa quando a `Session.close()` ou `Session.disconnect()` for chamada.

### 21.2.5. Estratégia: transacional

A estratégia de cache `transactional` provê suporte para provedores de cache transacional completo como o JBoss TreeCache. Tal cache, deve ser usado somente em um ambiente JTA e você deverá especificar o `hibernate.transaction.manager_lookup_class`.

### 21.2.6. Compatibilidade de Estratégia de Concorrência de Cache Provedor

## Importante

Nenhum provedor de cache suporta todas as estratégias de concorrência de cache.

A seguinte tabela mostra qual provedor é compatível com qual estratégia de concorrência.

Tabela 21.2. Suporte de Estratégia de Concorrência de Cache

Cache | read-only | nonstrict-read-write | read-write | transactional
--- | --- | --- | --- | ---
Hashtable (não recomendado para uso de produção) | yes | yes | yes |
EHCache | yes | yes | yes | yes
OSCache | yes | yes | yes |
SwarmCache | yes | yes |  |
JBoss Cache 1.x | yes |  |  | yes
JBoss Cache 2 | yes |  |  | yes

## 21.3. Gerenciando os caches

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.

Exemplo 21.9. Explcitly evicting a cached instance from the first level cache using `Session.evict()`

    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étodo`contains()` 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.

Exemplo 21.10. Second-level cache eviction via `SessionFactoty.evict()` and `SessionFacyory.evictCollection()`

    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:

- `CacheMode.NORMAL` - lê e escreve itens ao cache de segundo nível.

- `CacheMode.GET`: itens de leitura do cache de segundo nível. Não escreve ao cache de segundo nível, exceto quando atualizar dados.

- `CacheMode.PUT`: escreve itens ao cache de segundo nível. Não lê a partir do cache de segundo nível.

- `CacheMode.REFRESH`: escreve itens ao cache de segundo nível, mas não lê a partir do cache de segundo nível. Passa o efeito de `hibernate.cache.use_minimal_puts`, forçando uma atualização do cache de segundo nível para que todos os itens leiam a partir do banco de dados.

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

Exemplo 21.11. Browsing the second-level cache entries via the `Statistics` 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:

Exemplo 21.12. Enabling Hibernate statistics

    hibernate.generate_statistics true
    hibernate.cache.use_structured_entries true

## 21.4. O Cache de Consulta

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.

### 21.4.1. Ativação do cache de consulta

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:

- `org.hibernate.cache.StandardQueryCache`, mantendo os resultados da consulta com cache.

- `org.hibernate.cache.UpdateTimestampsCache`, mantém os timestamps das atualizações mais recentes para tabelas consultáveis. Elas são usadas para validar os resultados uma vez que elas são servidas a partir do cache de consulta.

## Importante

If you configure your underlying cache implementation to use expiry or timeouts is very important that the cache timeout of the underlying cache region for the UpdateTimestampsCache be set to a higher value than the timeouts of any of the query caches. In fact, we recommend that the the UpdateTimestampsCache region not be configured for expiry at all. Note, in particular, that an LRU cache expiry policy is never appropriate.

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.

## Nota

O cache de consulta não realiza o cache ao estado de entidades atuais no cache, ele apenas realiza o cache nos valores identificadores e resultados do tipo de valor. Por esta razão, o cache de consulta deve sempre ser usado em conjunção com o cache de segundo nível para as entidades esperadas a sofrerem o cache como parte de um cache de resultado de consulta (apenas com o cache de coleção).

### 21.4.2. Regiões de cache de consulta

Se você solicitar um controle de granulado fino com políticas de validade do cache de consulta, você poderá especificar uma região de cache nomeada para uma consulta em particular, chamando `Query.setCacheRegion()`.

    List blogs = sess.createQuery("from Blog blog where blog.blogger = :blogger")

            .setEntity("blogger", blogger)

            .setMaxResults(15)

            .setCacheable(true)

            .setCacheRegion("frontpages")

            .list();

Se você quiser forçar um cache de consulta para uma atualização de sua região (independente de quaisquer resultados com cache encontrados nesta região), você poderá usar `org.hibernate.Query.setCacheMode(CacheMode.REFRESH)`. Juntamente com a região que você definiu para o cache gerado, o Hibernate seletivamente forçará os resultados com cache, naquela região particular a ser atualizada. Isto é particularmente útil em casos onde dados adjacentes podem ter sido atualizados através de um processo em separado , além de ser uma alternativa mais eficiente se aplicada ao despejo de uma região de cache através de `SessionFactory.evictQueries()`.

## 21.5. Entendendo o desempenho da Coleção

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.

### 21.5.1. Taxonomia

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

- Coleções de valores

- Associações um-para-muitos

- Associações muitos-para-muitos

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:

- Coleções indexadas

- conjuntos

- Bags

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.

### 21.5.2. Listas, mapas, bags de id e conjuntos são coleções mais eficientes para atualizar

A partir da discussão acima, deve ficar claro que as coleções indexadas e conjuntos (geralmente) permitem uma operação mais eficente em termos de adição, remoção e atualização de elementos.

Existe ainda, mais uma vantagem, das coleções indexadas sob conjuntos para associações muitos-para-muitos. Por causa da estrutura de um `Set`, o Hibernate nunca utiliza o comando `UPDATE` em uma linha quando um elemento é "modificado". As mudanças para o `Conjunto` funcionam sempre através do comando `INSERT` e `DELETE` de linhas individuais. Novamente, esta consideração não se aplica às associações um para muitos.

Após observar que as matrizes não podem ser preguiçosas, nós concluimos que as listas, mapas e bags de id são tipos de coleções com maior desempenho (não inverso), com conjuntos que não ficam atrás. Espera-se que os conjuntos sejam um tipo mais comum de coleção nas aplicações Hibernate. Isto porque as semânticas "conjunto" são mais naturais em modelos relacionais.

No entanto, em modelos de domínio de Hibernate bem criados, geralmente vemos que a maioria das coleções são de fato, associações um-para-muitos com `inverse="true"`. Para estas associações, a atualização é manipulada pelo lado muitos-para-um de uma associação e portanto considerações de desempenho de atualização de coleção simplesmente não se aplicam a este caso.

### 21.5.3. As Bags e listas são as coleções de inversão mais eficientes.

Existe um caso em particular no qual as bags (e também as listas) possuem um desempenho muito maior do que conjuntos. Para uma coleção com `inverse="true"`, o idioma de relacionamento um-para-um bidirecional padrão, por exemplo, podemos adicionar elementos a uma bag ou uma lista sem precisar inicializar (buscar) os elementos da bag. Isto acontece porque a `Collection.add()` ou `Collection.addAll()` deve sempre retornar verdadeira para uma bag ou `List`. Isto pode fazer que o código comum seguinte seja muito mais rápido:

    Parent p = (Parent) sess.load(Parent.class, id);

    Child c = new Child();

    c.setParent(p);

    p.getChildren().add(c);  //no need to fetch the collection!

    sess.flush();

### 21.5.4. Deletar uma vez

Às vezes, deletar elementos de coleção um por um pode ser extremamente ineficiente. O Hibernate não é completamente burro, portanto ele sabe que não deve fazer isso no caso de uma coleção que tenha sido esvaziada recentemente (se você chamou `list.clear()`, por exemplo). Neste caso, o Hibernate irá editar um único `DELETE`.

Vamos supor que tenha adicionado um elemento único à uma coleção de tamanho vinte e então remove dois elementos. O Hibernate irá editar uma instrução `INSERT` e duas instruções `DELETE`, a não ser que a coleção seja uma bag. Isto é certamente desejável.

No entanto, suponha que removamos dezoito elementos, deixando dois e então adicionando três novos elementos. Existem duas formas possíveis de se proceder:

- delete dezoito linhas uma por uma e então insira três linhas

- remova toda a coleção em um SQL `DELETE` e insira todos os cinco elementos atuais, um por um

O Hibernate não sabe que a segunda opção é provavelmente mais rápida neste caso. O Hibernate não deseha saber a opção, uma vez que tal comportamento deve confundir os triggers do banco de dados, etc.

Felizmente, você pode forçar este comportamento (ou seja, uma segunda estratégia) a qualquer momento, descartando (ou seja, desreferenciando) a coleção original e retornando uma coleção recentemente instanciada com todos os elementos atuais.

É claro que, deletar somente uma vez, não se aplica às coleções mapeadas `inverse="true"`.

## 21.6. Monitorando desempenho

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

### 21.6.1. Monitorando uma 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`:

- no tempo de configuração, ajuste `hibernate.generate_statistics` para `falso`

- em tempo de espera: `sf.getStatistics().setStatisticsEnabled(true)` ou `hibernateStatsBean.setStatisticsEnabled(true)`

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

### 21.6.2. Métricas

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:

- As métricas relacionadas ao uso da `Sessão`, tal como um número de sessões em aberto, conexões JDBC recuperadas, etc.

- As métricas relacionadas às entidades, coleções, consultas e caches como um todo (mais conhecido como métricas globais).

- Métricas detalhadas relacionadas à uma entidade em particular, coleção, consulta ou região de cache.

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

Copyright © 2004 Red Hat, Inc.

- AnteriorCapítulo 20. Mapeamento XML
- Acima
- Principal
- PróximaCapítulo 22. Guia de Toolset