How do I clear and replace a collection in a one-t

2019-03-25 06:10发布

问题:

This question is in two parts, first part is about clearing a list and second part is about assigning an owner to an object.

I have a one-to-many relationship between two domain objects in my model in Grails. The relationship looks like this...

class Person {

    static hasMany = [authorities: Role, locations: Location]
}

class Location {

    static belongsTo = Person

}

In my app the locations list on Person gets completely refreshed and replaced with a new list on a user action. What's more I get the list of Location objects independent of the associated Person. I resolve which person to apply them to by retrieving the currently logged in user, which I call activePerson and is fine for my purposes.

What I want to do is delete all the existing locations on activePerson and insert all the new ones, associating them with the activePerson as I go. I have a bunch of properties on Person which I don't want to persist at this point, so I want to avoid saving the whole parent object just because locations children have changed.

I thought of iterating throught the activePerson.locations list and deleting them one by one and relying on GORM/Hibernate to batch the queries together and flush at the end. This seems like brute force, although it might work. I was expecting there to be a clearLocations method or something similar, but I can't find one.

If I do just replace the activePerson.locations with a new list and call activePerson.save(flush:true), will GORM handle the delete of the existing rows?

Secondly I want to put the new Location objects onto activePerson. Again I could populate the activePerson.locations and save the whole thing, but I would like to avoid that if I can. I can save them individually, but how do I set belongsTo on each Location object as I go?

So to recap:

  1. How do I clear a list at the many end of a one-to-many collection?
  2. How do I associate independent objects with a parent object and persist them individually?

Thanks

回答1:

Clearing Collection approach didn't worked for me:

activePerson.locations.clear()

Just try iterate over collection with avoiding ConcurrentModificationException by calling collect():

activePerson.locations.collect().each {
    item.removeFromLocations(it)
}

And after that save the entity to execute SQL statement:

activePerson.save flush:true

Link to article to read more: http://spring.io/blog/2010/07/02/gorm-gotchas-part-2/



回答2:

For #1 you can clear the collection (it's a Set by default):

activePerson.locations.clear()

For #2 use addToLocations:

activePerson.addToLocations(location)

and when you save the person the location<->person relationship will be updated.



回答3:

While this is an older question, I ran into a very similar situation in which I wanted to update a set of child records. Here's how I chose to resolve it. For simplicity, I'll use the same object/relation names as the asker of the question.

  1. In the mapping block of the parent domain, add:

    static mapping = {
        locations(cascade: "all-delete-orphan")
    }
    
  2. In the child domain, add the @EqualsAndHashCode annotation (just in case there's another one lurking about, I'm referring to groovy.transform.EqualsAndHashCode).

  3. Add all of the children elements to the parent domain using the Grails addTo methods (e.g. addToLocations) and then save the parent domain.

While this approach does require saving the parent domain (which the asker didn't want to do), it seems like it's the most appropriate approach given the clear belongsTo definition in the model. I would therefore answer the asker's numbered questions like this:

  1. Rather than doing it manually, let Grails/Hibernate clear the records by specifying the all-delete-oprhan cascade behavior on the relation.

  2. Don't attempt to save the child records directly, but instead save the parent object to match what Grails seems to expect.



标签: grails gorm