Validation differences between Grails 1.1 and 1.2
…or My Penance For Ignoring Validation Errors…
We recently updated our app from Grails 1.1.1 to 1.2.2. (We wanted to move all the way to 1.3.1 but since we build with maven, we finally decided to wait until a new grails-maven plugin is released. See GRAILS-6327.) During the upgrade, we hit two particularly annoying issues related to persistence and setting up associations.
The first involved a belongsTo relation like this:
class AppUser {
UserPrefs prefs = new UserPrefs()
}
class UserPrefs {
static belongsTo = [user: AppUser]
}
This worked under Grails 1.1, but under Grails 1.2 the prefs object was not getting persisted when the user was saved. Other belongsTo relations, both one-to-many and one-to-one worked as expected. We finally discovered that using the short form of the relation notation did work:
static belongsTo = AppUser
This post provided a clue as to where the problem may be. The user property of the Prefs object is not automatically set because the relationship is one-to-one. (At least, it is not automatically set all the time. If we load an AppUser object with Hibernate, it appears that the user property is set in the UserPrefs object. Go figure.) Under Grails 1.1, having a null user was apparently fine. My guess (based on the second error we hit, below) is that any validation errors caused by the child object were not stopping the save of the parent object. Grails 1.2, on the other hand, does care about the child object validation, though I could never get it to report anything in the errors property of either the parent or child object.
In order to work around the issue, I loosened the constraints on UserPrefs a little:
class UserPrefs {
static constraints = {
user(nullable: true)
}
static belongsTo = [user: AppUser]
}
With this change, cascading persistence works. Given, it’s not an ideal solution, but it does let us have access to the user property if needed.
What made this extra confusing is that we have a similar relationship in another set of classes that worked without a problem. The only difference there is that the child class is not automatically created with a static initializer as is done in for the prefs property in AppUser. It is always explicitly set into the owning object. I’ve run out of time to pursue this one further, so I don’t have a final answer. Anyone else out there have more insight into this?
The second persistence issue related to this section of code in the update method of one of our controllers:
def update = {
def hubMap = HubMap.get(params.id);
hubMap.properties = params;
if (hubMap.save()) {
...add success message to response...
} else {
...add failure message to response...
}
...
}
Again, the code worked fine under Grails 1.1.1, but the save call failed under 1.2.2. Unfortunately, there was a hole in both our unit and integration tests for this method, so we didn’t catch it until much later in the release cycle.
Was the JSON conversion in the controller’s get method that generated the original data for the browser different? Nope.
Had the behaviour of the properties meta-method changed? Nope.
The difference is in how Grails handles any existing validation errors on a domain object when you call save(). In our case, the JSON that was being sent to the controller (via a Prototype AJAX call) contained two properties that were references to other objects. The javascript object conversion in the browser was not setting these properties in a meaningful way for the AJAX call; they were both coming across with the string value [object Object]. Since these fields are never updated in this particular workflow, we had never checked what was happening to them. Grails obviously could not convert from the string values to the proper objects, it ignored the values and set two errors on the domain object to record them. However, we didn’t check for errors after the properties call. We went straight to the save call. Under Grails 1.1, deep within the saving sequence in a random class called AbstractDynamicPersistentMethod, you come across this bit of code:
doInvokeInternal(...) {
...
Errors errors = new BeanPropertyBindingResult(target, target.getClass().getName());
mc.setProperty(target, ERRORS_PROPERTY, errors);
...
}
Any existing errors are replaced with a fresh, clean BeanPropertyBindingResult object, wiping out the error information. We never spotted it because we never expected the properties with errors to change anyway. That hole has been closed in Grails 1.2:
doInvokeInternal(...) {
...
Errors errors = setupErrorsProperty(target);
...
}
The new setupErrorsProperty call will copy out existing errors. We put in a few adjustments to not attempt to update those properties and all is well.
So there you have it. A couple of gotchas in the upgrade path for grails. Hope this saves some folks from banging their head against the wall.