在Grails中,您可以在项目的Config.groovy文件中定义全局约束,如下所示
grails.gorm.default.constraints = {
myShared(nullable: false, blank: false)
}
并在域内使用它们
static constraints = {
name(shared: "myShared")
}
由于我们的域类在几个Grails项目中被重用,因此它们被拆分为插件。插件的Config.groovy文件被排除在外,因此定义全局约束是行不通的。因此,我创建了一个Constraints.groovy文件,该文件将合并到包含域类的插件的插件描述符中的应用程序配置中。这有效,但我仍然得到运行主项目的以下异常(grails run-app):
Caused by GrailsConfigurationException: Property [test.plugin.TestDomain.name] references shared constraint [myShared:null], which doesn't exist!
在Grails核心进行一些调试之后,我发现在运行插件描述符之前,域类已经使用共享约束进行了初始化。
public DefaultGrailsDomainClass(Class<?> clazz, Map<String, Object> defaultConstraints)
构造函数中的映射包含共享约束。如果我将全局约束放在主项目的Config.groovy文件中,它包含已定义的约束,一切正常。但是如果我在插件描述符中合并它们,那么这个映射是空的,并且会抛出异常。
我的问题是,是否有可能以某种方式在Grails插件中定义全局约束?我可能错过了什么吗? 将全局约束复制到每个Grails项目中不应该是解决方案。另外,不使用其他插件来定义约束的解决方案也是首选。
我们正在使用Grails 2.2.4。答案 0 :(得分:1)
由于Grails初始化doWithSpring
闭包中的约束,我认为你不能使用配置文件来完成它。
但是如果你看DomainClassGrailsPlugin
,你就可以访问配置对象了。
def doWithSpring = {
def config = application.config
def defaultConstraintsMap = getDefaultConstraints(config)
...
}
所以我认为你可以做一些像(未经测试)
的事情def loadBefore = ['domainClass']
def doWithSpring = {
def config = application.config
config.grails.gorm.default.constraints = {
myShared(nullable: false, blank: false)
}
}
答案 1 :(得分:1)
我对Sérgio的回答进行了进一步的调试,并提出了一个可能适用于某些项目的解决方案,而有些则不会。不幸的是,我们的Grails项目工作属于后一组项目...但首先要做的事情。这就是我所做的。
我设置了一个完全空的Grails项目和Grails插件项目。该插件包含在内,以反映我们在Grails项目中的工作情况。
我在插件的conf目录中创建了一个文件Constraints.groovy,如问题所述。
grails.gorm.default.constraints = {
myShared(nullable: false, blank: false)
}
为了让这些共享约束可用于在插件中进行测试,我将此文件添加到Config.groovy中的配置位置。
grails.config.locations = [Constraints]
现在是为主项目提供这些约束的部分。这是通过向插件描述符添加一些行来实现的。
def loadBefore = ['domainClass']
Sérgio建议我更改了加载顺序。虽然我不确定这是否真的有必要。关于为什么我不确定这一点的更多细节。
def doWithSpring = {
ConstraintEvalUtils.clearDefaultConstraints()
mergeConfig(application)
}
protected mergeConfig(application) {
application.config.merge(loadConfig(application))
}
protected loadConfig(application) {
new ConfigSlurper(Environment.current.name).parse(application.classLoader.loadClass("Constraints"))
}
这两种方法负责将Constraints.groovy的内容加载和合并到应用程序配置中。但实际上诀窍是在合并之前调用ConstraintEvalUtils.clearDefaultConstraints()
。
/**
* Looks up the default configured constraints from the given configuration
*/
public static Map<String, Object> getDefaultConstraints(ConfigObject config) {
def cid = System.identityHashCode(config)
if (defaultConstraintsMap == null || configId != cid) {
configId = cid
def constraints = config?.grails?.gorm?.default?.constraints
if (constraints instanceof Closure) {
defaultConstraintsMap = new ClosureToMapPopulator().populate((Closure<?>) constraints);
}
else {
defaultConstraintsMap = Collections.emptyMap()
}
}
return defaultConstraintsMap
}
此方法(也在ConstraintEvalUtils
中)被调用以加载共享约束。正如您所看到的,结果会缓存在defaultConstraintsMap
中。因此,如果在第一次调用此方法时共享约束为空,则它始终返回空映射。所以我调试了这个方法,以找出实际调用此方法的时间。它总是返回一张空地图。即使我在插件描述符中将loadBefore
设置为core
,该方法也会在插件描述符之前调用(不止一次)。正如我已经提到的ConstraintEvalUtils.clearDefaultConstraints()
工作,它清理缓存的约束,默认约束可以从我将约束合并到的配置中新加载。
这就是它。实际上不需要添加很多行。它适用于我的测试项目,也可能适用于其他项目,但不适用于我们的Grails项目。如果我在那里工作,我会更新我的答案。