Most of the stuff available online recommends using relationship domain class to fudge the many-to-many relationship. If I have a many to many relationship of User to Role, I could have a UserRoleRelation that has a one-to-many relationship with both a User and Role, which Grails should handle cleanly.
It is also pretty easy to set up a simple direct ManyToMany relationship manually, as follows:
In my Role.java class I have
@ManyToMany(targetEntity=User.class, mappedBy="roleCollection")
private Set userCollection;
In my User.java class I have
@ManyToMany(targetEntity=Role.class, cascade = {CascadeType.MERGE})
@JoinTable(name="USER_ROLE_XREF",
joinColumns=@JoinColumn(name="User_Name", referencedColumnName = "User_Name"),
inverseJoinColumns=@JoinColumn(name="Role_Name", referencedColumnName = "Role_Name")
)
private Set roleCollection;
I cross-refrence by name instead of id to make the table more human readable. This may have the unfortunate side effect of breaking the generate-views command.
In the user/edit.gsp, I add a select box with the list of roles available
<tr class="prop">
<td valign="top" class="name">
<label for="roleCollection">Roles:
</td>
<td valign="top" class="value ${hasErrors(bean:user,field:'roleCollection','errors')}">
<g:select name="newRoleCollection" from="${Role.list()}"
value="${user?.roleCollection?.id}" optionKey="id" multiple="multiple" />
</td>
</tr>
If you only want to present a subset of the Roles (which happens to be the case in my prototype app), you can use Role.findAllByXXX instead of Role.list()
Finally, in my controllers/UserController.groovy, I add the code to put humpty dumpty back together again.
user.roleCollection.clear()
//probably a groovier way to do this, but it works...
//if newRoleCollection is a string instead of a string[], each iterates over each character
boolean newRoleCollectionIsStringArray = params.newRoleCollection.class.name != 'java.lang.String'
if (newRoleCollectionIsStringArray) {
params.newRoleCollection.each{
user.roleCollection.add( Role.findById( Integer.valueOf( it ) ) )
}
} else {
user.roleCollection.add( Role.findById( Integer.valueOf( params.newRoleCollection ) ) )
}
It isn't pretty but it does the trick.
No comments:
Post a Comment