Пакетное обновление в NHibernate
Существует ли команда пакетного обновления в NHibernate? Насколько я знаю, это не так. Так каков наилучший способ справиться с этой ситуацией? Я хотел бы сделать следующее:
- Получить список объектов (давайте назовем их списком пользователей,
List<User>
) из базы данных - Изменить свойства этих объектов, (
Users.Foreach(User=>User.Country="Antartica"
) - Обновить каждый элемент обратно индивидуально (
Users.Foreach(User=>NHibernate.Session.Update(User)
). - Вызов
Session.Flush
обновить базу данных.
Это хороший подход? Приведет ли это к множеству обратных вызовов между моим кодом и базой данных?
Как вы думаете? Или есть более элегантное решение?
6 ответов
Запуск пакетных заданий NHibernate 3.2 имеет улучшения, которые сводят к минимуму обращения к базе данных. Более подробную информацию можно найти в блоге HunabKu. Вот пример из этого - эти пакетные обновления делают только 6 циклов:
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();
}
Я знаю, что опоздал на вечеринку по этому вопросу, но подумал, что вам может быть интересно узнать, что теперь это возможно, используя HQL в NHibernate 2.1+
session.CreateQuery(@"update Users set Country = 'Antarctica'")
.ExecuteUpdate();
Вы можете установить размер пакета для обновлений в конфигурационном файле nhibernate.
<property name="hibernate.adonet.batch_size">16</property>
И вам не нужно вызывать Session.Update(User) там - просто очистите или зафиксируйте транзакцию, и NHibernate справится с вами.
РЕДАКТИРОВАТЬ: Я собирался опубликовать ссылку на соответствующий раздел документов nhibernate, но сайт не работает - вот старая запись от Ayende на эту тему:
Относительно того, является ли использование NHibernate (или любого ORM) здесь хорошим подходом, это зависит от контекста. Если вы делаете одноразовое обновление каждой строки в большой таблице с одним значением (например, указание для всех пользователей страны "Антарктида" (это континент, а не страна!), То вам, вероятно, следует используйте оператор SQL UPDATE. Если вы собираетесь обновлять несколько записей одновременно со страной как часть вашей бизнес-логики при общем использовании вашего приложения, то использование ORM может быть более разумным методом. Это зависит от количества строк вы обновляете каждый раз.
Возможно, самый разумный вариант здесь, если вы не уверены, это настроить параметр batch_size в NHibernate и посмотреть, как это работает. Если производительность системы неприемлема, вы можете взглянуть на реализацию простого SQL-оператора UPDATE в своем коде.
Начиная с NHibernate 5.0, можно выполнять массовые операции с использованием LINQ.
session.Query<Cat>()
.Where(c => c.BodyWeight > 20)
.Update(c => new { BodyWeight = c.BodyWeight / 2 });
NHibernate сгенерирует одиночный SQL-запрос на обновление.
Смотрите Обновление сущностей
Вам не нужно обновлять или очищать:
IList<User> users = session.CreateQuery (...).List<User>;
users.Foreach(u=>u.Country="Antartica")
session.Transaction.Commit();
Я думаю, что NHibernate пишет пакет для всех изменений.
Проблема в том, что ваши пользователи должны быть загружены в память. Если возникает проблема, вы все равно можете использовать собственный SQL с помощью NHibernate. Но пока вы не докажете, что это проблема с производительностью, оставайтесь с хорошим решением.
Нет, это не очень хороший подход!
Собственный SQL во много раз лучше для такого рода обновлений.
UPDATE USERS SET COUNTRY = 'Antartica';
Просто не может быть проще, и ядро базы данных будет обрабатывать это в сто раз эффективнее, чем строка Java-кода за раз.