Actualización por lotes en NHibernate


¿Existe el comando batch update en NHibernate? Por lo que sé, no. ¿Cuál es la mejor manera de manejar esta situación? Me gustaría hacer lo siguiente:

  1. Obtenga una lista de objetos ( llamémoslos una lista de usuarios, List<User> ) de la base de datos
  2. Cambiar las propiedades de esos objetos, ( Users.Foreach(User=>User.Country="Antartica")
  3. Actualiza cada elemento individualmente ( Users.Foreach(User=>NHibernate.Session.Update(User)).
  4. Llama a Session.Flush para actualizar la base de datos.

¿Es este un buen enfoque? ¿Esto resultó en un ¿mucho viaje de ida y vuelta entre mi código y la base de datos?

¿Qué piensas? ¿O hay una solución más elegante?

 22
Author: Graviton, 2009-04-23

6 answers

Al iniciar NHibernate 3.2 los trabajos por lotes tienen mejoras que minimizan los roundtrips de la base de datos. Se puede encontrar más información en HunabKu blog . Aquí hay un ejemplo de ello: estas actualizaciones por lotes solo hacen 6 viajes de ida y vuelta:

using (ISession s = OpenSession())
using (s.BeginTransaction())
{
    for (int i = 0; i < 12; i++)
    {
        var user = new User {UserName = "user-" + i};
        var group = new Group {Name = "group-" + i};
        s.Save(user);
        s.Save(group);
        user.AddMembership(group);
    }
    s.Transaction.Commit();
}
 20
Author: marisks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-05 11:46:07

Sé que llego tarde a la fiesta en esto, pero pensé que te gustaría saber que ahora es posible usar HQL en NHibernate 2.1 +

session.CreateQuery(@"update Users set Country = 'Antarctica'")
.ExecuteUpdate();
 41
Author: MPritchard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-22 12:26:16

Puede establecer el tamaño del lote para las actualizaciones en el archivo de configuración de nhibernate.

<property name="hibernate.adonet.batch_size">16</property>

Y no es necesario llamar a Session.Actualice (Usuario) allí: simplemente vacíe o confirme una transacción y NHibernate se encargará de las cosas por usted.

EDITAR: Iba a publicar un enlace a la sección relevante de los documentos de nhibernate, pero el sitio está caído - aquí hay un viejo post de Ayende sobre el tema:

En cuanto a si el uso de NHibernate (o cualquier OR) aquí es un buen enfoque, depende en el contexto. Si está haciendo una actualización única de cada fila en una tabla grande con un solo valor (como configurar todos los usuarios al país 'Antártida' (que es un continente, no un país por cierto!), entonces probablemente debería usar una instrucción sql UPDATE. Si va a actualizar varios registros a la vez con un país como parte de su lógica de negocio en el uso general de su aplicación, entonces usar un OR podría ser un método más sensato. Esto depende del número de filas que se actualizando cada vez.

Quizás la opción más sensata aquí si no está seguro es ajustar la opción batch_size en NHibernate y ver cómo funciona. Si el rendimiento del sistema no es aceptable, entonces podría implementar una instrucción de ACTUALIZACIÓN sql directa en su código.

 9
Author: Steve Willcock,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-23 12:10:46

No es necesario actualizar, ni flush:

IList<User> users = session.CreateQuery (...).List<User>;
users.Foreach(u=>u.Country="Antartica")
session.Transaction.Commit();

Creo que NHibernate escribe un lote para todos los cambios.

El problema es que sus usuarios necesitan ser cargados en la memoria. Si tiene un problema, aún puede usar SQL nativo usando NHibernate. Pero hasta que no haya demostrado que es un problema de rendimiento, siga con la buena solución.

 4
Author: Stefan Steinegger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-23 09:34:44

A partir de NHibernate 5.0 es posible realizar operaciones masivas usando LINQ.

session.Query<Cat>()
.Where(c => c.BodyWeight > 20)
.Update(c => new { BodyWeight = c.BodyWeight / 2 });

NHibernate generará una única consulta sql "update".

Véase Actualización de entidades

 4
Author: bN_,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-12 13:58:23

No, no es un buen enfoque!

SQL nativo es muchas veces mejor para este tipo de actualización.

UPDATE USERS SET COUNTRY = 'Antartica';

Simplemente no podría ser más simple y el motor de base de datos procesará esto cien veces más eficientemente que row a la vez código Java.

 2
Author: James Anderson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-23 09:57:44