Question

I trying to build a dynamic query similar to:

def domain = DomainName
def ids = 1
def domainClass = "$domain" as Class
domainClass.find("from ${domain} as m where m.job = ${ids} ").id

But it's not working.

If I'm trying this, all is fine:

def domain = DomainName
def ids = 1
DomainName.find("from ${domain} as m where m.job = ${ids} ").id

How can I use dynamic domain class name with find?

Was it helpful?

Solution

The simplest way is to use the getDomainClass method:

String domainClassName = 'com.foo.bar.Person'
def ids = 1
def domainClass = grailsApplication.getDomainClass(domainClassName).clazz
domainClass.find("from $domainClassName as m where m.job = ${ids} ").id

Note that if you're trying to get a single instance by id, use get:

long id = 1234
def person = domainClass.get(id)

and if you want to get multiple instances and you have a list of ids, you can use getAll

def ids = [1,2,3,4,5]
def people = domainClass.getAll(ids)

Also it's a REALLY bad idea to use GStrings with property values embedded - Google 'SQL Injection'

For example to find a person by username:

String username = 'foo'
def person = domainClass.find(
    "from $domainClassName as m where m.username=:username",
    [username: username])

OTHER TIPS

You should be able to do this by explicitly using the GroovyClassLoader:

def domain = "DomainName"
def c = new GroovyClassLoader().loadClass(domain)
c.find('...').id

The best way to get a Domain class dynamically is through the GrailsApplication object. Example:

import org.codehaus.groovy.grails.commons.ApplicationHolder

def domainName = "full.package.DomainName"
def domainGrailsClass = ApplicationHolder.application.getArtefact("Domain", domainName)
def domainClass = domainGrailsClass.getClazz()
domainClass.find("from ${domainGrailsClass.name} as m where m.job = ${ids}").id

You can also use Class.forName() just as you would in Java. Use the 3 parameter version and pass in the current thread context class loader:

import grails.util.GrailsNameUtils

def domainName = "full.package.DomainName"
def domainClass = Class.forName(domainName, true, Thread.currentThread().getContextClassLoader())
domainClass.find("from ${GrailsNameUtils.getShortName(domainName)} as m where m.job = ${ids}").id

Classloaders are an ugly topic in Java and JVM frameworks. In Grails, you almost always want to use the thread context classloader. But even better is to use the GrailsApplication interface and avoid the issue altogether.

use GrailsClassUtils

GrailsClassUtils.getShortName(DomainName)

to get the name of the class, so this should work... if I understood the question

def domainClassName = GrailsClassUtils.getShortName(DomainName)
def ids = 1
DomainName.find("from ${domainClassName} as m where m.job = ${ids} ").id
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top