我想用Grails(GORM)中的Criteria API创建一个查询。 查询必须是这样的:
MyEntity.createCriteria().list{
assoc{
parent{
eq("code", val)
}
}
}
我需要的是从String对象动态构建嵌套闭包。上例中的字符串将为"assoc.parent.code"
。我逐点拆分String(通过执行String.split("\\.")
),但我不知道如何构造嵌套的闭包:
assoc{
parent{
eq("code", val)
}
}
动态地基于上面分割字符串的数组。
答案 0 :(得分:2)
createAlias
怎么样?你可以尝试这样的事情:
def path = "assoc.parent.code"
def split = path.split(/\./)
MyEntity.createCriteria().list {
// this will get you 'createAlias( assoc.parent, alias1 )'
createAlias split.take( split.size() - 1 ), "alias1"
// this will get you 'eq(alias1.code, userInput)'
eq "alias1.${split[-1]}", userInput
}
此代码段不是通用的,但您明白了。
<强>更新强>
不常规,但您可以构建一个包含带闭包的代码的字符串,并使用GroovyShell
对其进行评估:
assoc = 'assoc.parent.child.name'
split = assoc.split( /\./ )
path = split[-2..0] // will get us 'child.parent.assoc';
// we will build it from inside-out
def firstClosure = "{ eq '${split[-1]}', 'john doe' }"
def lastClosure = firstClosure
for (entity in path) {
def criteriaClosure = "{ ${entity} ${lastClosure} }"
lastClosure = criteriaClosure
}
assert lastClosure == "{ assoc { parent { child { eq 'name', 'john doe' } } } }"
def builtClosure = new GroovyShell().evaluate("return " + lastClosure)
assert builtClosure instanceof Closure
答案 1 :(得分:1)
更通用的方法是metaClass
String,如下所示,并将其用于任何类型的分隔符
. | , - ~
等等。
String.metaClass.convertToClosureWithValue = {op, val ->
split = delegate.split(op) as List
if(split.size() == 1) {return "Cannot split string '$delegate' on '$op'"}
items = []
split.each{
if(it == split.last()){
items << "{ eq '$it', $val }"
split.indexOf(it).times{items.push("}")}
} else {
items << "{$it"
}
}
println items.join()
new GroovyShell().evaluate("return " + items.join())
}
assert "assoc.parent.child.name".convertToClosureWithValue(/\./, "John Doe") instanceof Closure
assert "assoc-parent-child-name".convertToClosureWithValue(/\-/, "Billy Bob") instanceof Closure
assert "assoc|parent|child|grandChild|name".convertToClosureWithValue(/\|/, "Max Payne") instanceof Closure
assert "assoc~parent~child~grandChild~name".convertToClosureWithValue('\\~', "Private Ryan") instanceof Closure
assert "assocparentchildname".convertToClosureWithValue(/\|/, "Captain Miller") == "Cannot split string 'assocparentchildname' on '\\|'"
//Print lines from items.join()
{assoc{parent{child{ eq 'name', John Doe }}}}
{assoc{parent{child{ eq 'name', Billy Bob }}}}
{assoc{parent{child{grandChild{ eq 'name', Max Payne }}}}}
{assoc{parent{child{grandChild{ eq 'name', Private Ryan }}}}}