前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go 数据存储篇(六):数据表之间的关联关系和关联查询

Go 数据存储篇(六):数据表之间的关联关系和关联查询

作者头像
学院君
发布2020-10-19 09:46:32
3.1K0
发布2020-10-19 09:46:32
举报
文章被收录于专栏:学院君的专栏学院君的专栏

1、关联关系简介

MySQL 之所以被称之为关系型数据库,是因为可以基于外键定义数据表之间的关联关系,日常开发常见的关联关系如下所示:

  • 一对一:一张表的一条记录对应另一张表的一条记录,比如用户表与用户资料表
  • 一对多:一张表的一条记录对应另一张表的多条记录,比如用户表与文章表、文章表与评论表
  • 多对一:一张表的多条记录归属另一张表的一条记录(一对多的逆向操作)
  • 多对多:一张表的多条记录归属另一张表的多条记录,此时仅仅基于两张表的字段已经无法定义这种关联关系,需要借助中间表来定义,比如文章表与标签表往往是这种关联

我们在上篇教程已经介绍了 Go 语言中基于第三方包 go-sql-driver/mysql 对单张数据表的增删改查操作,接下来我们来看看如何基于这个包对关联表进行操作。

2、新建评论表

为了方便演示,我们在 test_db 数据库中新建一张评论表 comments

代码语言:javascript
复制
CREATE TABLE `comments` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `content` text COLLATE utf8mb4_unicode_ci,
  `author` varchar(30) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `post_id` bigint unsigned DEFAULT NULL,
  PRIMARY KEY (`id`),
  FOREIGN KEY fk_post_id(`post_id`) REFERENCES posts(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

这里我们创建了一个外键将 comments 表的 post_id 字段和 posts 表的 id 字段关联起来,并且通过 ON DELETE CASCADE 声明将两张表级联起来:当删除 posts 表中的某条记录时,自动删除 comments 中与之关联的评论记录(如果省略这个声明,则不能直接删除 posts 表中有 comments 关联依赖的记录)。

我们在 postscomments 插入两条记录,这两条记录通过 comments.post_id 建立了外键关联:

此时,如果删除 posts 表中的记录,刷新 comments 表,会发现 comments 表对应记录也被清空,说明外键关联生效。

3、编写示例代码

接下来,我们编写一段示例代码演示如何在 Go 语言中通过 go-sql-driver/mysql 包对文章表和评论表进行关联查询。

新建一个 mysql 子目录来存放示例代码,这一次,我们通过拆分不同操作业务逻辑到不同文件来构建这个示例程序。

初始化连接

mysql 目录下新建一个 conn.go 编写数据库连接代码:

代码语言:javascript
复制
package main

import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
)

var Db *sql.DB

// 初始化数据库连接
func init() {
    var err error
    Db, err = sql.Open("mysql", "root:root@/test_db?charset=utf8mb4&parseTime=true")
    if err != nil {
        panic(err)
    }
}

注意到 Db 变量首字母大写了,因此一旦初始化之后,就可以在当前包下的任何文件中直接引用了。

迁移文章增删改查代码

posts 表增删改查操作拆分到独立的 post.go,并且在 Post 结构体中引入 Comments []Comment 属性存放关联的评论信息:

代码语言:javascript
复制
package main

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

func Posts(limit int) (posts []Post, err error) {
    stmt, err := Db.Prepare("select id, title, content, author from posts limit ?")
    if err != nil {
        panic(err)
    }
    defer stmt.Close()
    rows, err := stmt.Query(limit)
    if err != nil {
        panic(err)
    }
    for rows.Next() {
        post := Post{}
        err = rows.Scan(&post.Id, &post.Title, &post.Content, &post.Author)
        if err != nil {
            panic(err)
        }
        posts = append(posts, post)
    }
    return
}

func GetPost(id int) (post Post, err error) {
    post = Post{}
    err = Db.QueryRow("select id, title, content, author from posts where id = ?", id).
        Scan(&post.Id, &post.Title, &post.Content, &post.Author)

    // 查询与之关联的 comments 记录
    rows, err := Db.Query("select id, content, author from comments where post_id = ?", post.Id)
    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
}

func (post *Post) Create() (err error) {
    sql := "insert into posts (title, content, author) values (?, ?, ?)"
    stmt, err := Db.Prepare(sql)
    if err != nil {
        panic(err)
    }
    defer stmt.Close()

    res, err := stmt.Exec(post.Title, post.Content, post.Author)
    if err != nil {
        panic(err)
    }

    postId, _ := res.LastInsertId()
    post.Id = int(postId)
    return
}

func (post *Post) Update() (err error)  {
    stmt, err := Db.Prepare("update posts set title = ?, content = ?, author = ? where id = ?")
    if err != nil {
        return
    }
    stmt.Exec(post.Title, post.Content, post.Author, post.Id)
    return
}

func (post *Post) Delete() (err error) {
    stmt, err := Db.Prepare("delete from posts where id = ?")
    if err != nil {
        return
    }
    stmt.Exec(post.Id)
    return
}

我们在 GetPost 方法中获取单条文章记录后,再通过对应文章 ID 进行数据库查询获取相关评论信息存放到 post 对象的 Comments 属性中,这样就可以通过该属性获取文章的评论数据了。

定义评论相关操作

紧接着创建 comment.go 定义 Comment 结构体及新建评论方法:

代码语言:javascript
复制
package main

import "errors"

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

func (comment *Comment) Create() (err error) {
    if comment.Post == nil {
        err = errors.New("Post not found")
        return
    }

    sql := "insert into comments (content, author, post_id) values (?, ?, ?)"
    res, err := Db.Exec(sql, comment.Content, comment.Author, comment.Post.Id)
    if err != nil {
        return
    }

    commentId, _ := res.LastInsertId()
    comment.Id = int(commentId)
    return
}

Comment 中,可以通过 Post *Post 指针引用其所属的文章对象。

整体测试代码

最后编写 main.go 测试上述关联查询:

代码语言:javascript
复制
package main

import (
    "fmt"
)

func main()  {
    // 插入文章记录
    post := Post{Title: "Golang 数据库编程", Content: "通过 go-sql-driver/mysql 包进行表之间的关联查询", Author: "学院君"}
    post.Create()

    // 插入评论记录
    comment1 := Comment{Content: "测试评论1", Author: "学院君", Post: &post}
    comment1.Create()

    comment2 := Comment{Content: "测试评论2", Author: "学院君", Post: &post}
    comment2.Create()

    // 查询文章评论信息
    mysqlPost, _ := GetPost(post.Id)
    fmt.Println(mysqlPost)
    fmt.Println(mysqlPost.Comments)
    fmt.Println(mysqlPost.Comments[0].Post)
}

我们在 PostComment 结构体中分别通过 Comments 切片(数组指针)和 Post 指针定义两者之间的一对多和多对一关联,然后在查询文章记录的 GetPost 方法中编写通过 Post ID 查询关联 Comment 记录的代码,在创建 Comment 的时候,也要确保对应的 Post 字段不为空,即 post_id 字段不为空,这样就将两者通过代码关联起来了。

编译 mysql 这个包,并运行生成的二进制可执行程序,输出结果如下:

表明关联查询成功。

虽然我们已经构建起关联关系,但是全靠自己撸代码有点麻烦,而且随着应用的增长,这种复杂度会越来越大。我们可以通过 ORM 类来简化这个流程,目前 Go 语言中最流行的 ORM 实现非 GORM 莫属,下篇教程,学院君就来给大家介绍 GORM 的基本使用。

(全文完)

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-10-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 极客书房 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、关联关系简介
  • 2、新建评论表
    • 3、编写示例代码
      • 初始化连接
        • 迁移文章增删改查代码
          • 定义评论相关操作
            • 整体测试代码
            相关产品与服务
            云数据库 SQL Server
            腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档