Multiple hasMany relationships to same domain class in Grails

做~自己de王妃 提交于 2019-11-30 07:26:18

The problem is that you have a one to many between Post and Contributor (post has an author, author has many posts) as well as two many to many relationships between Post and Contributor (post has many likers, likers like many posts) (post has many dislikers, dislikers dislike many posts). The belongsTo in Post does explain the behavior, but removing it will not fix the problem, just create different ones. The end result is that GORM conventions are going to fall short so you have to tell GORM how to behave or model things differntly.

There are several options, but the one that jumps to mind is to model Vote separately from Post and make it so that a Contributor hasMany likeVotes and hasMany dislikeVotes

class Vote {

   // for illustration here, you need to think about the 
   // cascading behavior that makes sense and model it if you decide 
   // to go this route. 
  belongsTo = [post, contributor] 

}

class LikeVote extends Vote {
}

class DislikeVote extends Vote {
}

GORM will model this as one vote table with a discriminator column to separate likes and dislikes; this will let you eliminate the conflicts between likes, dislikes, and authored posts.

Then in Contributor

 hasMany = [likes:LikeVote, dislikes:DislikeVote, posts:Post]

The relationships are cleared up now:

  1. Post has many likeVotes
  2. Post has many dislikeVotes
  3. Contributor has many likeVotes
  4. Contributor has many dislikeVotes
  5. Post has one contributor
  6. Contributor has many posts

GORM can understand these relationships and will behave appropriately.

If you don't like this option, the next step would be to specify custom mappings for your database structure and then use mappedBy to differentiate the various relationships. This is the approach to take if you absolutely want to have a Contributor relate directly to Post in three different ways.

Use static mappedBy in your domain class

For example:

In many side domain object (Contributer.groovy):

static hasMany = [ posts : Post, likes : Post, dislikes : Post ]
static mappedBy = [posts: "postsContributer", likes: "likesContributer", dislikes: "dislikesContributer"]

In one side domain object (Post.groovy):

Class Post {

       static belongsTo = [ contributer : Contributer ]

       Contributer postsContributer
       Contributer likesContributer
       Contributer dislikesContributer

   ... 
}
Victor Sergienko

Though, standard way for multi-M:M is to use joinTable, as recommended in GRAILS-4884.

Can you show the test case that fails? I put what I think is your case into a grails 1.3.7 project, and the test passes:

class Post {
    String text ="postal"
    static belongsTo = [ contributor : Contributor ]
    static constraints = { }
}
class Contributor {
    String name = "Big C"
    static hasMany = [ posts : Post, likes : Post, dislikes : Post ]
    static constraints = { }
}

// integration test
void testMultipleRel() {
    Contributor c = new Contributor().save()
    assertNotNull c

    Post p1 = new Post(text:"neutral")
    Post p2 = new Post(text:"like")
    Post p3 = new Post(text:"dislike")
    [p1,p2,p3].each {c.addToPosts(it).save()}
    assertNotNull p1
    assertNotNull p2
    assertNotNull p3

    assertNull c.likes
    assertNull c.dislikes

    c.addToLikes(p2)
    c.addToDislikes(p3)

    assertEquals ([p1, p2, p3] as Set, c.posts as Set)
    assertEquals ([p2]         as Set, c.likes as Set)
    assertEquals ([p3]         as Set, c.dislikes as Set)

}

Try switching to a many-to-many relationship and define a mapping domain class. In this mapping domain class you can then specify the type of relationship; like, dislike, or author.

class Contributor {
    static hasMany = [contributorPosts:ContributorPost]
}

class ContributorPost {
    Post post
    Contributor contributor
    Boolean like
    Boolean dislike
    Boolean author
}

class Post {
    static hasMany = [contributorPosts:ContributorPost]
}

You can look here http://www.grails.org/Many-to-Many+Mapping+without+Hibernate+XML for further information on a many-to-many mapping domain class.

This should works:

static hasMany = [ posts : Post, likes : Post, dislikes : Post ]

static mapping = {
    posts joinTable: [name: 'contributor_posts']
    likes joinTable: [name: 'contributor_likes']
    dislikes joinTable: [name: 'contributor_dislikes']
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!