Groovy class invocato in modo dinamico e trovare metodo non funziona?
-
26-09-2019 - |
Domanda
I cercando di costruire una query dinamica simile a:
def domain = DomainName
def ids = 1
def domainClass = "$domain" as Class
domainClass.find("from ${domain} as m where m.job = ${ids} ").id
Ma non funziona.
Se io sto cercando questo, tutto va bene:
def domain = DomainName
def ids = 1
DomainName.find("from ${domain} as m where m.job = ${ids} ").id
Come posso utilizzare dinamica nome della classe di dominio con find?
Soluzione
Il modo più semplice è quello di utilizzare il metodo getDomainClass
:
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
Si noti che se si sta cercando di ottenere una singola istanza da id, uso get
:
long id = 1234
def person = domainClass.get(id)
e se si vuole ottenere di più istanze e si dispone di un elenco di ID, è possibile utilizzare getAll
def ids = [1,2,3,4,5]
def people = domainClass.getAll(ids)
Inoltre è un VERAMENTE cattiva idea di utilizzare gstrings con proprietà dai valori incorporati - Google 'SQL Injection'
Per esempio, per trovare una persona con nome utente:
String username = 'foo'
def person = domainClass.find(
"from $domainClassName as m where m.username=:username",
[username: username])
Altri suggerimenti
Si dovrebbe essere in grado di fare questo in modo esplicito utilizzando il GroovyClassLoader:
def domain = "DomainName"
def c = new GroovyClassLoader().loadClass(domain)
c.find('...').id
Il modo migliore per ottenere una classe di dominio dinamico è attraverso l'oggetto GrailsApplication. Esempio:
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
È inoltre possibile utilizzare Class.forName()
proprio come si farebbe in Java. Utilizzare la versione 3 parametro e passare la corrente classe contesto di thread 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
Classloader sono un brutto argomento nel framework Java e JVM. In Grails, si vuole quasi sempre di utilizzare il programma di caricamento classe contesto thread. Ma ancora meglio è quello di utilizzare l'interfaccia GrailsApplication ed evitare il problema del tutto.
GrailsClassUtils.getShortName(DomainName)
per ottenere il nome della classe, quindi questo dovrebbe funzionare ... se ho capito la domanda
def domainClassName = GrailsClassUtils.getShortName(DomainName)
def ids = 1
DomainName.find("from ${domainClassName} as m where m.job = ${ids} ").id