在Grails中奇怪的afterInsert / afterUpdate循环

时间:2013-01-10 00:14:57

标签: grails gorm

我有一个Note域类,当保存新笔记时,我需要为其创建一个NoteEvent,为后代录制已创建笔记的内容。 Note有一个NoteEvents的集合,每个NoteEvent都会跟踪它所属的Note

Note类:

class Note {
    String noteText
    Date dateCreated

    static hasMany = [events : NoteEvent]
}

NoteEvent类:

class NoteEvent {
    Date dateCreated
    String type

    static belongsTo = [note : Note]
}

要在创建便笺时处理新NoteEvents的保存,我使用的是afterInsert,因为我正在整个地方保存便笺实例(这会重复且耗时。在每次保存新笔记后都有特定的事件创建代码),beforeInsert显然没有处理Note的持久化实例 - NoteEvent没有任何内容可供note作为Note

所以现在我的class Note { String noteText Date dateCreated static hasMany = [events : NoteEvent] def afterInsert = { def event = new NoteEvent(type: "CREATED") addToEvents(event) save() } } 课程是:

NoteEvent

但是当我更新其中一个笔记时,我还需要创建一个NoteEvent,这就是混乱和沮丧以及严重缺乏咖啡的地方。附加一个新的“更新的”{{1}在更新时的一个注释中,我出色地决定再次使用afterUpdate,以避免在需要更新Note实例时将事件创建代码洒在应用程序的所有位置。

现在,对于Note,我有:

class Note {
    String noteText
    Date dateCreated

    static hasMany = [events : NoteEvent]

    def afterInsert = {
        def event = new NoteEvent(type: "CREATED")
        addToEvents(event)
        save()
    }

    def afterUpdate = {
        def event = new NoteEvent(type: "UPDATED")
        addToEvents(event)
        save()
    }
}

要在笔记集合中添加新事件,我使用动态addTo()方法,然后需要save()个实例。但是在“之后”事件的情况下,这是对save()的第二次调用。因此,当我第一次保存新实例并调用afterInsert时,刚刚保存的实例会立即再次保存,这会导致afterUpdate事件被触发,现在我有两个音符事件:从我刚刚保存笔记时“创建”一个,并且从“创建”笔记中“更新”一个导致笔记再次保存。

我不清楚如何使用“之前”事件可以帮助解决这种情况。我怎么能这样做?

3 个答案:

答案 0 :(得分:4)

您实际上可以使用beforeInsertbeforeUpdate方法。这是因为addTo*方法不需要Note作为持久化实例。

NoteEvent会在保存Note时保存,因为在NoteEvent方法中保存Note之前添加了beforeUpdate。请查看addTo* docs以获得更长的解释。

我能够获得以下两个Note课程来完成我相信你想要的课程。我确实遇到了一个问题,即更新Note时会添加两个NoteEvent个对象。我能够通过确保控制器的更新方法使用noteInstance.save()而不是noteInstance.save(flush:true)来解决此问题。

class Note {
    String noteText
    Date dateCreated

    static hasMany = [events : NoteEvent]

    def beforeInsert = {
        def event = new NoteEvent(type: "CREATED")
        addToEvents(event)
    }

    def beforeUpdate = {
        def event = new NoteEvent(type: "UPDATED")
        addToEvents(event)
    }
}

如果你想要一个更精简的版本,addTo*方法知道添加了什么类型的对象,你可以使用Map的{​​{1}}构造函数

NoteEvent

答案 1 :(得分:3)

可能有一种方法可以使用beforeInsertbeforeUpdate,因为这些不需要保存Note实例。像这样进行二次更新/插入的典型方法是使用withNewSession,但在这种情况下,我不确定它是否有意义,因为这更像是创建一个独立的对象,而你需要重新加载新会话中的Note。不是那么糟糕,但没有表现。

执行此操作的一种方法是删除集合并直接保存NoteEvent个实例:

class Note {
   String noteText
   Date dateCreated

   Set<NoteEvent> getEvents() {
      NoteEvent.findAllByNote(this)
   }

   def afterInsert() {
      new NoteEvent(type: "CREATED", note: this).save()
   }

   def afterUpdate() {
      new NoteEvent(type: "UPDATED", note: this).save()
   }
}

class NoteEvent {
   Date dateCreated
   String type
   Note note
}

您丢失了级联,因此您希望删除事务服务方法中的Note实例,以便删除其关联的NoteEvent。但这确实是整个问题的解决方案。只需删除afterInsertafterUpdate回调,并在事务服务方法中完成所有工作(创建,更新和删除)。每当您执行多个数据库更新时,您应该以事务方式执行它们,以便它们都成功或全部失败。这也符合您的反杂乱要求,因为所有工作都封装在服务中。

答案 2 :(得分:2)

  

“因为我正在整个地方保存笔记实例”

我可以问你在哪里拯救他们吗?我会避免在您的控制器中保存您的域实例。如果你将它们全部保存在那里,那么可能值得一看整体设计。

就个人而言,我倾向于创建某种NoteService,如果可能的话我会集中CRUD操作。示例服务将是:

class NoteService 
{
    Note create (String noteText)
    {
       Note note = new Note(noteText: noteText)  
                       .addToEvents(new NoteEvent(type: NoteEvent.CREATED))
                       .save()   
    } 

    Note update (int id, String noteText)
    {
       Note note = Note.findById(id)
       note.setNoteText(noteText)
       note.addToEvents(new NoteEvent(type: NoteEvent.UPDATED))
           .save()   
    }

    .... 
}

我更喜欢上述方法的原因是,如果您发现自己希望在响应这些事件时做更多事情并避免重复代码,那么它会更好地扩展。

另一种方法可能是登录过滤器。但是,如果要在许多地方保存笔记实例,这可能会很棘手/混乱。

否则我会考虑使用上面提到的beforeInsert / beforeUpdate函数。