2013-01-10 141 views
0

我有一個Note域類,並且當保存一個新的筆記時,我需要爲它創建一個NoteEvent,爲後代記錄該筆記已經創建。 Note有一個NoteEvents的集合,並且每個NoteEvent都會跟蹤它屬於哪個NoteGrails中的奇怪的afterInsert/afterUpdate循環

Note類:

class Note { 
    String noteText 
    Date dateCreated 

    static hasMany = [events : NoteEvent] 
} 

NoteEvent類:

class NoteEvent { 
    Date dateCreated 
    String type 

    static belongsTo = [note : Note] 
} 

要處理的新NoteEvents節約創建一個音符的時候,我用afterInsert,因爲我節省筆記(在每次保存一個新筆記之後,創建特定的事件代碼將會是重複和耗時的),並且顯然不涉及Note的實例仍然存在 - NoteEventnote沒有任何內容。

所以現在我Note類:

class Note { 
    String noteText 
    Date dateCreated 

    static hasMany = [events : NoteEvent] 

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

但我也需要創建一個NoteEvent當這些筆記中的一個更新,而這正是困惑和沮喪和顯著缺乏咖啡進來了。爲了在更新時附加一個新的「更新」NoteEvent到筆記,我精心決定再次使用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時,剛剛保存的實例會立即再次保存,這會導致事件被觸發,現在我有兩個音符事件:從剛剛創建的事件開始保存了該筆記,以及從「創建」筆記導致筆記再次保存時的「更新」筆記。

我不清楚在這種情況下如何使用「之前」的事件。我還能如何做到這一點?

回答

3

您實際上可以使用beforeInsertbeforeUpdate方法。這是因爲addTo*方法不要求Note是一個持久實例。當Note節省,因爲之前的Note保存在beforeUpdate方法NoteEvent添加

NoteEvent將節省。請查看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構造NoteEvent

class Note { 
    String noteText 
    Date dateCreated 

    static hasMany = [events : NoteEvent] 

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

    def beforeUpdate = { 
     addToEvents(type: "UPDATED") 
    } 
} 
+0

我想你的意思是「不要求'NoteEvent'是一個持久化的實例。」:)無論如何:非常感謝你的答案。我試過了,它爲我工作。™ –

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 s。但這真是解決整個問題的方法。只需刪除afterInsertafterUpdate回調,並執行事務服務方法中的所有工作(創建,更新和刪除)。無論何時執行多個數據庫更新,您都應該事務性地執行它們,以便它們全部成功或全部失敗。這也符合您的防混亂要求,因爲所有的工作都被封裝在服務中。

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函數。

+0

嗯......我同意使用集中的CRUD方法可以更容易地修改各種持久性事件觸發的行爲。但爲了我的目的(這裏有很多現有的代碼,這將是大量的工作來移動)使用領域類的'before'或'after'事件是更可取的。生成的Grails控制器直接保存實例,現在我無法改變它。順便說一下,我所說的「遍佈全球」是指「在多個控制器中,並且可能是一兩個服務器」。(這確實是一個警告信號,但是這是在一個已建立的項目中......) –