Search code examples
grailsgrails-orm

Grails hasMany associations not saving to join table


I have a Grails 4.0.3 application. I am simply starting to test some domain mappings. It seems i am not able to create one to many and many to many relationships. Now i am trying with a simple one to many relation.

Here is my implementation:

    class Author {

    String name
    static hasMany = [books : Book]
    static constraints = {
    }
}

class Book {

    String title

    static constraints = {
    }
}

I have bootstrapped :

def author = new Author('name':"Author")
        author.addToBooks(new Book('title':"Book1"))
        author.addToBooks(new Book('title':"Book2"))
        author.save()

I have author and books in table but not the relationship.

Following codes give empty list.

def author = Author.get(1)
def books = author.books

Not sure what i am missing. I have read lots of answers similar to this question and some suggested to use a separate join class. But i am upgrading my existing application and there are lots of places where addTo syntax is being used. So i want to stick with that. At least, i want to know why this is not working as this is the standard implementation.

I have also shown the generated join table structure in the image. It seems the structure of join table is also not quite right. My understanding is it should create a table of name author_books with keys author_id and book_id.

Author table

Books table

empty author_book table

table structure


Solution

  • You have not shown enough context to know for sure but I expect that the save is happening in a context that is problematic, maybe because the session isn't being flushed. One way to verify that is to flush the session on save.

    See the project at https://github.com/jeffbrown/prabinupretirelationship.

    https://github.com/jeffbrown/prabinupretirelationship/blob/2fcf133f65309e449b408f4152f3a36fbb053a3e/grails-app/domain/prabinupretirelationship/Author.groovy

    package prabinupretirelationship
    
    class Author {
    
        String name
        static hasMany = [books : Book]
        static constraints = {
        }
    }
    

    https://github.com/jeffbrown/prabinupretirelationship/blob/2fcf133f65309e449b408f4152f3a36fbb053a3e/grails-app/domain/prabinupretirelationship/Book.groovy

    package prabinupretirelationship
    
    class Book {
    
        String title
    
        static constraints = {
        }
    }
    

    https://github.com/jeffbrown/prabinupretirelationship/blob/2fcf133f65309e449b408f4152f3a36fbb053a3e/grails-app/init/prabinupretirelationship/BootStrap.groovy

    (NOTE: I wouldn't actually do this, but I am trying to use code close to the approach you asked about. A better idea would be to move the persistence logic into a GORM Data Service (http://gorm.grails.org/7.0.4/hibernate/manual/index.html#dataServices) where the transaction and session would all be managed by GORM).

    package prabinupretirelationship
    
    class BootStrap {
    
        def init = { servletContext ->
            Author.withTransaction {
                def author = new Author('name': "Author")
                author.addToBooks(new Book('title': "Book1"))
                author.addToBooks(new Book('title': "Book2"))
                author.save(flush: true)
            }
        }
        def destroy = {
        }
    }
    

    logSql is set to true at https://github.com/jeffbrown/prabinupretirelationship/blob/2fcf133f65309e449b408f4152f3a36fbb053a3e/grails-app/conf/application.yml#L106.

    When the app is run, the following SQL statements are sent to the database, including populating the join table:

    Hibernate: insert into author (id, version, name) values (null, ?, ?)
    Hibernate: insert into book (id, version, title) values (null, ?, ?)
    Hibernate: insert into book (id, version, title) values (null, ?, ?)
    Hibernate: insert into author_book (author_books_id, book_id) values (?, ?)
    Hibernate: insert into author_book (author_books_id, book_id) values (?, ?)