2009-12-05 2 views
0

У нас есть таблица базы данных SQL Server, которая состоит из идентификатора пользователя, некоторого числового значения, например. баланса и столбца версии.NHibernate session.flush() терпит неудачу, но вносит изменения

У нас есть несколько потоков, которые обновляют столбец значений этой таблицы параллельно, каждый в своей собственной транзакции и сеансе (мы используем модель за сеанс за потоки). Поскольку мы хотим, чтобы вся логическая транзакция происходила, каждая нить выполняет следующее:

  1. загружает текущую строку (отображается на тип).
  2. внести изменение в значение, основанное на старом значении. (например, добавить 50).
  3. Session.update (OBJ)
  4. session.flush() (так как мы оптимистичны, мы хотим убедиться, что мы имели правильное значение версии, перед обновлением)
  5. если шаг 4 (флеш) бросил StaleStateException, обновить объект (с lockmode.read) и шаг Гото 1

мы только делаем это определенное количество раз в логической операции, если мы не можем совершить это после того, как попытки X, мы отвергаем логическую операцию ,

каждая такая резьба периодически фиксируется, например. после 100 успешных логических транзакций, чтобы поддерживать принудительный ввод-вывод на управляемые уровни. значение - у нас есть одна транзакция базы данных (за транзакцию) с несколькими сбросами, по крайней мере один раз за каждое логическое изменение.

В чем проблема, спросите вы? ну, на коммитах мы видим изменения в неудавшихся логических объектах. , если это значение было 50, когда мы прошли первый шаг (первый раз), и мы попытались обновить его до 100 (но мы потерпели неудачу, так как, например, другой поток изменил его на 70), тогда значение 50 будет зафиксировано для этой строки. очевидно, это неверно.

Что нам не хватает здесь?

+0

мы отсутствующее исключение StackTrace :) –

+0

вы пробовали с помощью SQL Profiler, чтобы посмотреть на T-SQL, что NHibernate является порождающим в вашем случае неудач? – RickNZ

ответ

2

Ну, у меня здесь не много опыта, но одна вещь, которую я помню, читала in the documentation, так это то, что если возникает исключение, вы должны немедленно отменить транзакцию и избавиться от сеанса. Возможно, ваша проблема связана с тем, что сессия находится в противоречивом состоянии?

Кроме того, вызывать обновление в вашем коде здесь not necessary. Поскольку вы загружали объект в этот сеанс, он уже отслеживается nhibernate.

+0

@ Крис, можете ли вы предоставить ссылку на документацию, где он так говорит? Я не могу найти его. Это звучит странно и сломанно. (почему исключение аннулирует весь сеанс?) – Shachar

+0

http://nhforge.org/doc/nh/en/index.html#manipulatingdata-exceptions –

0

Я не гуру nhibernate, но ответ кажется простым.

Когда nhibernate загружает объект, он ожидает, что он не изменится в db до тех пор, пока он находится в кэше сеансов nhibernate.

Как вы упомянули, у вас есть приложение с несколькими потоками.

Это то, что происходит =>

  • первая нить загружает объект
  • второй поток загружает объект
  • первый поток меняет Entity
  • второй поток меняет сущность и => выясняет, что загруженный объект изменился на что-то еще и боится, что он изменил изменения, сделанные 1-м потоком - выбрасывает исключение, чтобы программист знал об этом.

У вас отсутствует механизм блокировки. Не могу много рассказать о том, как правильно и элегантно применять это. Может быть, Transaction поможет.

У нас были схожие проблемы, когда мы использовали nhibernate и raw ado.net одновременно (к счастью - просто для запросов - по крайней мере, для производственного кода).Все, что нам нужно было сделать - принудительно обновить db при вставке/обновлении, чтобы мы могли действительно запросить что-то через полнотекстовый поиск для некоторых конкретных объектов.

Был StaleStateException в тестах интеграции, когда мы использовали raw ado.net для сброса db. Сессия NHibernate была жива через кучу тестов, но каждый тест пытался очистить db без осознания NHibernate.

+0

Фактически, мы используем транзакции во многих таких атомных оптимистических обновлениях. Я сделал небольшое изменение в вопросе, чтобы прояснить это. Но - я не уверен, что понимаю ваш ответ. 2-й поток не может просто «узнать», что другой экземпляр загружен. они используют разные сеансы, поэтому объекты не разделяются. Также добавил к вопросу. – Shachar

+0

Обнаруживает, что * STATE * (некоторые измененные поля) изменился не на то, что другой сеанс загрузил его. Фактически - не было бы проблем, если бы использовался один и тот же сеанс, 2-й поток будет знать об изменениях (все же - я не уверен, что использование 1 сеанса подходит для вашего случая). –

+0

@ Арнис, вам не хватает важного различия здесь. Нам не нужны блокировки на уровне приложений (специально), так как каждый поток имеет свою собственную копию данных - следовательно, никакое обнаружение (или приложение) изменений состояния другим потоком никогда. – Shachar

1

Если вы все равно хотите внести изменения, почему вы беспокоитесь о версиях строк? Похоже, вы должны получить тот же результат, если вы просто всегда обновляете данные и позволяете последнему выиграть транзакцию.

Что касается того, почему обновление становится постоянным, это зависит от того, как выглядят операторы SQL для проверки/обновления версии и управления транзакциями, которые вы оставили из примера кода. Если вы включите ведение журнала Hibernate SQL, вероятно, станет очевидным, как это происходит.

Смежные вопросы