NHibernate ISession Flush: где и когда его использовать и почему?

187

Одна из вещей, которая меня сильно смущает - это использование session.Flushв сочетании с session.Commitи session.Close.

Иногда session.Closeработает, например, он фиксирует все изменения, которые мне нужны. Я знаю, что мне нужно использовать коммит, когда у меня есть транзакция или единица работы с несколькими созданиями / обновлениями / удалениями, чтобы я мог откатиться, если произойдет ошибка.

Но иногда я действительно зацикливаюсь на логике позади session.Flush. Я видел примеры, когда за вами session.SaveOrUpdate()следовал сброс, но когда я удаляю сброс, он все равно работает нормально. Иногда я сталкиваюсь с ошибками в операторе Flush, говоря, что время сеанса истекло, и удаляя его, я не столкнулся с этой ошибкой.

У кого-нибудь есть хорошее руководство относительно того, где и когда использовать Flush? Я проверил документацию NHibernate для этого, но все еще не могу найти простой ответ.

Джон Лимджап
источник

Ответы:

236

Кратко:

  1. Всегда используйте транзакции
  2. Не используйте Close(), вместо этого оберните ваши вызовы ISessionвнутри usingоператора или управляйте жизненным циклом вашей ISession где-то еще .

Из документации :

Время от времени ISessionбудет выполняться SQL-операторы, необходимые для синхронизации состояния соединения ADO.NET с состоянием объектов, хранящихся в памяти. Этот процесс, flush, происходит по умолчанию в следующих точках

  • от некоторых вызовов Find()илиEnumerable()
  • из NHibernate.ITransaction.Commit()
  • из ISession.Flush()

Операторы SQL выдаются в следующем порядке

  1. все объекты вставки, в том же порядке соответствующие объекты были сохранены с помощью ISession.Save()
  2. все обновления сущностей
  3. все удаления коллекции
  4. все удаления, обновления и вставки элементов коллекции
  5. все коллекции вставок
  6. все удаления объектов, в том же порядке соответствующие объекты были удалены с помощью ISession.Delete()

(Исключением является то, что объекты, использующие генерацию собственных идентификаторов, вставляются при сохранении.)

За исключением случаев, когда вы работаете с подробностями Flush(), нет абсолютно никаких гарантий относительно того, когда Session выполняет вызовы ADO.NET, только порядок, в котором они выполняются . Однако NHibernate гарантирует, что ISession.Find(..)методы никогда не будут возвращать устаревшие данные; и при этом они не будут возвращать неправильные данные.

Можно изменить поведение по умолчанию, чтобы сброс происходил реже. FlushModeКласс определяет три различных режима: только на одном уровне во время фиксации (и только тогда , когда NHibernate ITransactionиспользуется API), флеш автоматически с помощью объясняемая рутина, или никогда не вровень , если Flush()не вызывается явно. Последний режим полезен для длительных единиц работы, где ISessionон остается открытым и отключенным в течение длительного времени.

...

Также обратитесь к этому разделу :

Завершение сеанса включает четыре отдельных этапа:

  • очистить сессию
  • совершить сделку
  • закрыть сессию
  • обрабатывать исключения

Промывка сессии

Если вы используете ITransactionAPI, вам не нужно беспокоиться об этом шаге. Это будет выполнено неявно, когда транзакция будет зафиксирована. В противном случае вам следует позвонитьISession.Flush() чтобы убедиться, что все изменения синхронизированы с базой данных.

Фиксация транзакции базы данных

Если вы используете API-интерфейс NHibernate ITransaction, это выглядит следующим образом:

tx.Commit(); // flush the session and commit the transaction

Если вы сами управляете транзакциями ADO.NET, вам следует вручную Commit()выполнить транзакцию ADO.NET.

sess.Flush();
currentTransaction.Commit();

Если вы решили не фиксировать свои изменения:

tx.Rollback();  // rollback the transaction

или:

currentTransaction.Rollback();

Если вы откатываете транзакцию, вы должны немедленно закрыть и отменить текущий сеанс, чтобы убедиться, что внутреннее состояние NHibernate согласовано.

Закрытие сессии

Вызов, чтобы ISession.Close()отметить конец сеанса. Основное значение Close () состоит в том, что соединение ADO.NET будет прервано сессией.

tx.Commit();
sess.Close();

sess.Flush();
currentTransaction.Commit();
sess.Close();

Если вы Close()указали собственное соединение, возвращает ссылку на него, чтобы вы могли вручную закрыть его или вернуть в пул. В противном случае Close()возвращает его в пул.

Мэтт Хинце
источник
2
для меня эта строка была ключевой: «Главное значение Close () заключается в том, что соединение ADO.NET будет разорвано сессией». Если вы не вызываете ISession.Close (), ваши соединения будут заполняться до тех пор, пока вы не получите тайм-ауты БД. : o
Дэйв Тибен
Мы обычно: открываем session session.BeginTransaction () work ... session.Transaction.Commit () session.BeginTransaction () work ... session.Transaction.Commit () session.BeginTransaction () work .. session.Transaction.Commit () распоряжаться сессией.
Проворный джедай
Блестящая рецензия, +1 и т. Д. - однако я думаю, что редактирование может потребоваться, потому что вы говорите вверху «Никогда не использовать закрытие», а затем «Если вы откатываете транзакцию, вы должны немедленно закрыть и отменить текущий сеанс»
SpaceBison
Можно ли изменить порядок операторов SQL. Я имею в виду, мне нужно выполнить обновление объекта объекта, а затем вставить, потому что у меня есть ограничение в соответствующей таблице.
bob_saginowski
14

Начиная с NHibernate 2.0, транзакции требуются для операций с БД. Поэтому ITransaction.Commit()вызов обработает любую необходимую очистку. Если по какой-то причине вы не используете транзакции NHibernate, то не будет автоматической очистки сеанса.

Шон Карпентер
источник
1

Время от времени ISession будет выполнять операторы SQL, необходимые для синхронизации состояния соединения ADO.NET с состоянием объектов, хранящихся в памяти.

И всегда использую

 using (var transaction = session.BeginTransaction())
 {
     transaction.Commit();
 }

после того, как изменения будут зафиксированы, чем эти изменения для сохранения в базе данных, мы используем транзакцию.Commit ();

гусаки
источник
0

Вот два примера моего кода, где он потерпит неудачу без session.Flush ():

http://www.lucidcoding.blogspot.co.uk/2012/05/changing-type-of-entity-persistence.html

в конце этого вы можете увидеть фрагмент кода, в котором я установил идентификационную вставку, сохранил сущность, затем сбросил, затем отключил идентификационную вставку. Без этого сброса казалось, что он устанавливает и выключает вставку идентификатора, а затем сохраняет сущность.

Использование Flush () дало мне больше контроля над тем, что происходит.

Вот еще один пример:

Отправка сообщения NServiceBus внутри TransactionScope

Я не совсем понимаю, почему на этом, но Flush () предотвратил мою ошибку.

Пол Т Дэвис
источник