Luis C.
Luis C.

Reputation: 755

Grails: Try to save a object with nested objects. No error but no ID

This is my application: I have the user that is compsed by three elemtents

  1. User (Username, Password, Role, etc.. From SpringSecurity)
  2. AppUser (Base details like surname, etc)
  3. Detail User (Details specific by application. Working area, etc etc)

AppUser contains an instance of User and DetailUser (this lastone nullable)

This is my code:

 if (user.validate() &&  appUser.validate() ){
            user.save()
            appUser.user = user
            appUser.save()

            appUserDetail.appUser = appUser
            appUserDetail.save()

        }

No error validations, but the ID of my appUser is null!

Sincerely I don't understand why.

UPDATE

Hibernate.AssertionFailure an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)
org.hibernate.AssertionFailure: null id in it.geny.auth.AppUser entry (don't flush the Session after an exception occurs)

Upvotes: 1

Views: 1044

Answers (2)

Luis C.
Luis C.

Reputation: 755

Finally I found a solution.

Searching on SO about this issue I found many post about this. Some people said that the :

<Domain>.get(id)

maybe can close session, as consequence the object will not be saved. But where I call .get function?!?!?

Here:

def appUser = new AppUser(params)

Inside I have a one-to-one rel to many domanins (ex. Region region). Inside the GrailsParameterMap I had params."appUser.region" with the ID of the Region.

If I use the binder of grails (spring) (I cannot check the code, but if this true, I will find a .get(id) somewhere...), my object will not persisted.

But , if I bind manually , ALL IS OK, my object is saved.

Upvotes: 0

Ted Delezene
Ted Delezene

Reputation: 2481

First off, you won't get any error on a .save() without failOnError: true i.e.

user.save(failOnError: true)

It will just quietly not save and not really notify that it failed (you have to check to see if it contains errors if you want to see a failure).

Finally, that particular error comes up in a large number of events regarding saves where you have join tables being created.

The best I've seen is you want to make sure that both the parent and the child both have links back to each other. So, you want to do something like this:

 if (user.validate() &&  appUser.validate() ){
        user.appUser = appUser
        appUser.user = user
        appUserDetail.appUser = appUser
        appUser.UserDetail = appUserDetail

        appUserDetail.save(failOnError: true)
        appUser.save(failOnError: true)
        user.save(failOnError: true)

    }

I didn't test this code, you may have to fidget with it a little bit but the point is you want to make sure that each object is pointing to the other before saving.

The failOnError may not be necessary, and you may not want an exception to be thrown in the event that a save failed, in which case you can just call .save() and then check user.errors for the given errors. Typically I use the g:eachError tag in my gsp to display the errors to the end user so they can clean up their submission and try again.

Upvotes: 1

Related Questions