Ошибка NonUniqueObjectException при вставке нескольких строк, LAST_INSERT_ID () возвращает 0 - PullRequest
2 голосов
/ 10 сентября 2009

Я использую NHibernate / Fluent NHibernate в приложении ASP.NET MVC с базой данных MySQL. Я работаю над операцией, которая читает довольно много данных (относительно того, сколько их вставлено), обрабатывает их и заканчивает тем, что вставляет (в настоящее время) около 50 записей. У меня есть одна ISession на запрос, который создается / уничтожается в обработчиках событий начала / конца запроса (точно так же, как http://ayende.com/Blog/archive/2009/08/06/challenge-find-the-bug-fixes.aspx),, и я читаю данные и добавляю новые объекты (как в разделе 16.3 на * 1004). * и, наконец, вызов Flush () для сеанса, чтобы фактически запустить все вставки.

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

NonUniqueObjectException: другой объект с тем же значение идентификатора уже было связанных с сеансом: 0, из юридическое лицо: ...

Я новичок в NHibernate и при поиске решения попытался явно установить для генератора свойства Id значение Native и Identity (это база данных MySQL, а в столбце Id - int с включенным auto_increment) и явно установить несохраненный значение для свойства Id равно 0. Однако я все еще получаю сообщение об ошибке.

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

Я не уверен, куда идти отсюда. Буду признателен за любую помощь или понимание.

РЕДАКТИРОВАТЬ: см. Ответ ниже.

1 Ответ

2 голосов
/ 11 сентября 2009

РЕДАКТИРОВАТЬ: Первоначально я опубликовал другой «ответ», который на самом деле не решил проблему, но я хочу задокументировать мои выводы здесь для всех, кто может столкнуться с этим.

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

Несколько раз после того, как я переключил уровень log4net для NHibernate на DEBUG, проблема исчезла, но я наконец смог получить ошибку с этим уровнем журнала. В журнал включены следующие строки:

Building an IDbCommand object for the SqlString: SELECT LAST_INSERT_ID()
...
NHibernate.Type.Int32Type: 15:10:36 [8] DEBUG NHibernate.Type.Int32Type: returning '0' as column: LAST_INSERT_ID()
NHibernate.Id.IdentifierGeneratorFactory: 15:10:36 [8] DEBUG NHibernate.Id.IdentifierGeneratorFactory: 
Natively generated identity: 0

И, глядя вверх, я увидел несколько строк:

NHibernate.AdoNet.ConnectionManager: 15:10:36 [8] DEBUG NHibernate.AdoNet.ConnectionManager: aggressively releasing database connection
NHibernate.Connection.ConnectionProvider: 15:10:36 [8] DEBUG NHibernate.Connection.ConnectionProvider: Closing connection

Кажется, что во время очистки сеанса и выполнения INSERT NHibernate закрывал соединение между оператором INSERT и "SELECT LAST_INSERT_ID ()", чтобы получить идентификатор, сгенерированный MySQL для оператора INSERT. Или, скорее, я должен сказать, что иногда закрывал соединение, что является одной из причин, по которой я считаю, что проблема была прерывистой. Я не могу найти ссылку сейчас, но я полагаю, что я также прочитал во всех моих поисках, что MySQL иногда возвращает правильное значение из LAST_INSERT_ID (), даже если соединение закрыто и повторно открыто, что является еще одной причиной Я считаю, что это было с перебоями. Однако в большинстве случаев LAST_INSERT_ID () вернет 0, если соединение закрыто и вновь открыто после INSERT.

Похоже, есть 2 способа исправить эту проблему. Во-первых, это патч , доступный здесь , который выглядит так, как будто он войдет в NHibernate 2.1.1, или который вы можете использовать для создания своей собственной сборки NHibernate, что заставляет INSERT и SELECT LAST_INSERT_ID () работать вместе , Во-вторых, вы можете установить для connection.release_mode значение on_close, как описано в этом сообщении в блоге , которое запрещает NHibernate закрывать соединение до тех пор, пока ISession не будет явно закрыт.

Я выбрал последний подход, который делается в FluentNHibernate следующим образом:

Fluently.Configure()
    ...
    .ExposeConfiguration(c => c.Properties.Add("connection.release_mode", "on_close"))
    ...

У этого также был побочный эффект резкого ускорения моего кода. То, что потребовалось 20-30 секунд для запуска (когда это только сработало до того, как я внес это изменение), теперь выполняется через 7-10 секунд, поэтому выполняет ту же самую работу в ~ 1/3 времени.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...