Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Grails Gorm : Object references an unsaved transient instance

I get the following Exception when saving an instance of Trip in Grails:

2011-01-26 22:37:42,801 [http-8090-5] ERROR errors.GrailsExceptionResolver - object references an unsaved transient instance - save the transient instance before flushing: Rower org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: Rower

The concept is simple: For a boattrip you need some rowers, a coxwain (is also a rower) and a boat:

Trip looks like (shortened):

class Trip {
    Boat boat;
    Rower coxwain;

    static belongsTo = [Rower,Boat]
    static hasMany = [rowers:Rower]
}

and Rower (shortened)

class Rower { 
    String firstname;
    String name;
    Rower reference;

    static hasMany = [trips:Trip];
    static mappedBy = [trips:"rowers"]
}

The trip then is saved in the controller like:

def save = {
        def trip = new Trip(params)

        // adding Rowers to Trip
        if (params.rower instanceof String) {
            def r = Rower.get(params?.rower)

            if (r != null) {
                trip.addToRowers(r)
            }
        } else {
            params?.rower?.each{
                rowerid ->
                def r = Rower.get(rowerid)
                log.info("rowerid (asList): " + rowerid)
                if (r != null) {
                    trip.addToRowers(r)
                }
            }
        } 

        // saving the new Trip -> EXCEPTION IN NEXT LINE
        if(!trip.hasErrors() && trip.save(flush:true)) {
          // ...
        }
        // ...
}

I think I have set the relations between the domains correct. The Rower is not changed while it is added to the Trip. Why does Grails want it to save? why is it a transient instance?

like image 789
Daniel Avatar asked Jan 26 '11 22:01

Daniel


2 Answers

Unfortunately this is an issue with the way GORM handles things, or more specifically the way that it expects that you deal with transients. If you don't persist the contained classes to the database first (Rowers in this case), you will get this exception every single time.

With GORM you have to save and attach in a bottom up fashion or when grails goes to flush the connection for the next instance you will get the transient instance exception. The instance is 'transient' because its just an in-memory reference. To persist the parent, GORM needs to link the parent to the child in the database. Without the child being persisted it has no way to do that, this is where the exception is coming from.

Wish there was better news. Not that its hard, but it gets annoying with complex hierarchies.

like image 188
AfroRick Avatar answered Nov 06 '22 07:11

AfroRick


The Problem was somehow different. it's in here:

def trip = new Trip(params)

which references a coxwain (of class Rower), which is not set (id=-1 is returned). This constructs a new Rower instead of a 'null' value. And this is the 'unsaved transient instance'. If I check first for a valid instance, then it works :-)

Thanks for the help!

like image 43
Daniel Avatar answered Nov 06 '22 08:11

Daniel