Grails继承和冲突的一对多和多对多关系

时间:2014-07-11 20:31:38

标签: grails inheritance database-design

我正在通过尝试创建一个简单的twitter副本来学习grails。我目前正在尝试加入粉丝和群组。我最初提出了一个非常基本的数据库结构,我没有运气实现它。关系设计如下:

    Person:
        has many: Groups, Tweets, (Person as followers through User2Person)
    Group:
        has many: (Person as followers through User2Person)
        belongs to: Person as owner
    User2Person:
        belongs to: (Person or Group)
        belongs to: Person

基本上,我希望Person和Group成为User的一个实例,然后创建一个将User映射到Person的表。这样,只有一个表被创建/用于Group2Person和Person2Person之间的关系。

更多信息:一个人是由一个人创建的,所以它应该有一个“所有者”(person_id)。它也有许多粉丝(即成员)。小组不能跟随其他小组,但是一个人可以跟随另一个人或小组。

以下是我在grails中实现的方法:

用户

    abstract class User {
        static hasMany = [followers: Person]
        static mappedBy = [followers: "followed"]
        String name
        Date dateCreated
        Date lastUpdated

        static constraints = {
            name shared: "mustFill", size: 3..20
        }
    }

    class Person extends User {
        static belongsTo = [followed: User]
        static hasMany = [tweets: Tweet, groups: Group]
        static mappedBy = [groups: "owner"]
        String username
        String email

        static constraints = {
            username shared: "mustFill", unique: true, size: 4..15
            email shared: "mustFill", email: true
        }

        static mapping = {
            tweets sort: 'dateCreated', order: 'desc'
        }

    }

    class Group extends User {
        Person owner
        String description

        def getTweets() {
            return followers.tweets.flatten()
        }

        static transients = {
            tweets
        }
    }

推文(以防万一?)

    class Tweet {
        static belongsTo = [author: Person]
        String text
        Date dateCreated

        static constraints = {
            text shared: "mustFill", maxSize: 140
        }
    }

当我运行cmd grails schema-export时,我收到以下错误:“|错误加载插件管理器错误:域类[class tweeter.Group]和[class tweeter.Person]不能在很多中互相拥有-to-many relationship。两者都包含相互引用的belongsTo定义。(使用--stacktrace查看完整的跟踪)“

1 个答案:

答案 0 :(得分:0)

我能够让数据库创建几乎正确的架构。不幸的是,使用了User2Person(a.k.a.粉丝)的连接表的主键(user_id,person_id)。这意味着我不能有两个记录,如:(1,2)和(2,1)(例如,两个用户互相跟随)。以下是更新的课程(commit):

用户

    class User {
        static belongsTo = Person
        static hasMany = [followers: Person]
        String name
        Date dateCreated
        Date lastUpdated

        static constraints = {
            name shared: "mustFill", size: 3..20
        }
    }

<强>人

    class Person extends User {
        static hasMany = [tweets: Tweet, groups: Group, follows: User]
        static mappedBy = [tweets: "author", groups: "owner"]
        String username
        String email

        static constraints = {
            username shared: "mustFill", unique: true, size: 4..15
            email shared: "mustFill", email: true
        }

        static mapping = {
            tweets sort: 'dateCreated', order: 'desc'
        }

    }

架构中的跟随者表格如下:

    create table user_follows (
        user_id bigint,
        follows__id bigint,
        primary_key(user_id, follows__id)
    )

我在网上搜索了有关更改连接表主键的信息。我能找到的最好的是使用如下代码:

    static mappedBy = { followers joinTable: [name:"someName", ...] }

不幸的是,我很难找到关于joinTable映射的好文档,大多数来源似乎都表明无法轻松更改连接表的主键。然后我决定使用一个单独的域类来定义本指南后面的连接表:没有Hibernate XML的多对多映射。以下是最终更新的代码(commit):

用户

    class User {
        static belongsTo = Person
        static hasMany = [people: UserFollower]
        static mappedBy = [people: "followed"]
        String name
        Date dateCreated
        Date lastUpdated

        static constraints = {
            name shared: "mustFill", size: 3..20
        }

        static transients = {
            followers
        }

        def getFollowers() {
            return people.collect { it.follower }
        }

        void addToFollowers(Person person) {
            UserFollower.link(this, person)
        }

        void removeFromFollowers(Person person) {
            UserFollower.unlink(this, person)
        }
    }

<强>人

    class Person extends User {
        static hasMany = [tweets: Tweet, groups: Group, users: UserFollower]
        static mappedBy = [tweets: "author", groups: "owner", users:"follower"]
        String username
        String email

        static constraints = {
            username shared: "mustFill", unique: true, size: 4..15
            email shared: "mustFill", email: true
        }

        static mapping = {
            tweets sort: 'dateCreated', order: 'desc'
        }

        static transients = {
            follows
        }

        def getFollows() {
            return users.collect { it.followed }
        }

        void addToFollows(User user) {
            UserFollower.link(user, this)
        }

        void removeFromFollows(User user) {
            UserFollower.unlink(user, this)
        }

    }

<强> UserFollower

    class UserFollower {
        User followed
        Person follower

        static constraints = {
            followed nullable: false
            follower nullable: false
        }

        static void link(User user, Person person) {
            UserFollower f = UserFollower.findByFollowedAndFollower(user, person)
            if(!f) {
                f = new UserFollower()
                user.addToPeople(f)
                person.addToUsers(f)
                f.save()
            }
        }

        static void unlink(User user, Person person) {
            UserFollower f = UserFollower.findByFollowedAndFollower(user, person)
            if(f) {
                f = new UserFollower()
                user.removeFromPeople(f)
                person.removeFromUsers(f)
                f.delete()
            }
        }
    }