【问题标题】:Grails "a different object with the same identifier value was already associated with the session" error [duplicate]Grails“具有相同标识符值的不同对象已与会话相关联”错误[重复]
【发布时间】:2012-09-30 07:14:48
【问题描述】:

可能重复:
Hibernate: different object with the same identifier value was already associated with the session

我在 Grails 中的控制器中有以下代码失败并显示 "a different object with the same identifier value was already associated with the session" 错误消息。 我已经访问了几个页面,上面说我必须在调用 save 之前调用 "merge" 最终出现此错误 Provided id of the wrong type for class com.easytha.QuizTag. Expected: class java.lang.Long, got class org.hibernate.action.DelayedPostInsertIdentifier

有人建议 grails 可搜索插件可能会导致此问题,我应该从我的域类中删除 searchable = true ,这不是一个选项(请参阅上一篇文章grails searcheable plugin search in inner hasMany class

需要指出的一点是,在调用 q.save() 时不会抛出错误,而是在调用 redirect redirect(action:"show",id:id) 时抛出错误!!

有什么建议吗?

def addTags(String tags,Long id){
        if(tags){
            String[] strTags = tags.split(",");
            Quiz q = Quiz.get(id)           
            for(String t in strTags){
                Tag tagToAdd = Tag.findByTag(t)

                if(!tagToAdd){
                    tagToAdd = new Tag(tag:t)
                    tagToAdd.save()
                }

                println "---> "+tagToAdd +" Quiz"+q?.id
                def qt = QuizTag.findByQuizAndTag(q,tagToAdd)
                if(!qt){
                    qt = new QuizTag(quiz:q,tag:tagToAdd);
                    q.addToTags(qt)
                }

            }           
            q.save()        
            redirect(action:"show",id:id)
        }
    }

-----------编辑---------------

Final code that worked with searchable plugin
        def addTags(String tags,Long id){
        if(tags){
            String[] strTags = tags.split(",");
            Quiz q = Quiz.get(id)           
            for(String t in strTags){
                if (q.tags.any { QuizTag qt -> qt.tag.tag == t }) { continue; }
                    Tag tagToAdd = Tag.findOrSaveByTag(t);
                    QuizTag qt = new QuizTag(quiz:q,tag:tagToAdd)
                    q.addToTags(qt)
                }           
            q.save(flush:true)      
            redirect(action:"show",id:id)
        }
    }

【问题讨论】:

  • 来自 save() 方法的描述:“除非使用flush参数,否则对象不会立即持久化。”这就是为什么只有在请求完成时才会出现错误。
  • @TiagoFarias 你是对的。在调用 q.save(flush:true) 之后,我在该行得到了错误,这里要注意的另一件事是,即使在错误之后我的数据仍然被保存!此外,仅当标签已经存在时才会发生此错误,这意味着“Tag.findByTag(t)”返回了一些东西
  • 我无法发布答案,但请看:保存前检查q.hasChanged()

标签: hibernate grails groovy grails-searchable


【解决方案1】:

既然你做了Quiz.get(id),你就有了一个“连接”的实例,所以你绝对不需要“合并”——这只是为了重新连接一个断开的实例。

我认为您收到错误的原因是该行:

def qt = QuizTag.findByQuizAndTag(q, tagToAdd)

QuizTag 实例拉入会话中,但Tag tagToAdd = Tag.findByTag(t) 已经将该实例与会话相关联,并且您正在分配给另一个变量。您现在有两个与会话关联的实例,它们都表示数据库中的同一行,qttagToAdd(它们具有相同的 id)。这会产生模棱两可的情况,并且是不允许的,因此会出现错误。

看来您实际上并不需要实例化qt(只有在它不存在时才采取行动)。因此,我建议进行查询以查明它是否存在(可能是“select count”),而不是实际检索对象实例。这样,您将只有一个对象的副本。

【讨论】:

  • 根据您的建议,“EDIT”中的新代码不应该可以工作吗?
  • 对不起,我不明白你的问题。什么是“编辑中的新代码”?
  • :) 我的意思是原始帖子中的编辑区域。我将变量分配更改为 Tag tagToAdd = Tag.findByTag(t)?:new Tag(tag:t) 和 def qt = QuizTag.findByQuizAndTag(q,tagToAdd)?:new QuizTag(quiz:q,tag:tagToAdd);你不认为这会奏效吗!
  • 不,我不这么认为。您需要安排一些事情,以免在同一个会话中两次实例化同一个对象。
  • 嘿,我在原帖的---EDIT---中再次更新了我的代码,但我仍然遇到同样的错误!!有什么提示吗?
【解决方案2】:

Grails 默认使用 Hibernate 代理延迟加载集合。因此,可能正在创建重复的 QuizTag 代理(或代理和膨胀对象),这会导致您的问题。

你可以试试这样的:

Quiz q = Quiz.get(id)         
for(String t in strTags){
    // Check if the tag is already joined to this quiz and
    // skip a dynamic finder load later
    if (q.tags.any { QuizTag qt -> qt.tag.tag == t }) { continue; }
    // Find or create-save the tag to join
    Tag tagToAdd = Tag.findOrSaveByTag(t);
    QuizTag qt = new QuizTag(quiz:q,tag:tagToAdd)
    qt.save()
    q.addToTags(qt)
}

【讨论】:

  • 好吧,我上面的代码工作了,但现在它没有索引标签和测验!所以没有搜索结果:)
  • 行为似乎很随机,它刚刚开始工作,但现在调用 q.save(flush:true) 时出现同样的错误
猜你喜欢
  • 2012-06-06
  • 2019-07-27
  • 2011-03-16
  • 2011-04-02
  • 2011-05-10
  • 1970-01-01
  • 2011-04-06
  • 2013-04-21
相关资源
最近更新 更多