The task is to support usernames being stored in the database as they are originally typed upon creation, and to check for uniqueness using the 'ilike' criteria in place of 'eq'.
In digging around, I saw 2 implementations of UniqueConstraint
, one provided by grails-datastore-gorm and one provided by grails-hibernate. I understand that I need to perform this lookup in a block where I reset the FlushMode
on the session object, so hibernate does not persist the change to the db before the data is validated. Here is the custom validator I wrote:
@Override
protected void processValidate(final Object target, final Object propertyValue, Errors errors) {
def reject = false
doWithManualSessionIfAppropriate {
def targetId = null
try {
targetId = InvokerHelper.invokeMethod(target, 'ident', null)
} catch (Exception e) {
throw new GrailsRuntimeException('Could not determine id of target')
}
def results = []
results += constraintOwningClass."findAllBy${GrailsNameUtils.getClassName(constraintPropertyName, '')}Ilike"(propertyValue)
reject = results.any {
try {
def existingId = InvokerHelper.invokeMethod(it, 'ident', null)
targetId != existingId
} catch (Exception e) {
// the existing field is not in the db
false
}
}
}
if (reject) {
errors.rejectValue(constraintPropertyName, 'unique')
}
}
This made our integration tests pass, but when I used it in a controller that calls importFrom
to validate the username, invokeMethod
fails to find an id on the command object. For example:
RealUser.groovy:
class RealUser {
String username, passwordHash
static constraints = {
username iunique: true // where iunique is the name of my registered constraint
}
}
UserController.groovy:
@Validateable
class UserCommandObject {
String username, password, passwordConfirmation
static constraints = {
importFrom RealUser
}
}
When I had unique: true
on RealUser
, the UserCommandObject
validated just fine. When I changed it to my custom validator iunique: true
, UserCommandObject
complains that it's not a domain class.
I can't possibly see how unique: true
works for command objects, since both implementations I see only work on domain objects (and throw a GrailsRuntimeException
when called with no domain object).
Any ideas? Am I thinking too much into this?
Approaching from the other point of view, is there any compelling reason to not support storing usernames as-entered, and just calling lower()
on the input before validating?