我们首先需要考虑的是如何使用PostComment这两个结构来构建一对多关系:

type Post struct {    
    Id     int    
    Content string    
    Author string  
    Comments []Comment
}

type Comment struct {    
    Id     int    
    Content string    
    Author  string    
    Post    *Post
}

注意,Post结构新增了一个Comments字段,这个字段是一个由任意多个Comment结构组成的切片;与此同时,Comment结构也新增了一个Post字段,这个字段是一个指向Post结构的指针。初看上去,程序似乎会把多个Comment结构存储到一个Post结构里面,然后让Comment结构通过指针引用Post结构。但是实际上,因为切片也是一个指向数组的指针,所以Post结构和Comment结构在构建关系时使用的都是指针:这种做法可以确保程序获取到的都是同一个Post结构或者Comment结构,而不是这些结构的副本。

在设定好Post结构和Comment结构之间的关系之后,我们接下来要考虑的就是如何实际地构建这些关系。正如前面所说,一对多关系实际上就是多对一关系,所以这两个结构在定义一对多关系的同时,也定义了多对一关系。当程序创建一条新评论的时候,它就会在评论和被评论的帖子之间建立起以上提到的这两种关系:

comment := Comment{Content: "Good post!", Author: "Joe", Post: &post}
comment.Create()

跟之前的做法一样,程序首先会创建一个Comment结构,然后通过调用该结构的Create方法来创建评论,并藉此建立起评论与帖子之间的关系。代码清单6-15展示了Comment结构的Create方法的具体定义。

代码清单6-15 创建评论,并建立评论与帖子之间的关系

func (comment *Comment) Create() (err error) {//①创建一条评论   
    if comment.Post == nil {
        err = errors.New("Post not found")        
        return    
    }    
    err = Db.QueryRow("insert into comments (content, author, post_id) values ($1, $2, $3) returning id", 
        comment.Content, comment.Author, comment.Post.Id).Scan(&comment.Id)    
    return
}

在为评论和帖子建立关系之前,Create方法会先检查给定的帖子是否存在,并在帖子不存在时返回一个错误。除了“通过post_id建立关系”这一细节没有提及之外,Create方法的其余代码的行为跟我们之前描述的一模一样。

在建立起评论和帖子之间的关系之后,我们接下来要考虑的就是如何修改GetPost函数,让它可以在获取帖子的同时,一并获取与帖子相关联的评论。比如说,程序在执行完以下代码之后,应该可以通过访问readPost变量的Comments字段来查看帖子已有的评论:

readPost, _ := GetPost(post.Id)

代码清单6-16展示了修改之后的GetPost函数的定义。

代码清单6-16 获取帖子及其评论

func GetPost(id int) (post Post, err error) {    
    post = Post{}      
    post.Comments = []Comment{}    

    err = Db.QueryRow("select id, content, author from posts where id = $1", id)
        .Scan(&post.Id, &post.Content, &post.Author)    

    rows, err := Db.Query("select id, content, author from comments where post_id = $1", id)
    if err != nil {        
        return    
    }

    for rows.Next() {        
        comment := Comment{Post: &post}        
        err = rows.Scan(&comment.Id, &comment.Content, &comment.Author)        
        if err != nil {            
            return       
        }        
        post.Comments = append(post.Comments, comment)    
    }    
    rows.Close()    

    return
}

GetPost函数首先会初始化Post结构中的Comments字段,并从数据库里面获取帖子的具体数据。在此之后,程序会从数据库里面获取与当前帖子关联的所有评论,接着迭代这些评论,为每个评论都创建一个Comment结构并将其追加到Comments切片里面。当所有评论都被迭代完毕之后,GetPost函数就会将包含了评论的Post结构返回给调用者。正如上述内容所示,在多个表之间建立关系并不困难,但是这一行为在Web应用变得越来越庞大的同时就会变得越来越麻烦。为了解决这个问题,我们将在接下来的一节中学习如何通过关系映射器来简化关系的构建方法。

虽然本节展示了所有数据库应用都会用到的CRUD操作,但这些操作充其量只是使用Go访问SQL数据库的基本知识,如果你有兴趣了解更多相关的知识,那么可以去读一下Go的官方文档。

results matching ""

    No results matching ""