Grails: Update One-to-Many Collection
One of the things you frequently have to do is update the many side of a one-to-many relationship. Since Grails of course uses Hibernate the many side is a collection (a Set unless you change it) so when this relationship is updated you need to add new elements to the collection and then remove any element that should no longer be in the collection. Here I am going to show you an easy way to accomplish this. Note, that I will be showing this with Grails domain objects but the same thing will work just fine if you are using Hibernate directly.
In our example we will use an imaginary use case where we want to keep track of clubs and members of the clubs. So we will have the domain objects Club, ClubMember, and Person. There is a one-to-many relationship between Club and ClubMember and a one-to-one relationship between ClubMember and a Person.
I have included the three domain objects below. Notice that I have also included a equals() and hashcode() method. The equals() method is very important to what we are trying to accomplish. Note, that I wrote the equals() method with bad style to save space, I wouldn’t recommend that someone writes code like that.
Let’s just assume a user is editing the members of a currently existing Club and that user has submitted a form that contains a multiple select box that contains a list of all the names of people that should now be in the Club. Some of the names might already be in the Club, some names might have been removed, and some of the names might be new members. What is the easiest way to handle this situation? The answer is to take advantage of Collection bulk operations and let the JDK handle it for you. Once you have the right objects in the collection Hibernate will take care of the rest. By default hibernate uses Sets to map collections which are well suited to this; however, these methods can be used on Lists as well (they are part of the Collection interface, not the Set interface). My example is shown as if it was a method in a Service and the id and submitted names have already been pulled from the form parameters by the controller and passed to this method:
If you had logSql set to true for the datasource in the Grails Datasource.groovy config file you would see that Hibernate does inserts for any new Person in the club and does deletes for any Person no longer in the Club. Remember that this functionality depends on having a proper implementation of equals() for all the domain objects (something that is a Hibernate best practice anyway).