Grails auto update related table - grails

I have following domain classes:
class UserGroup {
String name
String description
static hasMany = [users:User]
}
class User {
String name
static hasMany = [userGroups:UserGroup]
static belongsTo = UserGroup
}
Here User and UserGroup have many to many relationaship.but when I save user to usergroup,the user is not updated with that usergroup.Is there a mapping I can use to do that?I am using following code to add users to group:
static def addUserToGroups(def user){
def userGroups = UserGroup.findAll()
userGroups.each {
if(/** condition **/){
it.addToUsers(user)
it.save(flush:true,failOnError:true)
}
}
}

Related

Grails HasMany List Relation to Extended Class having BelongsTo Relationship

I am having a problem setting up a List as a hasMany relationship to a belongsTo withing an extended class.
With a new Grails 2.4.4 app I have these 3 Domains:
An Author class having many books.
class Author {
List books
static hasMany = [books: Book]
}
A Book class which extends Content
class Book extends Content {
String foo
}
And Content, which has a number of common properties.
class Content {
String title
static belongsTo = [author: Author]
static constraints = {
sort: 'title'
}
}
When running this application I get the following exception:
Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: test.relationships.Book column: author_id (should be mapped with insert="false" update="false")
I only get this exception when I make books a List.
I think the problem is probably something to do with the fact you're saying that Author has many Books, but the superclass of Book, Content, belongs to an Authur.
What happens if you change your Content and Book classes to this:
class Content {
String title
static constraints = {
sort: 'title'
}
}
class Book extends Content {
static belongsTo = [author: Author]
String foo
}
or, alternatively, change your Author class to this:
class Author {
List books
static hasMany = [contents: Content]
}

How to establish domain class constraints on a hasMany relationship

I have a user class as a grails domain class. I would like to establish constraints for a String list that is declared in a hasMany relationship.
class User {
String name
static hasMany = [interests: String]
}
How could I specify an inList constraint for each interest?
you could use an Enum instead, so your inList Constraint will be handled automatically?
The solution could look like this:
class User {
String name
static hasMany = [interests: Interest]
}
Enum Interest {
FOO('foo'),
BAR('bar')
final String id
Interest(String id) { this.id = id }
}

how to get parents id while fetching child's information - Grails?

How to access firstname and lastname of user depending on which statusItem is displayed which is the child of the user.
class UserAccount implements Serializable {
static transients = ['pass','passConfirm','familyPicTmp', 'familyPicTmpFilename', 'photoTmp', 'photoTmpFilename']
static hasMany = [authorities: Authorisation, memberships:FamilyMembership, progenitorIwi:Family, politicItems:PoliticItem,
relationships:Relationship, , agents:UserAccount, media:UserMedia, status:Status]
static mappedBy = [ progenitorIwi:"progenitor",relationships:'relationTo', relationships:'userAccount']
static fetchMode = [memberships:FetchMode.JOIN, agents:FetchMode.JOIN]
static mapping = {
memberships(lazy:false)
agents(lazy:false)
}
static belongsTo = [Authorisation]
STATUS DOMAIN
class Status {
static belongsTo = [userAccount:UserAccount]
def String statusMessage
Date dateCreated
Date lastUpdated
def String statusType
POLITIC DOMAIN
class PoliticItem {
SystemEntity politicItemName
UserAccount userAccount
def String politicItemValue
def boolean shared = false
Date dateCreated
Date lastUpdated
How can we load all the users that belong to all the status on to the politic's views?
I am still not sure which part you dont know. Assuming userAccount has the firstName and lastName field to access them from status try this:
status is the instance of your Status class. The one you need to get the userAccount off of it.
status.userAccount.firstName
or
status.userAccount.lastName

GORM many-to-many mapping and jointable with an additional field

I am working with legacy database and have a many-to-many association with a join-table that I have resolved to a large extent as the mappings are working fine. But there is an additional column and in case of Book, Author model lets say that the nm_author_books contain a field called 'royalty'. Question is how do I access this field from any direction?
class Book {
String title
static belongsTo = Author
static hasMany = [authors: Author]
static mapping = { authors joinTable: [name: "mm_author_books", key: 'mm_book_id' ] }
}
class Author {
String name
static hasMany = [books: Book]
static mapping = { books joinTable: [name: "mm_author_books", key: 'mm_author_id'] }
}
If the nm_author_book table has [nm_book_id, nm_author_id, royalty] what is the way to access the royalty?
The basic idea is change from 1 many-to-many to 2 many-to-one: a Book has many BookAuthorDetail and a Author has many BookAuthorDetail
class Book {
String title
static hasMany = [details: BookAuthorDetail]
}
class Author {
String name
static hasMany = [details: BookAuthorDetail]
}
class BookAuthorDetail {
String royalty
static belongsTo = [book: Book, author: Author]
}
to access royalty by BookAuthorDetail, you can do: BookAuthorDetail.findAllByBookAndAuthor(bookInstance, authorInstance)
You could make a domain object that models that join table so instead of changing the mapping for Book and Author you have them point to the AuthorBookRoyalty domain.
Class AuthorBookRoyalty {
Author author
Book book
Long royalty
}
class Book {
String title
static belongsTo =Author
Static hasMany[authors: AuthorBookRoyalty]
}
Do similar for Author and you can now deal with the royalties. You may need to adjust the mapping on the join table to make it map to your current database.
Another option is to access these additional fields directly from the mapped entity of the join table:
class Book {
String title
static belongsTo = Author
static hasMany = [authors: Author]
static mapping = { authors joinTable: [name: "mm_author_books", key: 'mm_book_id' ] }
}
class Author {
String name
static hasMany = [books: Book]
static mapping = { books joinTable: [name: "mm_author_books", key: 'mm_author_id'] }
}
class AuthorBooks {
Author author
Book book
Long royalty
static mapping = {
table name: "mm_author_books"
author column: "mm_author_id"
book column: "mm_book_id"
}
}
You can access royalty with the following code, for instance:
AuthorBooks.findAllByBookAndAuthor(bookInstance, authorInstance)*.royalty

Dynamic finders with Grails many-to-many relationship

I have 2 domain classes which are mapped by many-to-many relationship. I followed the instruction of Grails documentation, but I still have some problem when processing data on those domains. Here are my 2 domain classes:
class User {
String name
int age
String job
static hasMany = [groups : Group]
static belongsTo = [org : Organization]
}
class Group {
String groupName
String code
static hasMany = [members : User]
}
My problems are:
1. The above relationship require one class hold belongsTo to be the "owner" of the relationship. In this context, the User belongs to the Group, but I do not know how to put the belongsTo to User class, because the standard syntax that Grails suggest is static belongsTo = [Group] (just specify the owner class name), so I cannot:
- put it into the exist belongsTo like this: static belongsTo = [org : Organization, Group]
- or define another belongsTo like this: static belongsTo = [Group]
Is below example right:
class Book {
String title
static belongsTo = Author
static hasMany = [authors:Author]
static mapping = {
authors joinTable:[name:"mm_author_books", key:'mm_book_id' ]
}
}
class Author {
String name
static hasMany = [books:Book]
static mapping = {
books joinTable:[name:"mm_author_books", key:'mm_author_id']
}
}
(Ref link: Many-to-Many link tables in grails (GORM) / hibernate)
I mean that do we need to specify the name of foreign key of the join table for each class?
If I want to find all User that are members of a specified Group whose name is "ABC", how can I use the DynamicFinder of Grails?
Thank you so much
It is very rare that m2m relationships have an owning side, so I've always found it odd to have to specify one for GORM to work correctly. Because of this, I don't do it this way. I create the join table as a domain. Then things get really simple.
class UserGroup implements Serializable {
User user
Group group
boolean equals(other) {
if (!(other instanceof UserGroup)) {
return false
}
other.user?.id == user?.id &&
other.group?.id == group?.id
}
int hashCode() {
def builder = new HashCodeBuilder()
if (user) builder.append(user.id)
if (group) builder.append(group.id)
builder.toHashCode()
}
static UserGroup get(long userId, long groupId) {
find 'from UserGroup where user.id=:userId and group.id=:groupId',
[userId: userId, groupId: groupId]
}
static UserGroup create(User user, Group group, boolean flush = false) {
new UserGroup(user: user, group: group).save(flush: flush, insert: true)
}
static boolean remove(User user, Group group, boolean flush = false) {
UserGroup instance = UserGroup.findByUserAndGroup(user, group)
instance ? instance.delete(flush: flush) : false
}
static void removeAll(User user) {
executeUpdate 'DELETE FROM UserGroup WHERE user=:user', [user: user]
}
static void removeAll(Group group) {
executeUpdate 'DELETE FROM UserGroup WHERE group=:group', [group: group]
}
static mapping = {
id composite: ['group', 'user']
version false
}
}
Then you just need to create the getters in your User and Group class. You won't have User user or Group group in either class. There is no need to map them with hasMany/belongsTo because all that will do is create the join table, which you've done by creating the UserGroup domain.
class User {
Set<Group> getGroups() {
UserGroup.findAllByUser(this).collect { it.group } as Set
}
}
class Group {
Set<User> getUsers() {
UserGroup.findAllByGroup(this).collect { it.user } as Set
}
}
Once you have these in place you can use the methods you created in the UserGroup domain and/or you can use finders on it...
def userGroupInstance = UserGroup.findByUserAndGroup(userInstance, groupInstance)
def userGroups = UserGroup.findAllByUser(userInstance)
def userGroupInstance = UserGroup.get(userId, groupId)
You get the idea. This presentation by Burt Beckwith sheds more light on why this is a good approach along with some other great tips for performance increases.
belongsTo will create another M:1 relationship (field) - you tell if there is a "main group" for a User in your case. If I wished only to enforce at least one group, I'd go with a custom validator for User.groups checking it's not empty.
(I'm not sure here) - I believe yes, if the key name differs from default Hibernat/GORM "userId"/"groupId".
findBy*() methods won't work here, you need a CriteriaBuilder, like here.

Resources