6.4.2 一对多关系

我们首先需要考虑的是如何使用 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 ""