编写自定义grails唯一约束,该约束必须适用于命令对象和域对象

时间:2014-03-03 23:24:35

标签: grails groovy gorm

任务是支持在创建时最初输入的数据库中存储的用户名,并使用'ilike'标准代替'eq'检查唯一性。

在挖掘中,我看到了UniqueConstraint的两个实现,一个由grails-datastore-gorm提供,另一个由grails-hibernate提供。我知道我需要在一个块中执行此查找,我在会话对象上重置FlushMode,因此在验证数据之前,hibernate不会将更改保留到db。这是我写的自定义验证器:

    @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')
    }
}

这使我们的集成测试通过,但是当我在调用importFrom的控制器中使用它来验证用户名时,invokeMethod无法在命令对象上找到id。例如:

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
    }
}

当我unique: true RealUser时,UserCommandObject验证就好了。当我将其更改为自定义验证程序iunique: true时,UserCommandObject会抱怨它不是域类。

我无法看到unique: true如何对命令对象起作用,因为我看到的两个实现都只适用于域对象(并且在没有域对象的情况下调用时抛出GrailsRuntimeException。)

有什么想法吗?我是否想过这个?

从另一个角度来看,是否有任何令人信服的理由支持存入用户名,并且在验证之前只在输入上调用lower()

1 个答案:

答案 0 :(得分:1)

自定义验证器中的findByPropertyNameIlike(val)会不会这样做?

static constraints = {
    propertyName blank: false, validator: { val, obj, errors ->

        def results = this.findByPropertyNameIlike(val)

        if(results) {
            errors.rejectValue('propertyName', 'unique.propertyName')
            return false
        }

        return true
    }
}