Hibernate.org Community Documentation

- Anterior
- Siguiente

## Capítulo 15. Procesamiento por lotes

Un enfoque ingenuo para insertar 100.000 filas en la base de datos utilizando Hibernate puede verse así:

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

Esto podría caer dentro de una `OutOfMemoryException` en algún sitio cerca de la fila 50.000. Esto se debe a que Hibernate tiene en caché todas las instancias de `Customer` recién insertadas en el caché de nivel de sesión. En este capítulo le vamos a mostrar cómo evitar este problema.

Si está realizando un procesamiento por lotes (batch processing), es necesario que habilite el uso del lote JDBC. Esto es esencial si quiere lograr un rendimiento óptimo. Establezca el tamaño de lote JDBC con un número razonable (por ejemplo, 10-50):

    hibernate.jdbc.batch_size 20

Hibernate desactiva el lote de inserción a nivel de JDBC de forma transparente si usted utiliza un generador de identificador `identiy`.

También puede realizar este tipo de trabajo en un proceso en donde la interacción con el caché de segundo nivel se encuentre completamente desactivado:

    hibernate.cache.use_second_level_cache false

Sin embargo, esto no es absolutamente necesario ya que podemos establecer explícitamente el `CacheMode` para descativar la interacción con el caché de segundo nivel.

## 15.1. Inserciones de lotes

Al hacer persistentes los objetos nuevos es necesario que realice `flush()` y luego `clear()` en la sesión regularmente para controlar el tamaño del caché de primer nivel.

    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. Actualizaciones de lotes

Para recuperar y actualizar datos se aplican las mismas ideas. Además, necesita utilizar `scroll()` para sacar ventaja de los cursores del lado del servidor en consultas que retornen muchas filas de datos.

    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. La interfaz de Sesión sin Estado

Opcionalmente, Hibernate proporciona una API orientada a comandos que se puede utilizar para datos que concurren desde y hacia la base de datos en forma de objetos separados. Un `StatelessSession` no tiene un contexto de persistencia asociado con él y no proporciona mucha de la semántica a un alto nivel de ciclo de vida. En particular, una sesión sin estado no implementa un caché en primer nivel y tampoco interactúa con cachés de segundo nivel o de peticiones. No implementa escritura-retrasada transaccional o chequeo de desactualizaciones automático. Las operaciones realizadas con la utilización de una sesión sin estado nunca usan cascadas para las instancias asociadas. La sesión sin estado ignora las colecciones. Las operaciones llevadas a cabo por una sesión sin estado ignoran el modelo de evento y los interceptores de Hibernte. Las sesiones sin estado son vulnerables a efectos de sobrenombamiento de datos debido a la falta de un caché de primer nivel. Una sesión sin estado es una abstracción en un nivel más bajo, mucho más cerca del JDBC subyacente.

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

En este código de ejemplo, las instancias `Customer` retornadas por la petición se separan inmediatamente. Nunca se asocian con ningún contexto de persistencia.

Las operaciones `insert(), update()` y `delete()` definidas por la interfaz `StatelessSession` son consideradas como operaciones directas a nivel de filas de la base de datos. Esto resulta en una ejecución inmediata de un `INSERT, UPDATE` SQL o `DELETE` respectivamente. Tienen una semántica diferente a la de las operaciones `save(), saveOrUpdate()` y `delete()` definidas por la interfaz `Session`.

## 15.4. Operaciones de estilo DML

Como se discutió anteriormente, el mapeo objeto/relacional transparente se refiere a la administración del estado de objetos. El estado del objeto está disponible en la memoria. Esto significa que el manipular datos directamente en la base de datos (utilizando DML (del inglés `Data Manipulation Language`) las declaraciones: `INSERT`, `UPDATE`, `DELETE`) no afectarán el estado en la memoria. Sin embargo, Hibernate brinda métodos para la ejecución de declaraciones en masa DML del estilo de SQL, las cuales se realizan por medio del Lenguaje de Consulta de Hibernate (HQL).

La pseudo-sintáxis para las declaraciones `UPDATE` y `DELETE` es: `( UPDATE | DELETE ) FROM? EntityName (WHERE where_conditions)?`.

Algunos puntos a observar:

- En la cláusula-from, la palabra clave FROM es opcional

- Sólamente puede haber una entidad mencionada en la cláusula-from y puede tener un alias. Si el nombre de la entidad tiene un alias entonces cualquier referencia a la propiedad tiene que ser calificada utilizando ese alias. Si el nombre de la entidad no tiene un alias entonces es ilegal calificar cualquier referencia de la propiedad.

- No se puede especificar ninguna unión ya sea implícita o explícita, en una consulta masiva de HQL. Se pueden utilizar subconsultas en la cláusula-where y en donde las subconsultas puedan contener uniones en sí mismas.

- La cláusula-where también es opcional.

Como ejemplo, para ejecutar un `UPDATE` de HQL, utilice el método `Query.executeUpdate()`. El método es nombrado para aquellos familiarizados con el `PreparedStatement.executeUpdate()` de JDBC:

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

Para mantenerse de acuerdo con la especificación de EJB3, las declaraciones `UPDATE` de HQL, por defecto no afectan la versión o los valores de la propiedad sello de fecha para las entidades afectadas. Sin embargo, puede obligar a Hibernate a poner en cero apropiadamente los valores de las propiedades `versión` o `sello de fecha` por medio de la utilización de una `actualización con versión`. Esto se logra agregando la palabra clave `VERSIONED` después de la palabra clave `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();

Observe que los tipos de versiones personalizados (`org.hibernate.usertype.UserVersionType`) no están permitidos en conjunto con una declaración `update versioned`.

Para ejecutar un `DELETE` HQL, utilice el mismo 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();

El valor `int` retornado por el método `Query.executeUpdate()` indica el número de entidades afectadas por la operación. Considere que esto puede estar correlacionado o no con el número de filas afectadas en la base de datos. Una operación masiva de HQL puede llegar a causar que se ejecuten múltiples declaraciones SQL reales, por ejemplo, para una subclase-joined. El número retornado indica el número de entidades realmente afectadas por la declaración. De vuelta al ejemplo de la subclase joined, un borrado contra una de las subclases puede resultar, de hecho, en borrados de no sólamente la tabla a la cual esa subclase esta mapeada, sino también la tabla "raíz" y potencialmente las tablas de subclases joined hasta la jerarquía de herencia.

La pseudo-sintáxis para las declaraciones `INSERT` es: `INSERT INTO EntityName properties_list select_statement`. Algunos puntos que se deben observar son:

- Sólamente se soporta la forma INSERT INTO ... SELECT ..., no la forma INSERT INTO ... VALUES ...

  La lista de propiedades (properties_list) es análoga a la `column speficiation` en la declaración `INSERT` de SQL. Para las entidades involucradas en la herencia mapeada, sólamente las propiedades definidas directamente en ese nivel de clase dado se pueden utlizar en la lista de propiedades. Las propiedades de la superclase no están permitidas, y las propiedaeds de la subclase no tienen sentido. Es decir, las declaraciones `INSERT` son inherentemente no-polimórficas.

- select_statement puede ser cualquier consulta select de HQL válida con la advertencia de que los tipos de retorno coincidan con los tipos esperados por el insert. Actualmente, esto se verifica durante la compilación de la consulta en vez de permitir que se relegue la verificación a la base de datos. Sin embargo, esto puede crear problemas entre los `Type`s de Hibernate, los cuales son equivalentes y no iguales. Esto puede crear problemas con las uniones mal hechas entre una propiedad definida como un `org.hibernate.type.DateType` y una propiedad definida como una `org.hibernate.type.TimestampType`, aunque puede que la base de datos no distinga o no pueda manejar la conversión.

- Para la propiedad id, la declaración insert le da dos opciones. Puede especificar explícitamente la propiedad id en la lista de propiedades (properties_list ) (en tal caso su valor se toma de la expresión de selección correspondiente) o se omite de la lista de propiedades (en tal caso se utiliza un valor generado). Esta última opción sólamente está disponible cuando se utilizan generadores de id que operan en la base de datos, intentando utilizar esta opción con cualquier generador de tipo "en memoria" provocará una excepción durante el análisis sintáctico. Note que para los propósitos de esta discusión, los generadores en la base de datos son considerados `org.hibernate.id.SequenceGenerator` (y sus subclases) y cualquier implementador de `org.hibernate.id.PostInsertIdentifierGenerator`. La excepción más importante aquí es `org.hibernate.id.TableHiLoGenerator`, la cual no se puede utilizar ya que no expone una manera selectiva de obtener sus valores.

- Para las propiedades mapeadas como `version` o `timestamp`, la declaración insert le da dos opciones. Puede especificar la propiedad en la lista de propiedades (en tal caso su valor se toma de las expresiones de selección correspondientes) o se omite de la lista de propiedades (en tal caso se utiliza el `seed value` definido por el `org.hibernate.type.VersionType`).

Un ejemplo de la ejecución de la declaración `INSERT` de HQL:

    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. Interceptores y eventos
- Subir
- Inicio
- SiguienteCapítulo 16. HQL: El lenguaje de consulta de Hibe...