<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Go 數(shù)據(jù)存儲(chǔ)篇(六):數(shù)據(jù)表之間的關(guān)聯(lián)關(guān)系和關(guān)聯(lián)查詢

          共 5535字,需瀏覽 12分鐘

           ·

          2020-10-03 12:21

          1、關(guān)聯(lián)關(guān)系簡(jiǎn)介

          MySQL 之所以被稱之為關(guān)系型數(shù)據(jù)庫(kù),是因?yàn)榭梢曰谕怄I定義數(shù)據(jù)表之間的關(guān)聯(lián)關(guān)系,日常開(kāi)發(fā)常見(jiàn)的關(guān)聯(lián)關(guān)系如下所示:

          • 一對(duì)一:一張表的一條記錄對(duì)應(yīng)另一張表的一條記錄,比如用戶表與用戶資料表

          • 一對(duì)多:一張表的一條記錄對(duì)應(yīng)另一張表的多條記錄,比如用戶表與文章表、文章表與評(píng)論表

          • 多對(duì)一:一張表的多條記錄歸屬另一張表的一條記錄(一對(duì)多的逆向操作)

          • 多對(duì)多:一張表的多條記錄歸屬另一張表的多條記錄,此時(shí)僅僅基于兩張表的字段已經(jīng)無(wú)法定義這種關(guān)聯(lián)關(guān)系,需要借助中間表來(lái)定義,比如文章表與標(biāo)簽表往往是這種關(guān)聯(lián)

          我們?cè)谏掀坛桃呀?jīng)介紹了 Go 語(yǔ)言中基于第三方包 go-sql-driver/mysql 對(duì)單張數(shù)據(jù)表的增刪改查操作,接下來(lái)我們來(lái)看看如何基于這個(gè)包對(duì)關(guān)聯(lián)表進(jìn)行操作。

          2、新建評(píng)論表

          為了方便演示,我們?cè)?test_db 數(shù)據(jù)庫(kù)中新建一張?jiān)u論表 comments

          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;

          這里我們創(chuàng)建了一個(gè)外鍵將 comments 表的 post_id 字段和 posts 表的 id 字段關(guān)聯(lián)起來(lái),并且通過(guò) ON DELETE CASCADE 聲明將兩張表級(jí)聯(lián)起來(lái):當(dāng)刪除 posts 表中的某條記錄時(shí),自動(dòng)刪除 comments 中與之關(guān)聯(lián)的評(píng)論記錄(如果省略這個(gè)聲明,則不能直接刪除 posts 表中有 comments 關(guān)聯(lián)依賴的記錄)。

          我們?cè)?postscomments 插入兩條記錄,這兩條記錄通過(guò) comments.post_id 建立了外鍵關(guān)聯(lián):


          此時(shí),如果刪除 posts 表中的記錄,刷新 comments 表,會(huì)發(fā)現(xiàn) comments 表對(duì)應(yīng)記錄也被清空,說(shuō)明外鍵關(guān)聯(lián)生效。

          3、編寫(xiě)示例代碼

          接下來(lái),我們編寫(xiě)一段示例代碼演示如何在 Go 語(yǔ)言中通過(guò) go-sql-driver/mysql 包對(duì)文章表和評(píng)論表進(jìn)行關(guān)聯(lián)查詢。

          新建一個(gè) mysql 子目錄來(lái)存放示例代碼,這一次,我們通過(guò)拆分不同操作業(yè)務(wù)邏輯到不同文件來(lái)構(gòu)建這個(gè)示例程序。

          初始化連接

          mysql 目錄下新建一個(gè) conn.go 編寫(xiě)數(shù)據(jù)庫(kù)連接代碼:

          package?main

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

          var?Db?*sql.DB

          //?初始化數(shù)據(jù)庫(kù)連接
          func?init()?{
          ????var?err?error
          ????Db,?err?=?sql.Open("mysql",?"root:root@/test_db?charset=utf8mb4&parseTime=true")
          ????if?err?!=?nil?{
          ????????panic(err)
          ????}
          }

          注意到 Db 變量首字母大寫(xiě)了,因此一旦初始化之后,就可以在當(dāng)前包下的任何文件中直接引用了。

          遷移文章增刪改查代碼

          posts 表增刪改查操作拆分到獨(dú)立的 post.go,并且在 Post 結(jié)構(gòu)體中引入 Comments []Comment 屬性存放關(guān)聯(lián)的評(píng)論信息:

          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)

          ????//?查詢與之關(guān)聯(lián)的?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
          }

          我們?cè)?GetPost 方法中獲取單條文章記錄后,再通過(guò)對(duì)應(yīng)文章 ID 進(jìn)行數(shù)據(jù)庫(kù)查詢獲取相關(guān)評(píng)論信息存放到 post 對(duì)象的 Comments 屬性中,這樣就可以通過(guò)該屬性獲取文章的評(píng)論數(shù)據(jù)了。

          定義評(píng)論相關(guān)操作

          緊接著創(chuàng)建 comment.go 定義 Comment 結(jié)構(gòu)體及新建評(píng)論方法:

          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 中,可以通過(guò) Post *Post 指針引用其所屬的文章對(duì)象。

          整體測(cè)試代碼

          最后編寫(xiě) main.go 測(cè)試上述關(guān)聯(lián)查詢:

          package?main

          import?(
          ????"fmt"
          )

          func?main()??{
          ????//?插入文章記錄
          ????post?:=?Post{Title:?"Golang?數(shù)據(jù)庫(kù)編程",?Content:?"通過(guò)?go-sql-driver/mysql?包進(jìn)行表之間的關(guān)聯(lián)查詢",?Author:?"學(xué)院君"}
          ????post.Create()

          ????//?插入評(píng)論記錄
          ????comment1?:=?Comment{Content:?"測(cè)試評(píng)論1",?Author:?"學(xué)院君",?Post:?&post}
          ????comment1.Create()

          ????comment2?:=?Comment{Content:?"測(cè)試評(píng)論2",?Author:?"學(xué)院君",?Post:?&post}
          ????comment2.Create()

          ????//?查詢文章評(píng)論信息
          ????mysqlPost,?_?:=?GetPost(post.Id)
          ????fmt.Println(mysqlPost)
          ????fmt.Println(mysqlPost.Comments)
          ????fmt.Println(mysqlPost.Comments[0].Post)
          }

          我們?cè)?PostComment 結(jié)構(gòu)體中分別通過(guò) Comments 切片(數(shù)組指針)和 Post 指針定義兩者之間的一對(duì)多和多對(duì)一關(guān)聯(lián),然后在查詢文章記錄的 GetPost 方法中編寫(xiě)通過(guò) Post ID 查詢關(guān)聯(lián) Comment 記錄的代碼,在創(chuàng)建 Comment 的時(shí)候,也要確保對(duì)應(yīng)的 Post 字段不為空,即 post_id 字段不為空,這樣就將兩者通過(guò)代碼關(guān)聯(lián)起來(lái)了。

          編譯 mysql 這個(gè)包,并運(yùn)行生成的二進(jìn)制可執(zhí)行程序,輸出結(jié)果如下:

          表明關(guān)聯(lián)查詢成功。

          雖然我們已經(jīng)構(gòu)建起關(guān)聯(lián)關(guān)系,但是全靠自己擼代碼有點(diǎn)麻煩,而且隨著應(yīng)用的增長(zhǎng),這種復(fù)雜度會(huì)越來(lái)越大。我們可以通過(guò) ORM 類來(lái)簡(jiǎn)化這個(gè)流程,目前 Go 語(yǔ)言中最流行的 ORM 實(shí)現(xiàn)非 GORM 莫屬,下篇教程,學(xué)院君就來(lái)給大家介紹 GORM 的基本使用。

          (全文完)



          推薦閱讀


          福利

          我為大家整理了一份從入門(mén)到進(jìn)階的Go學(xué)習(xí)資料禮包(下圖只是部分),同時(shí)還包含學(xué)習(xí)建議:入門(mén)看什么,進(jìn)階看什么。

          關(guān)注公眾號(hào) 「polarisxu」,回復(fù)?ebook?獲?。贿€可以回復(fù)「進(jìn)群」,和數(shù)萬(wàn) Gopher 交流學(xué)習(xí)。



          瀏覽 163
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  囯产精品久久 | 久草久| 亚洲天堂在线观看视频 | 国产精品国产三级国产 | 大鸡巴免费视频 |