Hibernate.org Community Documentation

- Anterior
- Próxima

## Capítulo 15. Batch processing

Uma alternativa para inserir 100.000 linhas no banco de dados usando o Hibernate pode ser a seguinte:

    Session session = sessionFactory.openSession();

    Transaction tx = session.beginTransaction();

    for ( int i=0; i<100000; i++ ) {

        Customer customer = new Customer(.....);

        session.save(customer);

    }

    tx.commit();

    session.close();

Isto irá falhar com um `OutOfMemoryException` em algum lugar próximo a linha 50.000. Isso ocorre devido ao fato do Hibernate fazer cache de todas as instâncias de `Customer` inseridas num cachê em nível de sessão. Nós demonstraremos neste capitulo como evitar este problema.

Entretanto, se você vai realizar processamento em lotes, é muito importante que você habilite o uso de lotes JDBC, se você pretende obter um desempenho razoável. Defina o tamanho do lote JDBC em um valor razoável (algo entre 10-50, por exemplo):

    hibernate.jdbc.batch_size 20

Note que o Hibernate desabilita o loteamento de inserção no nível JDBC de forma transparente se você utilizar um gerador de identificador `identiy`.

Você também pode querer rodar esse tipo de processamento em lotes com o cache secundário completamente desabilitado:

    hibernate.cache.use_second_level_cache false

Mas isto não é absolutamente necessário, desde que possamos ajustar o `CacheMode` para desabilitar a interação com o cache secundário.

## 15.1. Inserção em lotes

Quando você estiver inserindo novos objetos persistentes, vocês deve executar os métodos `flush()` e `clear()` regularmente na sessão, para controlar o tamanho do cache primário.

    Session session = sessionFactory.openSession();

    Transaction tx = session.beginTransaction();

    for ( int i=0; i<100000; i++ ) {

        Customer customer = new Customer(.....);

        session.save(customer);

        if ( i % 20 == 0 ) { //20, same as the JDBC batch size

            //flush a batch of inserts and release memory:

            session.flush();

            session.clear();

        }

    }

    tx.commit();

    session.close();

## 15.2. Atualização em lotes

Para recuperar e atualizar informações a mesma idéia é válida. Além disso, pode precisará usar o `scroll()` para usar recursos no lado do servidor em consultas que retornem muita informação.

    Session session = sessionFactory.openSession();

    Transaction tx = session.beginTransaction();

    ScrollableResults customers = session.getNamedQuery("GetCustomers")

        .setCacheMode(CacheMode.IGNORE)

        .scroll(ScrollMode.FORWARD_ONLY);

    int count=0;

    while ( customers.next() ) {

        Customer customer = (Customer) customers.get(0);

        customer.updateStuff(...);

        if ( ++count % 20 == 0 ) {

            //flush a batch of updates and release memory:

            session.flush();

            session.clear();

        }

    }

    tx.commit();

    session.close();

## 15.3. A interface de Sessão sem Estado

Como forma alternativa, o Hibernate provê uma API orientada à comandos, que pode ser usada para transmitir um fluxo de dados de e para o banco de dados na forma de objetos desanexados. Um `StatelessSession` não tem um contexto persistente associado e não fornece muito das semânticas de alto nível para controle do ciclo de vida. Especialmente uma Sessão sem Estado não implementa um cachê primário e nem interage com o cache secundário ou cachê de consulta. Ela não implementa uma gravação temporária transacional ou checagem suja automática. Operações realizadas usando uma sessão sem estado não fazem nenhum tipo de cascata com as instâncias associadas. As coleções são ignoradas por uma Sessão sem Estado. Operações realizadas com uma Sessão sem Estado ignoram a arquitetura de eventos e os interceptadores. As sessões sem estado são vulneráveis aos efeitos do alias dos dados, devido à falta do cachê primário. Uma Sessão sem Estado é uma abstração de baixo nível, muito mais próxima do JDBC adjacente.

    StatelessSession session = sessionFactory.openStatelessSession();

    Transaction tx = session.beginTransaction();

    ScrollableResults customers = session.getNamedQuery("GetCustomers")

        .scroll(ScrollMode.FORWARD_ONLY);

    while ( customers.next() ) {

        Customer customer = (Customer) customers.get(0);

        customer.updateStuff(...);

        session.update(customer);

    }

    tx.commit();

    session.close();

Veja neste exempo, as instâncias de `Customer` retornadas pela consulta, são imediatamente desvinculadas. Elas nunca serão associadas à um contexto persistente.

As operações `insert(), update()` e `delete()` definidas pela interface `StatelessSession` são considerados operações diretas no banco de dados. Isto resulta em uma execução imediata de comandos SQL `INSERT, UPDATE` ou `DELETE` respectivamente. Dessa forma, eles possuem uma semântica bem diferente das operações `save(), saveOrUpdate()` ou `delete()` definidas na interface `Session`.

## 15.4. Operações no estilo DML

Como já discutido anteriormente, o mapeamento objeto/relacional automático e transparente é adquirido com a gerência do estado do objeto. Com isto o estado daquele objeto fica disponível na memória. Isto significa que a manipulação de dados (usando as instruções SQL `Data Manipulation Language` (SQL-style DML): `INSERT`, `UPDATE`, `DELETE`) diretamente no banco de dados não irá afetar o estado registrado em memória. Entretanto, o Hibernate provê métodos para executar instruções de volume de SQL-style DML, que são totalmente executados com HQL (Hibernate Query Language - Linguagem de Consulta Hibernate) (HQL).

A pseudo-sintaxe para instruções `UPDATE` e `DELETE` é: Algumas observações: `( UPDATE | DELETE ) FROM? EntityName (WHERE where_conditions)?`.

Alguns pontos a serem destacados:

- Na cláusula from, a palavra chave FROM é opcional;

- Somente uma entidade pode ser chamada na cláusula from. Isto pode, opcionalmente, ser um alias. Se o nome da entidade for um alias, então qualquer referência de propriedade deve ser qualificada usando esse alias. Caso o nome da entidade não for um alias, então será ilegal qualquer das referências de propriedade serem qualificadas.

- Nenhum joins, tanto implícito ou explícito, pode ser especificado em uma consulta de volume HQL. As Sub-consultas podem ser utilizadas na cláusula onde, em que as subconsultas podem conter uniões.

- A clausula onde também é opcional.

Como exemplo para executar um HQL `UPDATE`, use o método `Query.executeUpdate()`. O método ganhou o nome devido à sua familiaridade com o do JDBC `PreparedStatement.executeUpdate()`):

    Session session = sessionFactory.openSession();

    Transaction tx = session.beginTransaction();

    String hqlUpdate = "update Customer c set c.name = :newName where c.name = :oldName";

    // or String hqlUpdate = "update Customer set name = :newName where name = :oldName";

    int updatedEntities = s.createQuery( hqlUpdate )

            .setString( "newName", newName )

            .setString( "oldName", oldName )

            .executeUpdate();

    tx.commit();

    session.close();

As instruções do HQL `UPDATE` por padrão não afetam o version ou os valores de propriedade timestamp para as entidades afetadas, de acordo com a especificação EJB3. No entanto, você poderá forçar o Hibernate a redefinir corretamente os valores de propriedade `version` ou `timestamp` usando um `versioned update`. Para tal, adicione uma palavra chave `VERSIONED` após a palavra chave `UPDATE`.

    Session session = sessionFactory.openSession();

    Transaction tx = session.beginTransaction();

    String hqlVersionedUpdate = "update versioned Customer set name = :newName where name = :oldName";

    int updatedEntities = s.createQuery( hqlUpdate )

            .setString( "newName", newName )

            .setString( "oldName", oldName )

            .executeUpdate();

    tx.commit();

    session.close();

Note que os tipos de versões padronizadas, `org.hibernate.usertype.UserVersionType`, não são permitidos junto às instruções `update versioned`.

Para executar um HQL `DELETE`, use o mesmo método `Query.executeUpdate()`:

    Session session = sessionFactory.openSession();

    Transaction tx = session.beginTransaction();

    String hqlDelete = "delete Customer c where c.name = :oldName";

    // or String hqlDelete = "delete Customer where name = :oldName";

    int deletedEntities = s.createQuery( hqlDelete )

            .setString( "oldName", oldName )

            .executeUpdate();

    tx.commit();

    session.close();

O valor `int` retornado pelo método `Query.executeUpdate()` indica o número de entidade afetadas pela operação. Lembre-se que isso pode estar ou não relacionado ao número de linhas alteradas no banco de dados. Uma operação de volume HQL pode resultar em várias instruções SQL atuais a serem executadas (por exemplo, no caso de subclasses unidas). O número retornado indica a quantidade real de entidades afetadas pela instrução. Voltando ao exemplo da subclasse unida, a exclusão de uma das subclasses pode resultar numa exclusão em outra tabelas, não apenas na tabela para qual a subclasses está mapeada, mas também tabela "root" e possivelmente nas tabelas de subclasses unidas num nível hierárquico imediatamente abaixo.

A pseudo-sintáxe para o comando `INSERT` é: `INSERT INTO EntityName properties_list select_statement`. Alguns pontos a observar:

- Apenas a forma INSERT INTO ... SELECT ... é suportada; INSERT INTO ... VALUES ... não é suportada.

  A lista de propriedade é análoga ao `column specification` do comando SQL `INSERT`. Para entidades envolvidas em mapeamentos, apenas as propriedades definidas diretamente em nível da classe podem ser usadas na properties_list. Propriedades da superclasse não são permitidas e as propriedades da subclasse não fazem sentido. Em outras palavras, os comandos `INSERT` não são polimórficos.

- selecionar_instruções pode ser qualquer consulta de seleção HQL válida, desde que os tipos de retorno sejam compatíveis com os tipos esperados pela inserção. Atualmente, isto é verificado durante a compilação da consulta, ao invés de permitir que a verificação chegue ao banco de dados. Entretanto, perceba que isso pode causar problemas entre os `Tipo`s de Hibernate que são equivalentes e não iguais. Isso pode causar problemas nas combinações entre a propriedade definida como `org.hibernate.type.DateType`e uma propriedade definida como `org.hibernate.type.TimestampType`, embora o banco de dados não possa fazer uma distinção ou possa ser capaz de manusear a conversão.

- Para a propriedade id, a instrução insert oferece duas opções. Você pode especificar qualquer propriedade id explicitamente no properties_list (em alguns casos esse valor é obtido diretamente da instrução select) ou pode omitir do properties_list (nesse caso, um valor gerado é usado). Essa última opção só é válida quando são usados geradores de ids que operam no banco de dados; a tentativa de usar essa opção com geradores do tipo "em memória" irá causar um exceção durante a etapa de análise. Note que para a finalidade desta discussão, os seguintes geradores operam com o banco de dados `org.hibernate.id.SequenceGenerator` (e suas subclasses) e qualquer implementação de `org.hibernate.id.PostInsertIdentifierGenerator`. Aqui, a exceção mais notável é o `org.hibernate.id.TableHiLoGenerator`, que não pode ser usado porque ele não dispõe de mecanismos para recuperar os seus valores.

- Para propriedades mapeadas como `version` ou `timestamp`, a instrução insert lhe oferece duas opções. Você pode especificar a propriedade na properties_list, nesse caso o seu valor é obtido a partir da instrução select correspondente, ou ele pode ser omitido da properties_list (neste caso utiliza-se o `seed value` definido pela classe `org.hibernate.type.VersionType`).

Segue abaixo o exemplo da execução de um HQL `INSERT`:

    Session session = sessionFactory.openSession();

    Transaction tx = session.beginTransaction();

    String hqlInsert = "insert into DelinquentAccount (id, name) select c.id, c.name from Customer c where ...";

    int createdEntities = s.createQuery( hqlInsert )

            .executeUpdate();

    tx.commit();

    session.close();

Copyright © 2004 Red Hat, Inc.

- AnteriorCapítulo 14. Interceptadores e Eventos
- Acima
- Principal
- PróximaCapítulo 16. HQL: A Linguagem de Consultas do Hib...