Search code examples
javagrailsgroovygrails-orm

Grails How to work with GORM when it comes to composition (To HasMany or to not)


So I have an object Person let's say, with the following fields (for simplicity)

class Person{
    String name
    List<Address> addressList
}

class Address{
    String streetNo
}

This would be the way to go usually, but now that im working with grails I thought it should be the following:

class Person{
    String name
    List addresses
    static hasMany = [addresses: Address]    
}

class Address{
    String streetNo
    static belongsTo = Person
}

I'm receiving the data from an HTTP call and im trying to save it:

    def persons = response.getAt("response").getAt("persons").collect()
    persons.forEach({ current ->
        def person = new Person(current)
        person.save()
    })

The parsing is working properly as when I check the person object before saving it, I can see the address being added correctly (at first NOT saved and without an ID but after the save, an ID is added)

Now when I want to fetch all persons, I do a basic

    respond Person.list()

I get all the details correct but the address I get only the address IDs so for example:

{
    id: 1,
    name: foo,
    addresses:[
    {id:1},{id:2}    
    ]
}

But what I want here is the actual address object not its id! Also I noticed that after retrieving the Person list a couple of times the addresses list gets empty and I end up with addresses:[]

I tried to remove the hasMany and go back to the list but it didn't work either, I went through the official documentation but nothing points out more than what I already tried and/or mentioned


Solution

  • There are several approaches to render your hasMany refs:

    1. turn lazy-loading for addresses off:

      class Person{
          String name
          List addresses
          static hasMany = [addresses: Address]    
          static mapping = {
              addresses lazy: false
          }
      }
      

      as you rather want the addresses to behave as embedded objects.

    2. You can resolve each address in a person by explicitly call it's props:

      def list = Person.list()
      list*.addresses*.streetNo
      respond list
      
    3. use Jackson-annotations

      @JsonAutoDetect(fieldVisibility = Visibility.PUBLIC_ONLY) // either for the class
      class Person{
          String name
      
          @JsonProperty // or for each prop
          List addresses
          static hasMany = [addresses: Address]    
      }
      

    UPDATE

    1. If you use mongo, you can use embedded to its full strength:

      class Person{
          String name
          List<Address> addresses    
          static embedded = [ 'addresses' ]
      }
      
      class Address{
          String streetNo
      }