Search code examples
grailsgrails-ormtransient

GORM: Save transient object before flushing


I'm integrating the SpringSecurity plugin with an application. To do so, I installed the plugin and ran the s2-quickstart script, which created three domain classes: SecUser, SecRole, and SecUserSecRole as well as the login/logout controllers.

As I already have an existing User domain class and wanted to keep it, I modified User to extend SecUser and removed the overlapping fields of username and password from User. Also, I've added tablePerHierarchy false to both User and SecUser mapping sections.

In BootStrap, I'm trying to create an Admin user to exist on startup. To do so, I create a new SecRole object adminRole with the authority set to ROLE_ADMIN. Then, I create a new User object and check if it contains the admin role via

def springSecurityService
def init {
    def adminUser = User.findByUsername('admin') ?: new User(
        username:'admin',
        password:'adminUser',
        userType:'Admin',
        enabled:true    
    ).save(failOnError:true)

    if (!adminUser.authorities.contains(adminRole))
        SecUserSecRole.create adminUser, adminRole, true

}

My issue is I keep getting a transient instance exception, regardless if adminUser is declared to be a User or SecUser instance. As userType has a constraint of blank:false, if I comment out the userType field when creating a User instance, I get an excception stating that null is not a valid setting for userType. When I include it, I get the transient object exception: object references an unsaved transient instance - save the transient instance before flushing: books4africa.SecUser, so I know I'm passing all User validation settings.

What causes a valid User object to not be saved in this circumstance?

EDIT

The solution for this issue was to delete the existing dev database file and reinitialize the application. My guess is that there was some sort of conflict between the db and Spring Security with the original db, so restarting the app with a fresh db solved the problem.

Since none of the answers below solved the exact problem, I won't be accepting in this instance, but thanks for the help!


Solution

  • To me it seems like a problem with the domains or the way you're creating instances of them.

    You said:

    I modified User to extend SecUser and removed the overlapping fields of username and password from User. Also, I've added tablePerHierarchy false to both User and SecUser mapping sections.

    But maybe you didn't tell the plugin that you did that. Please, have a look a the documentation regarding the domain classes. To use your own domains you must configure the SpringSecurity plugin (the doc has all info you need). So, for example, if you want to use your own user class. you put something like this on your config:

    grails.plugins.springsecurity.userLookup.userDomainClassName = 'your.package.NameOfYourClass'
    

    And that will tell the plugin that the class it must use for users is NameOfYourClass.

    It is important you use the default classes, that the quickstart generated, as an example, is the easiest way of properly using custom Users and Roles classes (just copy every property to your own classes, and rename as you see fit).

    And take also a look at the tutorials, to see how to properly save users and roles.