Grails hook into GORM beforeUpdate()
I have an internal requirement with nested domain classes where I want updates to a parent relationship to be propagated to children. A code example may make it clear:
class Milestone {
static belongsTo = [project:Project]
static hasMany = [goals:OrgGoals, children:Milestone]
String name
Date start
Date estimatedEnd
Date achievedEnd
...
}
When the parent milestone's estimatedEnd is updated, I want the children's estimated to automatically be updated by the same amount. GORM's beforeUpdate() hook seems like a logical place to do this:
To make life easier, I'd like to use some simple Date arithmetic, so I've added the following method to the Milestone class:
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 }
}
}
No compilation errors. But when I run the following integration test:
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)
}
I get:
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
Which suggests that the beforeUpdate is not firing and doing what I want. Any ideas?
I have a solution for you.
1) Call save(flush:true) on your second save call after you update milestone1's estimatedEnd. This will force beforeUpdate() to fire immediately.
2) Even after you do #1, your assertion will continue to fail because you are comparing 2 slightly different dates (you use a separate Date object within each Milestone constructor, so the 2nd date is slightly later/greater than the first.) If you used the same date instance, e.g.
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,
)
then the assertion will be successful. I will leave to you what to do next in terms of the best way to compare slightly different dates, but you might have to tolerate a precision difference on the order of milliseconds.
Hope that helps,
Jordan
精彩评论