複数の行を挿入するNonUniqueObjectExceptionエラー、LAST_INSERT_ID()は0を返します
-
05-07-2019 - |
質問
MySQLデータベースを使用するASP.NET MVCアプリでNHibernate / Fluent NHibernateを使用しています。私は、かなりの量のデータを(挿入量と比較して)読み取り、処理し、最終的に(現在)約50レコードを挿入する操作に取り組んでいます。開始/終了要求イベントハンドラーで作成/破棄される要求ごとに1つのISessionがあります( http://ayende.com/Blog/archive/2009/08/06/challenge-find-the-bug-fixes.aspx )、データを読み込んで新しいオブジェクトを追加しています(セクション16.3の https://www.hibernate.org/hib_docs/nhibernate/html/example-parentchild.html )、最後にセッションでFlush()を呼び出して、実際にすべての挿入を実行します。
データの取得と遅延読み込みは正常に機能し、Flushを呼び出すと、正確に2つの新しいレコードが挿入されます(これを見つけるために手動でテーブルをチェックしています)。その後、次のエラーが表示されます。
NonUniqueObjectException:同じ値を持つ異なるオブジェクト 識別子の値はすでに セッションに関連付けられている:0、 エンティティ:...
NHibernateは初めてであり、ソリューションの検索中にIdプロパティのジェネレーターをネイティブとIDの両方に明示的に設定しようとしました(MySQLデータベースであり、Id列はauto_incrementがオンのintです)。 Idプロパティの値を0に設定します。ただし、エラーは表示されます。
また、Flushを異なる時間に(実際にはINSERTごとに1回)呼び出してみましたが、同じエラーが発生しますが、0以外のアイデンティティ値とプロセス内のランダムなポイントに対して(時々取得できません)このシナリオではすべてですが、場合によっては異なる時点で行います。
ここからどこに行くべきかわかりません。どんな助けや洞察も大歓迎です。
編集:以下の回答を参照してください。
解決
編集:最初に別の" answer"を投稿しましたそれは実際には問題を解決しませんでしたが、私が発見した他の人のために私の発見をここに文書化したいと思います。
問題を把握してこの問題を解決しようとして数日後、問題がしばらく消え去ってから断続的に戻ってきたために非常にイライラしました(変更が修正されたことを何度も考えさせられました) 、実際にはそうではなかった場合)、実際の問題を追跡したと思います。
NHibernateのlog4netレベルを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()"の間の接続を閉じていたようです。 INSERTステートメント用にMySQLによって生成されたIDを取得します。むしろ、問題が断続的であると信じる理由の1つである、接続を閉じる時々であると言うべきです。私は今リンクを見つけることができませんが、接続が閉じられて再度開かれた場合でも、MySQLがLAST_INSERT_ID()から正しい値を時々返すことをすべての検索で読んだと思います断続的だったと思う理由。ただし、ほとんどの場合、LAST_INSERT_ID()は、INSERTの後に接続が閉じられて再度開かれた場合、0を返します。
この問題を修正するには2つの方法があるようです。最初はパッチです。 NHibernate 2.1.1に含まれるかのように見える、またはINSERTとSELECT LAST_INSERT_ID()を強制的に一緒に実行するNHibernateの独自のビルドを作成するために使用できるように見える。次に、このブログ投稿では、ISessionが明示的に閉じられるまでNHibernateが接続を閉じないようにします。
私は後者のアプローチを取りました。これはFluentNHibernateで次のように行われます。
Fluently.Configure()
...
.ExposeConfiguration(c => c.Properties.Add("connection.release_mode", "on_close"))
...
これには、コードを大幅に高速化するという副作用もありました。実行に20〜30秒かかっていた(この変更を行う前にたまたま動作していたとき)が7〜10秒で実行されるようになったため、同じ作業を1/3の時間で実行しています。