在Grails中,同一领域类有多个hasMany关系

8

我正在使用Grails,并且我有一个领域模型,其中有多个hasMany属性指向相同的领域类,如下所示:

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

我遇到的问题是,当我在帖子列表中添加某些内容时,它也不知怎么就进了“喜欢”和“不喜欢”的列表里。至少,当我遍历这些列表时是这样看的。
我认为问题在于我的Post域中也有以下关系:
static belongsTo = [ contributer : Contributer ]

什么是最好的配置这些关系使我的模型工作?有什么建议吗?
@Wayne,
我也尝试了你的测试,它成功通过了。所以,我唯一能想到的就是我的PostController中的save方法出了问题。下面是相关代码(我使用了Spring Security Core插件,并且我的Contributer类扩展了该插件创建的User类):
@Secured(['IS_AUTHENTICATED_FULLY'])
def save = {
def props = [title:params.title, post:params.post,   category:Category.get(params.category.id)]

def user = Contributer.get(springSecurityService.principal.id)
def postInstance = new Post(props)

postInstance.contributer = user
if (postInstance.save(flush: true)) {
  flash.message = "${message(code: 'default.created.message', args: [message(code: 'post.label', default: 'Post'), postInstance.id])}"
  redirect(action: "show", id: postInstance.id)
}
else {
  render(view: "create", model: [postInstance: postInstance])
}
}

这里有什么值得注意的吗?


当您向贡献者添加帖子时,您是使用'myPost.contributer=myContributer'还是'myContributer.addToPosts(myPost)'? - fixitagain
@fixitagain,谢谢回复。我已尝试了两种方法,结果相同。 - NickForrer
6个回答

12
问题在于Post和Contributor之间存在一对多的关系(Post有一个作者,作者有多篇文章),以及Post和Contributor之间存在两个多对多的关系(Post有很多点赞者,点赞者点赞了很多文章)(Post有很多踩的人,这些人踩了很多文章)。Post中的belongsTo解释了这种行为,但是删除它不会解决问题,只会产生不同的问题。最终结果是GORM约定将无法满足,所以您必须告诉GORM如何行事或以不同的方式建模。
有几个选项可供选择,但我首先想到的是将投票(Vote)单独建模并使Contributor具有hasMany likeVotes和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将其建模为一个投票表,使用鉴别器列来区分“赞”和“踩”,这样可以消除“赞”、“踩”和作者帖子之间的冲突。

然后在Contributor中:

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

现在这些关联已经清晰了:

  1. 文章拥有多个点赞投票(likeVotes)
  2. 文章拥有多个踩投票(dislikeVotes)
  3. 贡献者拥有多个点赞投票(likeVotes)
  4. 贡献者拥有多个踩投票(dislikeVotes)
  5. 文章拥有一个贡献者
  6. 贡献者拥有多篇文章

GORM可以理解这些关系并会根据情况表现出不同的行为。

如果你不喜欢这种选项,下一步将是为你的数据库结构指定自定义映射,然后使用mappedBy来区分不同的关系。如果你绝对想要让贡献者以三种不同的方式直接与文章相关联,那么这就是要采取的方法。


谢谢你的回答!我认为这解决了很多关于GORM的困惑。非常有启发性! - NickForrer
类型不再相同,但它们应该是相同的。这可能会产生不良的副作用。 - tmanthey

5

在你的领域类中使用 static mappedBy

例如:

在许多关联方面的领域对象中 (Contributer.groovy):

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

在一个域对象(Post.groovy)中:
Class Post {

       static belongsTo = [ contributer : Contributer ]

       Contributer postsContributer
       Contributer likesContributer
       Contributer dislikesContributer

   ... 
}

4

1
你能展示一下失败的测试用例吗?我将我认为是你的情况放入了一个Grails 1.3.7项目中,测试通过了:
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)

}


1
尝试切换到多对多关系并定义一个映射域类。在这个映射域类中,您可以指定关系的类型;例如喜欢、不喜欢或作者。
class Contributor {
    static hasMany = [contributorPosts:ContributorPost]
}

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

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

您可以查看http://www.grails.org/Many-to-Many+Mapping+without+Hibernate+XML,以获取有关多对多映射域类的更多信息。


1
这应该可以工作:

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']
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接