|
|
|
@ -1,360 +1,364 @@
|
|
|
|
|
<?xml version='1.0' encoding="UTF-8"?>
|
|
|
|
|
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
|
|
|
|
|
|
|
|
|
|
<chapter id="batch">
|
|
|
|
|
<title>Processamento de lotes</title>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Uma alternativa para inserir 100.000 linhas no banco de dados usando o Hibernate
|
|
|
|
|
pode ser a seguinte:
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[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();]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Isto irá falhar em algum lugar próximo a linha 50.000, lançando uma
|
|
|
|
|
<literal>OutOfMemoryException</literal>. Isso ocorre devido ao fato do Hibernate
|
|
|
|
|
fazer cache de todas as instâncias de <literal>Customer</literal> inseridas num
|
|
|
|
|
cachê em nível de sessão.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Neste capítulo veremos como contornar esse problema. Entretanto, se você vai realizar
|
|
|
|
|
processamento de 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):
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[hibernate.jdbc.batch_size 20]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Você também pode querer rodar esse tipo de processamento de lotes com o cache
|
|
|
|
|
secundário completamente desabilitado:
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[hibernate.cache.use_second_level_cache false]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Mas isto não é absolutamente necessário, desde que nós possamos ajustar o
|
|
|
|
|
<literal>CacheMode</literal> para desabilitar a interação com o cache secundário.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<sect1 id="batch-inserts">
|
|
|
|
|
<title>Inserção de lotes</title>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Quando você estiver inserindo novos objetos persistentes, vocês deve executar
|
|
|
|
|
os métodos <literal>flush()</literal> e <literal>clear()</literal> regularmente
|
|
|
|
|
na sessão, para controlar o tamanho do cache primário.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[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();]]></programlisting>
|
|
|
|
|
|
|
|
|
|
</sect1>
|
|
|
|
|
|
|
|
|
|
<sect1 id="batch-update" >
|
|
|
|
|
<title>Batch updates</title>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Para recuperar e atualizar informações a mesma idéia é válida. Adicionalmente,
|
|
|
|
|
pode precisar usar o <literal>scroll()</literal> para usar recursos no lado
|
|
|
|
|
do servidor em queries que retornam muita informação.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[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();]]></programlisting>
|
|
|
|
|
|
|
|
|
|
</sect1>
|
|
|
|
|
|
|
|
|
|
<sect1 id="batch-statelesssession">
|
|
|
|
|
<title>A interface StatelessSession</title>
|
|
|
|
|
<para>
|
|
|
|
|
Alternativamente, o Hibernate provê uma API orientada à comandos, usada para
|
|
|
|
|
transmitir um fluxo de dados de e para o banco de dados na forma de objetos soltos.
|
|
|
|
|
Uma <literal>StatelessSession</literal> 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.
|
|
|
|
|
Em especial, uma StatelessSession não implemente o cache primário e nem interage
|
|
|
|
|
com o cache secundário ou query cache. Ele não implementa salvamento transacional
|
|
|
|
|
automatico ou checagem automática de mudanças. Operação realizadas usando uma
|
|
|
|
|
StatelessSession não fazem nenhum tipo de cascade com as instancias associadas.
|
|
|
|
|
As coleções são ignoradas por uma StatelessSession. Operações realizadas com um
|
|
|
|
|
StatelessSession ignoram a arquitetura de eventos e os interceptadores.
|
|
|
|
|
StatelessSession são vulneráveis aos efeitos do aliasing dos dados, devido a
|
|
|
|
|
falta do cache primário. Uma StatelessSession é uma abstração de baixo nível,
|
|
|
|
|
muito mais próxima do JDBC.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[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();]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Veja neste exempo, as instancias de <literal>Customer</literal> retornadas pela query
|
|
|
|
|
são imediatamente desvinculadas. Elas nunca serão assossiadas à um contexto persistente.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
As operações <literal>insert(), update()</literal> e <literal>delete()</literal>
|
|
|
|
|
definidos pela interface <literal>StatelessSession</literal> são considerados
|
|
|
|
|
operações diretas no banco de dados (row-level operations), isso resulta em uma
|
|
|
|
|
execução imediata de comandos SQL <literal>INSERT, UPDATE</literal> ou
|
|
|
|
|
<literal>DELETE</literal> respectivamente. Devido a isso, eles possuem uma
|
|
|
|
|
semântica bem diferente das operações <literal>save(), saveOrUpdate()</literal>
|
|
|
|
|
ou <literal>delete()</literal> definidas na interface <literal>Session</literal>.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
</sect1>
|
|
|
|
|
|
|
|
|
|
<sect1 id="batch-direct" revision="3">
|
|
|
|
|
<title>Operações no estilo DML</title>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Como já discutido, mapeamento objeto/relacional automático e transparente é conseguido
|
|
|
|
|
com a gerência do estado do objeto. Com isto o estado daquele objeto fica disponível na
|
|
|
|
|
memória, manipulando(usando as expressões SQL <literal>Data Manipulation Language</literal>
|
|
|
|
|
(SQL-style DML): <literal>INSERT</literal>, <literal>UPDATE</literal>, <literal>DELETE</literal>)
|
|
|
|
|
os dados diretamente no banco de dados não irá afetar o estado registrado em memória.
|
|
|
|
|
Entretanto, o Hibernate provê métodos para executar queries SQL-style DML, que são
|
|
|
|
|
totalmente executas com HQL (Hibernate Query Language)
|
|
|
|
|
(<xref linkend="queryhql">HQL</xref>).
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
A pseudo-sintaxe para expressões <literal>UPDATE</literal> e <literal>DELETE</literal> é:
|
|
|
|
|
<literal>( UPDATE | DELETE ) FROM? NomeEntidade (WHERE condições_where)?</literal>.
|
|
|
|
|
Algumas observações:
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<itemizedlist spacing="compact">
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
Na clausula from, a palavra chave FROM é opcional;
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
Somente uma entidade pode ser chamada na clausula from; opcionalmente pode ter
|
|
|
|
|
um alias. Se o nome da entidade for possuir um alias, então qualquer propriedade
|
|
|
|
|
referenciada deve usar esse alias qualificado; se o nome da entidade não possuir
|
|
|
|
|
um alias, então nenhuma das propriedade precisa usar o acesso qualificado.
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
Na <xref linkend="queryhql-joins-forms">joins</xref> (ambas implícita ou explicita)
|
|
|
|
|
pode ser especificada em um bulk HQL query. Sub-queries podem ser usadas na clausula
|
|
|
|
|
where; as subqueries podem conter joins.
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
A clausula where também é opcional.
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Como exemplo para executar um HQL <literal>UPDATE</literal>, use o
|
|
|
|
|
método <literal>Query.executeUpdate()</literal>(o método ganhou o nome
|
|
|
|
|
devido a sua familiaridade com o do JDBC
|
|
|
|
|
<literal>PreparedStatement.executeUpdate()</literal>):
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[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();]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
HQL <literal>UPDATE</literal> statements, by default do not effect the
|
|
|
|
|
<xref linkend="mapping-declaration-version">version</xref>
|
|
|
|
|
or the <xref linkend="mapping-declaration-timestamp">timestamp</xref> property values
|
|
|
|
|
for the affected entities; this is in keeping with the EJB3 specification. However,
|
|
|
|
|
you can force Hibernate to properly reset the <literal>version</literal> or
|
|
|
|
|
<literal>timestamp</literal> property values through the use of a <literal>versioned update</literal>.
|
|
|
|
|
This is achieved by adding the <literal>VERSIONED</literal> keyword after the <literal>UPDATE</literal>
|
|
|
|
|
keyword.
|
|
|
|
|
|
|
|
|
|
</para>
|
|
|
|
|
<programlisting><![CDATA[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();]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Note that custom version types (<literal>org.hibernate.usertype.UserVersionType</literal>)
|
|
|
|
|
are not allowed in conjunction with a <literal>update versioned</literal> statement.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
|
|
|
|
|
Para executar um HQL <literal>DELETE</literal>, use o mesmo método
|
|
|
|
|
<literal>Query.executeUpdate()</literal>:
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[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();]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
O valor <literal>int</literal> retornado pelo método <literal>Query.executeUpdate()</literal>
|
|
|
|
|
indica o numero 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 bulk HQL pode resultar
|
|
|
|
|
em várias expressões SQL reais a serem executadas, por exemplo, no caso de joined-subclass.
|
|
|
|
|
O número retornado indica a quantidade real de entidades afetadas pela expressão. Voltando
|
|
|
|
|
ao exemplo da joined-subclass, 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 joined-subclass num nível hierárquico
|
|
|
|
|
imediatamente abaixo.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
|
|
|
|
|
A pseudo-sintaxe para o comando <literal>INSERT</literal> é:
|
|
|
|
|
<literal>INSERT INTO EntityName properties_list select_statement</literal>. Alguns
|
|
|
|
|
pontos a observar:
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<itemizedlist spacing="compact">
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
Apenas a forma INSERT INTO ... SELECT ... é suportada; INSERT INTO ... VALUES ...
|
|
|
|
|
não é suportada.
|
|
|
|
|
</para>
|
|
|
|
|
<para>
|
|
|
|
|
A lista de propriedade é análoga à <literal>especificação da coluna</literal>
|
|
|
|
|
do comando SQL <literal>INSERT</literal>. Para entidades envolvidas em mapeamentos,
|
|
|
|
|
apenas a propriedades definidas diretamente a nível da classe podem ser usandas na
|
|
|
|
|
properties_list. Propriedades da superclass não são permitidas; e as propriedades
|
|
|
|
|
da subclasse não faz sentido. Em outras palavras, os comandos
|
|
|
|
|
<literal>INSERT</literal> não são polimorficos.
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
O camando select pode ser qualquer query HQL válida, que tenha um retorno compatível
|
|
|
|
|
com o tipo com o esperado pela inclusão. Atualmente, isto é verificado durante a compilação
|
|
|
|
|
da query, isto é melhor do que permitir que a verificação chegue ao banco de dados.
|
|
|
|
|
Entretanto perceba que isso pode causar problemas entre os <literal>Tipo</literal> do Hibernate
|
|
|
|
|
que são <emphasis>equivalentes</emphasis> em oposição a <emphasis>equal</emphasis>.
|
|
|
|
|
Isso pode causar problemas nas combinações entre a propriedade definida como
|
|
|
|
|
<literal>org.hibernate.type.DateType</literal>e um propriedade definida como
|
|
|
|
|
<literal>org.hibernate.type.TimestampType</literal>, embora o banco de dados não possa
|
|
|
|
|
fazer uma distinção ou possa ser capaz de manusear a conversão.
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
Para a propriedade id, a expressão insert oferece duas opções. Você pode especificar
|
|
|
|
|
qualquer propriedade id explicitamente no properties_list (em alguns casos esse valor
|
|
|
|
|
é obtido diretamente da expressão select) ou pode omitir do properties_list (nesse caso,
|
|
|
|
|
um valor gerado é usado). Essa ultima 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" vai causar um exceção durante a etapa de parser. Veja a finalidades desta
|
|
|
|
|
discussão, os seguintes geradores operam com o banco de dados
|
|
|
|
|
<literal>org.hibernate.id.SequenceGenerator</literal> (e suas subclasses)
|
|
|
|
|
e qualquer implementação de <literal>org.hibernate.id.PostInsertIdentifierGenerator</literal>.
|
|
|
|
|
Aqui, a exceção mais notável é o <literal>org.hibernate.id.TableHiLoGenerator</literal>, que
|
|
|
|
|
não pode ser usado porque ele não dispõe de mecanismos para recuperar o seu valor.
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
For properties mapped as either <literal>version</literal> or <literal>timestamp</literal>,
|
|
|
|
|
the insert statement gives you two options. You can either specify the property in the
|
|
|
|
|
properties_list (in which case its value is taken from the corresponding select expressions)
|
|
|
|
|
or omit it from the properties_list (in which case the <literal>seed value</literal> defined
|
|
|
|
|
by the <literal>org.hibernate.type.VersionType</literal> is used).
|
|
|
|
|
|
|
|
|
|
Para propriedades mapeadas como <literal>version</literal> ou <literal>timestamp</literal>,
|
|
|
|
|
a expressão insert oferece a você duas opções. Você pode especificar a propriedade na
|
|
|
|
|
properties_list (nesse caso o seu valor é obtido a partir da expressão select correspondente)
|
|
|
|
|
ou ele pode ser omitido da properties_list (neste caso o usa o <literal>valor semente</literal>
|
|
|
|
|
definido pela classe <literal>org.hibernate.type.VersionType</literal>).
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Exemplo da execução de um HQL <literal>INSERT</literal>:
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[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();]]></programlisting>
|
|
|
|
|
|
|
|
|
|
</sect1>
|
|
|
|
|
|
|
|
|
|
</chapter>
|
|
|
|
|
<chapter id="batch">
|
|
|
|
|
<title>Processamento de lotes</title>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Uma alternativa para inserir 100.000 linhas no banco de dados usando o Hibernate
|
|
|
|
|
pode ser a seguinte:
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[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();]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Isto irá falhar em algum lugar próximo a linha 50.000, lançando uma
|
|
|
|
|
<literal>OutOfMemoryException</literal>. Isso ocorre devido ao fato do Hibernate
|
|
|
|
|
fazer cache de todas as instâncias de <literal>Customer</literal> inseridas num
|
|
|
|
|
cachê em nível de sessão.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Neste capítulo veremos como contornar esse problema. Entretanto, se você vai realizar
|
|
|
|
|
processamento de 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):
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[hibernate.jdbc.batch_size 20]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Você também pode querer rodar esse tipo de processamento de lotes com o cache
|
|
|
|
|
secundário completamente desabilitado:
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[hibernate.cache.use_second_level_cache false]]></programlisting>
|
|
|
|
|
<para id="disablebatching" revision="1">
|
|
|
|
|
Note that Hibernate disables insert batching at the JDBC level transparently if you
|
|
|
|
|
use an <literal>identiy</literal> identifier generator.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Mas isto não é absolutamente necessário, desde que nós possamos ajustar o
|
|
|
|
|
<literal>CacheMode</literal> para desabilitar a interação com o cache secundário.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<sect1 id="batch-inserts">
|
|
|
|
|
<title>Inserção de lotes</title>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Quando você estiver inserindo novos objetos persistentes, vocês deve executar
|
|
|
|
|
os métodos <literal>flush()</literal> e <literal>clear()</literal> regularmente
|
|
|
|
|
na sessão, para controlar o tamanho do cache primário.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[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();]]></programlisting>
|
|
|
|
|
|
|
|
|
|
</sect1>
|
|
|
|
|
|
|
|
|
|
<sect1 id="batch-update" >
|
|
|
|
|
<title>Batch updates</title>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Para recuperar e atualizar informações a mesma idéia é válida. Adicionalmente,
|
|
|
|
|
pode precisar usar o <literal>scroll()</literal> para usar recursos no lado
|
|
|
|
|
do servidor em queries que retornam muita informação.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[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();]]></programlisting>
|
|
|
|
|
|
|
|
|
|
</sect1>
|
|
|
|
|
|
|
|
|
|
<sect1 id="batch-statelesssession">
|
|
|
|
|
<title>A interface StatelessSession</title>
|
|
|
|
|
<para>
|
|
|
|
|
Alternativamente, o Hibernate provê uma API orientada à comandos, usada para
|
|
|
|
|
transmitir um fluxo de dados de e para o banco de dados na forma de objetos soltos.
|
|
|
|
|
Uma <literal>StatelessSession</literal> 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.
|
|
|
|
|
Em especial, uma StatelessSession não implemente o cache primário e nem interage
|
|
|
|
|
com o cache secundário ou query cache. Ele não implementa salvamento transacional
|
|
|
|
|
automatico ou checagem automática de mudanças. Operação realizadas usando uma
|
|
|
|
|
StatelessSession não fazem nenhum tipo de cascade com as instancias associadas.
|
|
|
|
|
As coleções são ignoradas por uma StatelessSession. Operações realizadas com um
|
|
|
|
|
StatelessSession ignoram a arquitetura de eventos e os interceptadores.
|
|
|
|
|
StatelessSession são vulneráveis aos efeitos do aliasing dos dados, devido a
|
|
|
|
|
falta do cache primário. Uma StatelessSession é uma abstração de baixo nível,
|
|
|
|
|
muito mais próxima do JDBC.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[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();]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Veja neste exempo, as instancias de <literal>Customer</literal> retornadas pela query
|
|
|
|
|
são imediatamente desvinculadas. Elas nunca serão assossiadas à um contexto persistente.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
As operações <literal>insert(), update()</literal> e <literal>delete()</literal>
|
|
|
|
|
definidos pela interface <literal>StatelessSession</literal> são considerados
|
|
|
|
|
operações diretas no banco de dados (row-level operations), isso resulta em uma
|
|
|
|
|
execução imediata de comandos SQL <literal>INSERT, UPDATE</literal> ou
|
|
|
|
|
<literal>DELETE</literal> respectivamente. Devido a isso, eles possuem uma
|
|
|
|
|
semântica bem diferente das operações <literal>save(), saveOrUpdate()</literal>
|
|
|
|
|
ou <literal>delete()</literal> definidas na interface <literal>Session</literal>.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
</sect1>
|
|
|
|
|
|
|
|
|
|
<sect1 id="batch-direct" revision="3">
|
|
|
|
|
<title>Operações no estilo DML</title>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Como já discutido, mapeamento objeto/relacional automático e transparente é conseguido
|
|
|
|
|
com a gerência do estado do objeto. Com isto o estado daquele objeto fica disponível na
|
|
|
|
|
memória, manipulando(usando as expressões SQL <literal>Data Manipulation Language</literal>
|
|
|
|
|
(SQL-style DML): <literal>INSERT</literal>, <literal>UPDATE</literal>, <literal>DELETE</literal>)
|
|
|
|
|
os dados diretamente no banco de dados não irá afetar o estado registrado em memória.
|
|
|
|
|
Entretanto, o Hibernate provê métodos para executar queries SQL-style DML, que são
|
|
|
|
|
totalmente executas com HQL (Hibernate Query Language)
|
|
|
|
|
(<xref linkend="queryhql">HQL</xref>).
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
A pseudo-sintaxe para expressões <literal>UPDATE</literal> e <literal>DELETE</literal> é:
|
|
|
|
|
<literal>( UPDATE | DELETE ) FROM? NomeEntidade (WHERE condições_where)?</literal>.
|
|
|
|
|
Algumas observações:
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<itemizedlist spacing="compact">
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
Na clausula from, a palavra chave FROM é opcional;
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
Somente uma entidade pode ser chamada na clausula from; opcionalmente pode ter
|
|
|
|
|
um alias. Se o nome da entidade for possuir um alias, então qualquer propriedade
|
|
|
|
|
referenciada deve usar esse alias qualificado; se o nome da entidade não possuir
|
|
|
|
|
um alias, então nenhuma das propriedade precisa usar o acesso qualificado.
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
Na <xref linkend="queryhql-joins-forms">joins</xref> (ambas implícita ou explicita)
|
|
|
|
|
pode ser especificada em um bulk HQL query. Sub-queries podem ser usadas na clausula
|
|
|
|
|
where; as subqueries podem conter joins.
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
A clausula where também é opcional.
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Como exemplo para executar um HQL <literal>UPDATE</literal>, use o
|
|
|
|
|
método <literal>Query.executeUpdate()</literal>(o método ganhou o nome
|
|
|
|
|
devido a sua familiaridade com o do JDBC
|
|
|
|
|
<literal>PreparedStatement.executeUpdate()</literal>):
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[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();]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
HQL <literal>UPDATE</literal> statements, by default do not effect the
|
|
|
|
|
<xref linkend="mapping-declaration-version">version</xref>
|
|
|
|
|
or the <xref linkend="mapping-declaration-timestamp">timestamp</xref> property values
|
|
|
|
|
for the affected entities; this is in keeping with the EJB3 specification. However,
|
|
|
|
|
you can force Hibernate to properly reset the <literal>version</literal> or
|
|
|
|
|
<literal>timestamp</literal> property values through the use of a <literal>versioned update</literal>.
|
|
|
|
|
This is achieved by adding the <literal>VERSIONED</literal> keyword after the <literal>UPDATE</literal>
|
|
|
|
|
keyword.
|
|
|
|
|
|
|
|
|
|
</para>
|
|
|
|
|
<programlisting><![CDATA[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();]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Note that custom version types (<literal>org.hibernate.usertype.UserVersionType</literal>)
|
|
|
|
|
are not allowed in conjunction with a <literal>update versioned</literal> statement.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
|
|
|
|
|
Para executar um HQL <literal>DELETE</literal>, use o mesmo método
|
|
|
|
|
<literal>Query.executeUpdate()</literal>:
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[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();]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
O valor <literal>int</literal> retornado pelo método <literal>Query.executeUpdate()</literal>
|
|
|
|
|
indica o numero 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 bulk HQL pode resultar
|
|
|
|
|
em várias expressões SQL reais a serem executadas, por exemplo, no caso de joined-subclass.
|
|
|
|
|
O número retornado indica a quantidade real de entidades afetadas pela expressão. Voltando
|
|
|
|
|
ao exemplo da joined-subclass, 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 joined-subclass num nível hierárquico
|
|
|
|
|
imediatamente abaixo.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
|
|
|
|
|
A pseudo-sintaxe para o comando <literal>INSERT</literal> é:
|
|
|
|
|
<literal>INSERT INTO EntityName properties_list select_statement</literal>. Alguns
|
|
|
|
|
pontos a observar:
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<itemizedlist spacing="compact">
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
Apenas a forma INSERT INTO ... SELECT ... é suportada; INSERT INTO ... VALUES ...
|
|
|
|
|
não é suportada.
|
|
|
|
|
</para>
|
|
|
|
|
<para>
|
|
|
|
|
A lista de propriedade é análoga à <literal>especificação da coluna</literal>
|
|
|
|
|
do comando SQL <literal>INSERT</literal>. Para entidades envolvidas em mapeamentos,
|
|
|
|
|
apenas a propriedades definidas diretamente a nível da classe podem ser usandas na
|
|
|
|
|
properties_list. Propriedades da superclass não são permitidas; e as propriedades
|
|
|
|
|
da subclasse não faz sentido. Em outras palavras, os comandos
|
|
|
|
|
<literal>INSERT</literal> não são polimorficos.
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
O camando select pode ser qualquer query HQL válida, que tenha um retorno compatível
|
|
|
|
|
com o tipo com o esperado pela inclusão. Atualmente, isto é verificado durante a compilação
|
|
|
|
|
da query, isto é melhor do que permitir que a verificação chegue ao banco de dados.
|
|
|
|
|
Entretanto perceba que isso pode causar problemas entre os <literal>Tipo</literal> do Hibernate
|
|
|
|
|
que são <emphasis>equivalentes</emphasis> em oposição a <emphasis>equal</emphasis>.
|
|
|
|
|
Isso pode causar problemas nas combinações entre a propriedade definida como
|
|
|
|
|
<literal>org.hibernate.type.DateType</literal>e um propriedade definida como
|
|
|
|
|
<literal>org.hibernate.type.TimestampType</literal>, embora o banco de dados não possa
|
|
|
|
|
fazer uma distinção ou possa ser capaz de manusear a conversão.
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
Para a propriedade id, a expressão insert oferece duas opções. Você pode especificar
|
|
|
|
|
qualquer propriedade id explicitamente no properties_list (em alguns casos esse valor
|
|
|
|
|
é obtido diretamente da expressão select) ou pode omitir do properties_list (nesse caso,
|
|
|
|
|
um valor gerado é usado). Essa ultima 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" vai causar um exceção durante a etapa de parser. Veja a finalidades desta
|
|
|
|
|
discussão, os seguintes geradores operam com o banco de dados
|
|
|
|
|
<literal>org.hibernate.id.SequenceGenerator</literal> (e suas subclasses)
|
|
|
|
|
e qualquer implementação de <literal>org.hibernate.id.PostInsertIdentifierGenerator</literal>.
|
|
|
|
|
Aqui, a exceção mais notável é o <literal>org.hibernate.id.TableHiLoGenerator</literal>, que
|
|
|
|
|
não pode ser usado porque ele não dispõe de mecanismos para recuperar o seu valor.
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
For properties mapped as either <literal>version</literal> or <literal>timestamp</literal>,
|
|
|
|
|
the insert statement gives you two options. You can either specify the property in the
|
|
|
|
|
properties_list (in which case its value is taken from the corresponding select expressions)
|
|
|
|
|
or omit it from the properties_list (in which case the <literal>seed value</literal> defined
|
|
|
|
|
by the <literal>org.hibernate.type.VersionType</literal> is used).
|
|
|
|
|
|
|
|
|
|
Para propriedades mapeadas como <literal>version</literal> ou <literal>timestamp</literal>,
|
|
|
|
|
a expressão insert oferece a você duas opções. Você pode especificar a propriedade na
|
|
|
|
|
properties_list (nesse caso o seu valor é obtido a partir da expressão select correspondente)
|
|
|
|
|
ou ele pode ser omitido da properties_list (neste caso o usa o <literal>valor semente</literal>
|
|
|
|
|
definido pela classe <literal>org.hibernate.type.VersionType</literal>).
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Exemplo da execução de um HQL <literal>INSERT</literal>:
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[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();]]></programlisting>
|
|
|
|
|
|
|
|
|
|
</sect1>
|
|
|
|
|
|
|
|
|
|
</chapter>
|
|
|
|
|