hibernate-orm/reference/es/modules/batch.xml

193 lines
7.8 KiB
XML

<chapter id="batch">
<title>Procesamiento por lotes</title>
<para>
Un enfoque ingenuo para insertar 100.000 filas en la base de datos usando Hibernate podría verse así:
</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>
Esto podría caer sobre una <literal>OutOfMemoryException</literal> en algún sitio
cerca de la fila 50.000. Esto es porque Hibernate tiene en caché todas las instancias
de <literal>Customer</literal> recién instanciadas en el caché de nivel de sesión.
</para>
<para>
En este capítulo te mostraremos cómo evitar este problema. Primero, sin embargo,
si estás haciendo procesamiento por lotes (batch processing), es absolutamente crítico
que habilites el uso de loteo JDBC, si pretendes lograr un rendimiento razonable.
Establece el tamaño de lote JDBC a un número razonable (digamos 10-50):
</para>
<programlisting><![CDATA[hibernate.jdbc.batch_size 20]]></programlisting>
<para>
Podrías además querer hacer este tipo de trabajo en un proceso donde la interacción con el caché de
segundo nivel esté completamente deshabilitado:
</para>
<programlisting><![CDATA[hibernate.cache.use_second_level_cache false]]></programlisting>
<sect1 id="batch-inserts">
<title>Inserciones en lote</title>
<para>
Al hacer persistentes objetos nuevos, debes limpiar con <literal>flush()</literal> y
llamar a <literal>clear()</literal> en la sesión regularmente, para controlar el tamaño
del caché de primer nivel.
</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>Actualizaciones en lote</title>
<para>
Para recuperar y actualizar datos se aplican las mismas ideas. Adicionalmente, necesitas usar
<literal>scroll()</literal> para sacar ventaja de los cursores del lado del servidor en consultas
que devuelvan muchas filas de datos.
</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-direct">
<title>update/delete en masa</title>
<para>
Como ya se ha discutido, el mapeo objeto/relacional automático y transparente se refiere
al manejo de estado de objetos. Esto implica que el estado del objeto está disponible
en memoria, por lo tanto actualizar o borrar (usando <literal>UPDATE</literal> y
<literal>DELETE</literal> de SQL) datos directamente en la base de datos no afectará el
estado en memoria. Sin embargo, Hibernate provee métodos para la ejecución de sentencias
del estilo de <literal>UPDATE</literal> y <literal>DELETE</literal> de SQL que se realizan
a través del Lenguaje de Consulta de Hibernate (Hibernate Query Language o
<xref linkend="queryhql">HQL</xref>).
</para>
<para>
La pseudo-sintáxis para sentencias <literal>UPDATE</literal> y <literal>DELETE</literal> es:
<literal>( UPDATE | DELETE ) FROM? ClassName (WHERE WHERE_CONDITIONS)?</literal>. Algunos puntos
a tener en cuenta:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
En la cláusula-from, la palabra clave FROM es opcional
</para>
</listitem>
<listitem>
<para>
Puede haber sólo una clase mencionada en la cláusula-from, y <emphasis>no puede</emphasis>
tener un alias.
</para>
</listitem>
<listitem>
<para>
No puede especificarse ningún join (bien implícito o explícito) en una consulta masiva de HQL.
Pueden usarse subconsultas en la cláusula-where.
</para>
</listitem>
<listitem>
<para>
La cláusula-where es también opcional.
</para>
</listitem>
</itemizedlist>
<para>
Como un ejemplo, para ejecutar un <literal>UPDATE</literal> HQL, usa el
método <literal>Query.executeUpdate()</literal>:
</para>
<programlisting><![CDATA[Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
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>
Para ejecutar un <literal>DELETE</literal> HQL, usa el mismo método <literal>Query.executeUpdate()</literal>
(el método está nombrado para aquellos familiarizados con
<literal>PreparedStatement.executeUpdate()</literal> de JDBC):
</para>
<programlisting><![CDATA[Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlDelete = "delete Customer where name = :oldName";
int deletedEntities = s.createQuery( hqlDelete )
.setString( "oldName", oldName )
.executeUpdate();
tx.commit();
session.close();]]></programlisting>
<para>
El valor <literal>int</literal> devuelto por el método <literal>Query.executeUpdate()</literal>
indica el número de entidades afectadas por la operación. Considera que esto puede o no
correlacionarse al número de filas afectadas en la base de datos. Una operación masiva HQL podría
resultar en que se ejecuten múltiples sentencias de SQL reales, para joined-subclass, por ejemplo.
El número devuelto indica el número de entidades reales afectadas por la sentencia. Volviendo al
ejemplo de joined-subclass, un borrado contra una de las subclases puede resultar realmente en
borrados contra no sólo la tabla a la que está mapeada esa subclase, sino también la tabla "raíz"
y potencialmente tablas de joined-subclass más debajo en la jerarquía de herencia.
</para>
<para>
Ten en cuenta que existen actualmente unas pocas limitaciones con las operaciones HQL masivas,
que serán atendidas en lanzamientos futuros; consulta la hoja de ruta de JIRA para más detalles.
</para>
</sect1>
</chapter>