I know from the Grails/GORM documentation that if you wish to store a Map of objects, both the key and value need to be Strings: Map<String,String>
.
I'd like to use another domain object (Animal
) as the key within this map, but due to the constraint above, that's not possible. The identifier of Animal
can be easily converted to a string type, but if I were to do that, I don't believe GORM would be smart enough to perform the mapping when retrieving the parent object.
Has anyone run into this?
I think this should work:
Assuming you have a domainclass that has a map:
class Test {
String name
Map myAnimals=[:]
//When given a specific key it will return actual animal object
def findAnimal(String myKey) {
return myAnimals.find{it.key==myKey}?.value
//What above is doing can be seen broken down here:
//def ani = myAnimals.find{it.key==myKey}
//println "ANI::: ${ani} ${ani.value}"
//def animal = ani?.value
//return animal
}
}
In a service when saving to map
class TestService {
def save(values) {
Test test1 = new Test()
test1.name='something'
// so to add 3 of animal objects above values would be
def animal1=Animal.find()
String key1='ANI' //For animals
def animal2=Monkey.find() // where Monkey extends Animal (hence the keys)
String key2='MON' //For monkeys only
test1.myAnimals[key1]=animal1
test1.myAnimals[key2]=animal2
test1.save()
/**
* When you have a real values map that contains useful method you can
* do it this way - left commented out FYI on manual process above
Test test = new Test()
// you now have some map of values coming in that contains a key
// and actual object so you could make this up if you are testing
values.animals?.each{k,v->
test.myAnimals[k]=v
}
}
test.save()
*/
}
So the first example the values.each is where you have built your own map that contains a key and actual domain object being saved.
The second test1 example is where I did it manually without having automated values to pass in as a test and possibly best starting point.
Then when you have test object to get actual animal (as you can see limited to ) key so one animal one monkey one bird etc
When you have
Test test = Test.findByName('something)
def animal = test.findAnimal('ANI')
def monkey = test.findAnimal('MON')
println "animal is ${animal} ${animal.getClass()} monkey is ${monkey} ${monkey.getClass()}"
This now will look up the domain class method and attempt to return animal object as per call
Before launching this I will need 1 animal and 1 Monkey added since it gets or does a find or first object above. So in my bootstrap:
import test.Animal
import test.Monkey
class BootStrap {
def init = { servletContext ->
Animal animal = new Animal()
animal.name='Daffy Duck'
animal.save(flush:true)
Monkey monkey = new Monkey()
monkey.name='King Kong'
monkey.save(flush:true)
}
def destroy = {
}
}
When I run it I get from that println
:
animal is class test.Animal class java.lang.Class monkey is class test.Monkey class java.lang.Class
Also getting some class to string error unsure which bit is causing it - the println from the output looks as you desire.
You may have to choose a different method to hold the keys and use Animal as the main class to query so some other code for the key then you can change the findByAnimal to always return Animal
object:
//When given a specific key it will return actual animal object
Animal findAnimal(String myKey) {
Animal animal = myAnimals.find{it.key==myKey}?.value
return animal
}
Higher call got changed due to it being Animal
or Monkey
which extended Animal. Two missing classes from above example:
package test
class Animal {
String name
}
package test
class Monkey extends Animal {
}