Search code examples
grailsgrails-orm

Strange Grails behaviour populating List at bootstrap


I'm becoming crazy on a strange Grails behaviour.

I get a domain class user :

class User {

    String firstName
    String token
    List roles = [] as List

    static constraints = {
    }
}

I modify my BootStrap.groovy like this to populate 5 user instance :

class BootStrap {

    def init = { servletContext ->
        switch (Environment.getCurrent()) {
            case 'DEVELOPMENT':
                def user1 = new User(
                    id: 1,
                    firstName: 'Enricot',
                    token: 'L\'abricot'
                )
                user1.roles.add('ROLE_USER')
                user1.save(failOnError: true)
                def user2 = new User(
                    id: 2,
                    firstName: 'Arnaud',
                    token: 'Dauphin')
                user2.roles.add('ROLE_USER')
                user2.roles.add('PERM_WRITE')
                user2.save(failOnError: true)
                def user3 = new User(
                    id: 3,
                    firstName: 'Magalie',
                    token: 'La banane')
                user3.roles.add('ROLE_USER')
                user3.roles.add('PERM_READ')
                user3.save(failOnError: true)
                def user4 = new User(
                    id: 4,
                    firstName: 'Jeremy',
                    token: 'Wistiti')
                user4.roles.add('ROLE_USER')
                user4.roles.add('ROLE_ADMIN')
                user4.save(failOnError: true)
                def user5 = new User(
                    id: 5,
                    firstName: 'Jimini',
                    token: 'Criquet')
                user5.roles.add('ROLE_USER')
                user5.roles.add('ROLE_INTERACTIONS')
                user5.save(failOnError: true)
                break
            case "test":
                DataBuilder.init()
                break
        }
    }
    def destroy = {
    }
}

In my controller, when I'm getting one user, all fields are corect, except the List.

If I do :

def user = User.get(1)
println user.firstName // Output is "Enricot"
println user.token // Output is "L'abricot"
println user.roles // Output is "[]" and not "[ROLE_USER]"

Trying populate object like this did not solve my problem :

def user1 = new User(
        id: 1,
        firstName: 'Enricot',
        token: 'L\'abricot',
        roles: ['ROLE_USER']).save(failOnError: true)

I'have try with double quote, simple quote.

The most funny part is when I'm making "user.roles" INTO BootStrap.groovy, I get the correct List.

It's making no sense for me, any help is welcome,


Solution

  • It's not strange behavior, you're doing it wrong :)

    When you add a field that you want to be persistent, you need to give Grails information about how to store it. Just a simple List isn't enough information. If it's a list of other domain classes, it will create a foreign key and store the ids. If it's a list of Strings, it will store them as varchars. As shown in the documentation, you need to do something like this:

    class User {
       String firstName
       String token
       static hasMany = [roles: String]
    }
    

    This changes the backing collection to a Set, but this is likely correct for roles since you don't care about ordering and want uniqueness. If you do need ordering, add an uninitialized List field to signal to Grails that you don't want a Set:

    class User {
       String firstName
       String token
       List roles
       static hasMany = [roles: String]
    }
    

    This changes how you populate the data. Instead of directly adding to the collection, use the dynamic addToRoles method that gets added for you:

    class BootStrap {
       def init = { ctx ->
          environments {
             development {
                def user1 = new User(firstName: 'Enricot', token: 'L\'abricot')
                user1.addToRoles('ROLE_USER')
                user1.save(failOnError: true)
                def user2 = new User(firstName: 'Arnaud', token: 'Dauphin')
                user2.addToRoles('ROLE_USER')
                user2.addToRoles('PERM_WRITE')
                user2.save(failOnError: true)
                def user3 = new User(firstName: 'Magalie', token: 'La banane')
                user3.addToRoles('ROLE_USER')
                user3.addToRoles('PERM_READ')
                user3.save(failOnError: true)
                def user4 = new User(firstName: 'Jeremy', token: 'Wistiti')
                user4.addToRoles('ROLE_USER')
                user4.addToRoles('ROLE_ADMIN')
                user4.save(failOnError: true)
                def user5 = new User(firstName: 'Jimini', token: 'Criquet')
                user5.addToRoles('ROLE_USER')
                user5.addToRoles('ROLE_INTERACTIONS')
                user5.save(failOnError: true)
             }
             test {
                DataBuilder.init()
             }
          }
       }
    }
    

    See http://grails.org/doc/latest/guide/conf.html#environments for information about the environment block support in BootStrap.groovy.

    Also note that I removed your explicit id values since you are not using assigned ids, so those values are ignored.

    Finally, it looks like you're rolling your own security. Don't do this. Use a proven framework. Both Spring Security Core and Shiro are powerful and easy to get started with.