2011-05-08 99 views
1

我有一个嵌套域类的内部需求,我想更新父级关系以传播给子级。一个代码示例可以说清楚:Grails挂钩到GORM beforeUpdate()

class Milestone { 
    static belongsTo = [project:Project] 
    static hasMany = [goals:OrgGoals, children:Milestone] 
    String name 
    Date start 
    Date estimatedEnd 
    Date achievedEnd 
    ... 
} 

当父里程碑的estimatedEnd更新,我想孩子们的估计,自动相同的量进行更新。 GORM's beforeUpdate() hook似乎是一个合乎逻辑的地方要做到这一点:

为了使生活更轻松,我想用一些simple Date arithmetic,所以我增加了以下方法给里程碑类:

def beforeUpdate() 
    { 
     // check if an actual change has been made and the estimated end has been changed 
     if(this.isDirty() && this.getDirtyPropertyNames().contains("estimatedEnd")) 
     { 
      updateChildEstimates(this.estimatedEnd,this.getPersistentValue("estimatedEnd")) 
     } 
    } 

private void updateChildEstimates(Date newEstimate, Date original) 
    { 
     def difference = newEstimate - original 
     if(difference > 0) 
     { 
      children.each{ it.estimatedEnd+= difference } 
     } 
    } 

没有编译错误。但是,当我运行下面的集成测试:

void testCascadingUpdate() { 
     def milestone1 = new Milestone(name:'test Parent milestone', 
      estimatedEnd: new Date()+ 10, 
     ) 
     def milestone2 = new Milestone(name:'test child milestone', 
      estimatedEnd: new Date()+ 20, 
     ) 
     milestone1.addToChildren(milestone2) 
     milestone1.save() 
     milestone1.estimatedEnd += 10 
     milestone1.save() 
     assert milestone1.estimatedEnd != milestone2.estimatedEnd 
     assert milestone2.estimatedEnd == (milestone1.estimatedEnd + 10) 
    } 

我得到:

Unit Test Results. 

    Designed for use with JUnit and Ant. 
All Failures 
Class Name Status Type Time(s) 
MilestoneIntegrationTests testCascadingUpdate Failure Assertion failed: assert milestone2.estimatedEnd == (milestone1.estimatedEnd + 10) | | | | | | | | | | | Mon Jun 06 22:11:19 MST 2011 | | | | Fri May 27 22:11:19 MST 2011 | | | test Parent milestone | | false | Fri May 27 22:11:19 MST 2011 test child milestone 

junit.framework.AssertionFailedError: Assertion failed: 

assert milestone2.estimatedEnd == (milestone1.estimatedEnd + 10) 
     |   |   | |   |   | 

     |   |   | |   |   Mon Jun 06 22:11:19 MST 2011 
     |   |   | |   Fri May 27 22:11:19 MST 2011 
     |   |   | test Parent milestone 

     |   |   false 
     |   Fri May 27 22:11:19 MST 2011 
     test child milestone 

    at testCascadingUpdate(MilestoneIntegrationTests.groovy:43) 

    0.295 

这表明更新前不点火,做我想要的。有任何想法吗?

+0

为什么不使用'estimatedEnd'二传手? – 2011-05-08 11:30:04

+0

我怀疑GORM事件可能不会在测试中执行 – 2011-05-09 08:50:33

+0

@Victor:好点。我甚至没有考虑过这个问题,但这更有意义。 @Don:他们不会在单元测试后执行,我相信,但他们在集成测试中针对完整数据库运行。 – 2011-05-09 15:05:26

回答

5

我有你的解决方案。

1)更新里程碑1的估计结束后,在第二次保存呼叫时调用保存(刷新:true)。这将强制beforeUpdate()立即触发。

2)即使在您执行#1之后,您的断言仍然会失败,因为您正在比较两个稍微不同的日期(在每个Milestone构造函数中使用单独的Date对象,所以第二个日期略晚/第一个。)如果你使用了相同的日期实例,例如

Date date = new Date() 
def milestone1 = new Milestone(name:'test Parent milestone', 
      estimatedEnd: date + 10) 
def milestone2 = new Milestone(name:'test child milestone', 
      estimatedEnd: date + 20, 
     ) 

然后断言将成功。根据比较稍微不同的日期的最佳方法,我会告诉你接下来要做什么,但是你可能不得不忍受毫秒量级的精度差异。

希望帮助,

乔丹

+0

就是这样。好表演,老伙计!我有一种关于冲洗保存的感觉,但日期算术的事情是微妙的。谢谢您的帮助! – 2011-05-09 15:03:19