插入多行时出现NonUniqueObjectException错误,LAST_INSERT_ID()返回0

时间:2009-09-10 04:39:36

标签: mysql asp.net-mvc nhibernate fluent-nhibernate

我在带有MySQL数据库的ASP.NET MVC应用程序中使用NHibernate / Fluent NHibernate。我正在进行一项操作,该操作读取相当多的数据(相对于插入的数量),处理它,并最终插入(当前)大约50条记录。每个请求都有一个ISession,它在开始/结束请求事件处理程序中创建/销毁(与http://ayende.com/Blog/archive/2009/08/06/challenge-find-the-bug-fixes.aspx完全相同),我正在读取数据并添加新对象(如{16}中{16}节所述}),最后在会话上调用Flush()来实际运行所有插入。

获取数据和延迟加载工作正常,当我调用Flush时,正在插入2个新记录(我手动检查表以找到它),然后我收到以下错误:

  

NonUniqueObjectException:具有相同的不同对象   标识符值已经存在   与会话相关联:0,of   实体:......

我是NHibernate的新手,在搜索解决方案时尝试将Id属性的生成器显式设置为Native和Identity(它是一个MySQL数据库,Id列是一个带有auto_increment的int),并明确设置未保存的但是,Id属性的值为0.我仍然会收到错误。

我也尝试过在不同的时间调用Flush(每次INSERT一次有效)然后我得到相同的错误,但是对于0以外的身份值和过程中看似随机的点(有时我不明白所有在这种情况下,但有时我在不同的地方做。)

我不知道从哪里开始。任何帮助或见解将不胜感激。

编辑:请参阅下面的答案。

1 个答案:

答案 0 :(得分:2)

编辑:我最初发布了一个不能解决问题的不同“答案”,但我想在此处将我的调查结果记录给可能遇到此问题的其他人。

经过几天试图找出问题并解决了这个问题,并且非常沮丧,因为这个问题似乎已经消失了一段时间然后间歇性地回来(让我多次想到我修改了它的一次改变) ,实际上并没有),我相信我已经找到了真正的问题。

在我将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()”之间的连接,以获取MySQL为INSERT语句生成的id。或者更确切地说,我应该说有时关闭连接,这是我认为问题是间歇性的一个原因。我现在找不到链接,但是我相信我也会在我的所有搜索中读到MySQL将有时从LAST_INSERT_ID()返回正确的值,即使连接已关闭并重新打开,这是另一个我相信这是间歇性的。但是,大多数情况下,如果连接关闭,LAST_INSERT_ID()将返回0,并在INSERT之后重新打开。

看来有两种方法可以解决这个问题。首先是一个补丁available here,看起来它会进入NHibernate 2.1.1,或者你可以使用它来自己构建NHibernate,这会强制INSERT和SELECT LAST_INSERT_ID()一起运行。其次,您可以将connection.release_mode设置为on_close,如this blog post中所述,这会阻止NHibernate关闭连接,直到明确关闭ISession。

我采用了后一种方法,这是在FluentNHibernate中完成的,如下所示:

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

这也有大幅加速我的代码的副作用。运行20-30秒(当我在进行此更改之前恰好工作的时间)现在正在7-10秒内运行,因此它在~1 / 3时间内完成相同的工作。