2

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?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Fabien Barbier
  • 1,514
  • 4
  • 28
  • 41

4 Answers4

9

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])
Burt Beckwith
  • 75,342
  • 5
  • 143
  • 156
  • I have a general idea. All programming languages has a kind of keywords / restricted words, documented in any introduction. Groovy should document in compact form list of factory "overloaded" methods. What do You think? – Jacek Cz Aug 20 '15 at 12:25
1

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

def domain = "DomainName"
def c = new GroovyClassLoader().loadClass(domain)
c.find('...').id
ig0774
  • 39,669
  • 3
  • 55
  • 57
  • 2
    In a grails app, it's better to use GrailsAwareClassLoader instead of GroovyClassLoader. The GrailsAwareClassLoader is the same, but also takes care of dynamic injections. In this particular case, the domain class will have been loaded at already and the GroovyClassLoader will just return the reference already loaded by the grails loader, but in general you could introduce some unexpected bugs this way. – ataylor Nov 20 '10 at 00:36
1

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.

ataylor
  • 64,891
  • 24
  • 161
  • 189
0

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
Aaron Saunders
  • 33,180
  • 5
  • 60
  • 80