Grails accessing nested fields using gstrings - grails

I am trying to access a nested field using gstring but it throws exception groovy.lang.MissingPropertyException
I have two classes
Class Person{
Address address
}
Class Address{
String city
}
Somewhere in my code I am doing,
def person = Person.get(1)
def field = "address.city"
def city = person."${field}"
The line where I am trying to fetch city from person is throwing groovy.lang.MissingPropertyException
If I try to fetch a direct property using gstring it works but the above given code doesnt work.
Any help?

What you're doing here is trying to access a property by name address.city which is equal to person."address.city", which means that the dot here gets considered as part of property name - not as access separator as you expect. The following code should resolve your property:
def city = field.tokenize('.').inject(person) {v, k -> v."$k"}

I think that the problem is with dot operator for access to a subproperty.
This works:
class Person{
String address
}
def person = new Person(address:'Madrid')
def field = 'address'
assert 'Madrid' == person."${field}"
This works:
class Person{
Address address
}
class Address {
String city
}
def person = new Person(address: new Address(city: 'Madrid'))
def field = 'address'
def subField = 'city'
assert 'Madrid' == person."${field}"."${subField}"

Related

grails - org.hibernate.QueryException (could not resolve property)

I'm trying to get the list of a specific rental from the current user.
Code in controller:
def index() {
if (isLoggedIn()) {
String username = getPrincipal().username
def accountInstance = Account.findByUsername(username)
def rentalInstanceList = Rental.findAll("from Rental as r where r.account_id=:accountid", [accountid: accountInstance.id])
}
}
account_id is a foreign key.
After running I get the error:
could not resolve property: account_id of: ers.Rental
What am I doing wrong?
Generally, in HQL you have to use the field names as defined in your domain classes. So, your query should look like:
def list = Rental.findAll("from Rental where accountId=:accountid", [accountid: accountInstance.id])
or
def list = Rental.findAllByAccount accountInstance
or even
def list = Rental.findAllByAccount getPrincipal()
if the return type of getPrincipal() has the id field.
findAll is not limited to instances of the calling class so I use executeQuery instead. https://stackoverflow.com/a/8916483/5011228

Why does overriding putAt result in MissingPropertyException?

I've been trying to take advantage of operator overloading in Groovy by defining a custom putAt method in my POGO like this:
class Book {
Map additionalInfo = [:]
def putAt(key, value) {
additionalInfo[key] = value
}
}
So that I can do something like, book['notes'] = 'I like this one.' (let's say this makes sense). However, I've been getting:
groovy.lang.MissingPropertyException: No such property: notes for class: Book
at BookSpec.Set property using putAt(BookSpec.groovy:40)
My class is part of a Grails application so I'm not sure if Grails has something to do with the problem. Can anyone enlighten me on this?
The signature should be
def putAt(String key, value)
Instead of doing putAt and then override the operator, there is an easy/better way by adding the propertyMissing methods.
Here is the link for more explanation.
class Foo {
def storage = [:]
def propertyMissing(String name, value) { storage[name] = value }
def propertyMissing(String name) { storage[name] }
}
def f = new Foo()
f.foo = "bar"
assertEquals "bar", f.foo

findAllWhere and List fetch

So I have the domain class as follows:
class Enrollment {
Course course
Date date
User user
static constraints = {
}
}
In my controller, I have this action :
def persons = Enrollment.list(fetch :[user : "a"])
render persons
I am trying to fetch only a user named "a" and its corresponding map. But it displays all..I tried FindAllWhere but throws an error
No such property: user for class: tester.EnrollmentController
I am assuming that the User class has a name property.
What about:
def user = User.findByName("a")
def persons = user ? Enrollment.findAllByUser(user) : []
Assuming here that you can find a unique user (name probably isn't unique enough), otherwise I would do something like:
def persons = Enrollment.createCriteria().list{
user {
eq('name', "a")
}
}

Dynamic namedQueries

Is their a dynamic namedquery on grails? Im not sure if its the right term but, I mean a namedquery that can be true to all.
Something like:
namedQueries = {
dynamicQuery{ term, name, value ->
term(name, value)
}
}
Then it can be called maybe like but not exactly:
def testClass = TestClass.dynamicQuery('eq', 'lastname', 'Bill').list()
and so you call it too like:
def testClass = TestClass.dynamicQuery('gt', 'id', 12).list()
This one might not work but is their something similar in grails?
UPDATE
The idea is that so I can chained it as many as I want like:
def testClass = TestClass.dynamicQuery('gt', 'id', 12).dynamicQuery('eq', 'stat', 11).list()
This is so that I dont have to create many namedqueries. I was hoping I can create one and use it multiple times.
Grails' createCriteria method generates Grails HibernateCriteriaBuilder instance, within which you can call invokeMethod method to dynamically create query criteria, which usually is defined by the standard DSL.
Here is a example in some controller:
private String dynamicCriteriaTest(String term, name, value) {
def c = TestClass.createCriteria()
def param = []
param << name
param << value
def result = c.list{
c.invokeMethod(term, param as Object[])
}
return result.toString()
}
def test() {
render dynamicCriteriaTest('eq','lastname','Bill')
}
That will get something you want.
update
If you want to call this method multiple times, pass the criteria parameters in an a List then execute the query:
private List dynamicCriteriaTest(List param) {
def c = TestClass.createCriteria()
def paramList = param.collate(3) //split the parameters into groups
def result = c.list{
paramList.each { paramInstance ->
def command = paramInstance[0]
paramInstance.remove(0)
c.invokeMethod(command, paramInstance as Object[])
}
}
return result
}
def test() {
ArrayList param = new ArrayList()
//the 1st criteria
param << 'gt'
param << 'id'
param << (long)12 //you have to check the Grails [HibernateCriteriaBuilder] API to make sure the parameter passed to `invokeMethod` is in the right type (e.g. **long** in this case)
//the 2nd one
param << 'eq'
param << 'stat'
param << (long)11
//even more
param << 'like'
param << 'description'
param << 'some text%'
render dynamicCriteriaTest(param)
}
In Grails you have NamedQueries and also Where Queries. The example you give can possibly be implemented by using a namedqueries and placing this in a abstract domain class. Your domain classes should extend this abstract domain.

Groovy dynamically invoked class and find method doesn't work?

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?
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])
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

Resources