Patrick McDaniel
Patrick McDaniel

Reputation: 1083

How do I properly update a one to many association using GORM events

I'm trying to update a one to many association in Grails using the GORM events beforeInsert and beforeUpdate. Currently the beforeInsert seems to be working, but beforeUpdate always throws a TransientObjectException on the hibernate flush.

The domains are mapped in the following way (an example, but hopefully enough to get the idea):

class Part {

   String xml

   static hasMany = [screws: Screws]

   static mapping = {
      screws cascade: "all-delete-orphan"
   }
}

class Screw {
   static belongsTo = Part
}

My update and insert essentially do the same thing and this method gets called from both beforeInsert and beforeUpdate (again a representative example):

private void augmentPartWithScrews() {
   screws?.clear()
   Set<Screw> parsedScrews = service.parseXmlAndReturnSetofScrews(xml)
   parsedScrews.each {
      addToScrews(it)
   }
}

What I am seeing is that when I create a new Part it works, but when I update Part such that screws are deleted I get a TransientObjectException in Hibernate's ForeignKeys class. I did some debugging and found that when creating a new instance of Part the objects come into the function getEntityIdentifierIfNotUnsaved method as saved instances, but on update they come in as unsaved instances and thus the exception.

This is Grails 2.3.7 and Hibernate plugin 3.6.10.10

Upvotes: 0

Views: 654

Answers (1)

Aaron
Aaron

Reputation: 546

We've found that trying to do inserts or deletes in the beforeInsert or beforeUpdate methods can be a real pain. The withNewSession is almost always essential since you are effectively trying to modify the session while it's being flushed, causing untold confusion to Hibernate. However, when using withNewSession, the screws has already been loaded in a different session and will therefore be detached in this new session.

One think you might be able to do in this case is move the logic into a beforeValidate method which is not tied into the session flushing behavior. There's a ticket open to add afterValidate which is more along the lines of what you want (it's not there yet, though).

However, in this case, it might be best to just reverse the polarity and have a service update/save the new Parts and parse and add the screws instead of trying to put the logic into the domain object.

Upvotes: 1

Related Questions