Putting my deep SQL and HSQL roots on hold I took advantage of Hibernate Criteria (via the excellent Groovy builder for super readable code) to safely generate a dynamic query today. I stumbled into two problems:
- how to keep things DRY - can I avoid rewriting the same query multiple times when simple things change?
- how can I elegantly query associations whose names I do not know in advance?
I was able to resolve both issues quite satisfactory.
Keeping queries DRY
My first attempt was to create a basic query structure as a Groovy closure and then modify it at runtime as the exact query would be known. Couldn't make it work. Fortunately, the second attempt was more successful: I used a simple factory pattern to create the closure dynamically before feeding it into the Criteria Builder:
def createQuery = { Boolean discriminate ->
return {
projections {
sum "property"
}
if (discriminate) {
idEq 42
}
}
}
MyClass.createCriteria().list(createQuery(false))
Choosing associations dynamically
In order to reference an association, you have to use its name in the closure as a method call with a closure as the parameter. Suppose you have the name of the property in a string object, how do you call it as a method in the closure ? Using evals could've worked, but that would present a serious security risk. Groovy's GStrings to the rescue:
def discriminateProp = params.prop
def discriminateBy = params.val
def createQuery = { Boolean discriminate ->
return {
projections {
sum "property"
}
if (discriminate) {
"${discriminateProp}" {
idEq discriminateBy.toLong()
}
}
}
}
MyClass.createCriteria().list(createQuery(true))
I still have to wrap my brains around possible security risks of this approach, but at the first glance it seems solid.