15 | 定義簡潔架構 Store 層的數據類型

提示:

  • 所有體系課見專欄:Go 項目開發極速入門實戰課;
  • 歡迎加入 云原生 AI 實戰 星球,12+ 高質量體系課、20+ 高質量實戰項目助你在 AI 時代建立技術競爭力(聚焦于 Go、云原生、AI Infra);
  • 本節課最終源碼位于 fastgo 項目的 feature/s11 分支;
  • 更詳細的課程版本見:Go 項目開發中級實戰課:24 | 業務實現(1):實現 Store 層數據結構定義

在完成基礎功能開發后,需要進一步開發與業務邏輯相關的代碼。相較于基礎功能,業務邏輯代碼不僅占據了代碼倉庫的大部分代碼量,其復雜性也更高。因此,需要設計一種合理的代碼架構,以確保代碼的可讀性、可維護性和可擴展性。目前,業界較為流行且被廣泛認可的代碼架構是簡潔架構。

本課程第 14 節課詳細介紹了 fastgo 項目的簡潔架構設計。本節課,將根據第 14 節課的簡潔架構設計,實現 fastgo 的業務邏輯。

三層架構開發

在第 14 節課中介紹了 fastgo 三層簡潔架構的依賴關系:Handler 層依賴 Biz 層,Biz 層依賴 Store 層,Store 層依賴數據庫。依賴關系如下圖所示。

為了能夠隨時測試所開發的代碼功能,最優的方式是優先開發依賴較少的組件。否則,需要先 Mock 或開發所依賴的功能(層)。因此,開發順序應為:先開發 Store 層,接著是 Biz 層,最后是 Handler 層。

本節課及接下來幾節課代碼改動量較大,其中有很多同類改動。為了提高你的學習效率,本節課不會對代碼進行逐行解讀,相反主要講解其中的核心設計和實現。

Store 層數據結構定義

根據依賴關系,需要先開發 Store 層代碼。Store 層依賴一些數據類型。如果項目持久存儲用的是 MySQL/MariaDB 數據庫,這些數據類型其實就是 GORM Model。GORM Model 實際上是數據庫表字段到 Go 結構體的映射。可以根據 fastgo 數據庫中的表來創建對應的 Model。

fastgo 數據庫中包含以下三張表,這些表的結構在項目設計階段已完成設計和創建:

  • user 表:該表用于存儲用戶數據;
  • post 表:該表用于存儲博客數據。

需要根據表名、表字段及表字段類型創建 Store 層的 Go 結構體,以映射數據庫中的對應表。

GORM Model 結構體定義可以手動編寫,也可以借助工具自動生成。建議使用工具自動生成,具體步驟如下:

  1. 創建數據庫和數據庫表;
  2. 根據數據庫表生成 Model 文件;
  3. 修改生成的 Go 代碼。

根據數據庫表生成 Model 文件

在 Go 項目開發中,編寫 GORM Model 文件通常有多種方式,例如根據數據庫表結構手動編寫 GORM Model,或使用讀取數據庫表結構并自動生成 GORM Model 的工具,例如 GORM 官方提供 gentool。

$ go install gorm.io/gen/tools/gentool@latest
$ gentool -db mysql -dsn 'fastgo:fastgo1234@tcp(1127.0.0.1:3306)/fastgo' -onlyModel -modelPkgName internal/apiserver/model

上述命令會在 internal/apiserver/model/ 目錄下生成 user.gen.go 和 post.gen.go GORM Model 文件。這 2 個文件中包含了 GORM Model 結構體定義。

提示:在第 02 節課中,我們安裝了數據庫,并創建了 fastgo 數據庫及表。

添加 GORM 鉤子

在生成了 GORM Model 結構體之后,可根據需要給這些結構體添加一些 GORM 鉤子。常用的 GORM 鉤子見下表所示:

返回頭說明
BeforeCreate在執行 INSERT 語句前觸發(比如對數據進行校驗或補充字段)
AfterCreate在執行 INSERT 語句后觸發(比如生成關聯值或更新其他表數據)
BeforeFind在執行查詢(SELECT)語句前觸發(比如動態調整查詢條件)
AfterFind在執行查詢(SELECT)操作后觸發(比如格式化數據或處理查詢返回值)
BeforeUpdate在執行 UPDATE 語句前觸發(比如校驗更新字段的合法性)
AfterUpdate在執行 UPDATE 語句后觸發(比如記錄操作日志或更新緩存)
BeforeDelete在執行 DELETE 語句前觸發(比如檢查業務邏輯或軟刪除處理)
AfterDelete在執行 DELETE 語句后觸發(比如記錄日志或同步其他系統的數據)
BeforeSave在執行任何保存(INSERT 或 UPDATE)操作之前觸發(適用于既需要創建也需要更新的公共邏輯)
AfterSave在執行任何保存(INSERT 或 UPDATE)操作之后觸發(適用于既需要創建也需要更新的公共邏輯)

fastgo 項目在 internal/apiserver/model/hook.go 文件中,添加了數據庫表 userIDpostID 字段的自動生成鉤子,用來生成并保存記錄的唯一標識符,代碼如下所示:

package modelimport ("gorm.io/gorm""github.com/onexstack/fastgo/internal/pkg/rid"
)// AfterCreate 在創建數據庫記錄之后生成 postID.
func (m *PostM) AfterCreate(tx *gorm.DB) error {m.PostID = rid.PostID.New(uint64(m.ID))return tx.Save(m).Error
}// AfterCreate 在創建數據庫記錄之后生成 userID.
func (m *UserM) AfterCreate(tx *gorm.DB) error {m.UserID = rid.UserID.New(uint64(m.ID))return tx.Save(m).Error
}

上述代碼在創建完數據庫表記錄之后,會調用 rid 包,基于數據庫生成的自增 ID 生成一個形如 user-uvalgf 的英文唯一 ID,并調用 tx.Save() 方法將 ID 更新到表記錄中。

Go 項目開發中,需要為每一個條 REST 資源生成唯一標識符(Unique Identifier,UID),以唯一定位該 REST 資源。例如更新資源、刪除資源時,需要提供該唯一 ID。

fastgo 項目的 github.com/onexstack/fastgo/internal/pkg/rid 包用來生成唯一 ID。rid(resource id)包核心實現如下:

package ridimport ("github.com/onexstack/onexstack/pkg/id"
)const defaultABC = "abcdefghijklmnopqrstuvwxyz1234567890"type ResourceID stringconst (// UserID 定義用戶資源標識符.UserID ResourceID = "user"// PostID 定義博文資源標識符.PostID ResourceID = "post"
)// String 將資源標識符轉換為字符串.
func (rid ResourceID) String() string {return string(rid)
}// New 創建帶前綴的唯一標識符.
func (rid ResourceID) New(counter uint64) string {// 使用自定義選項生成唯一標識符uniqueStr := id.NewCode(counter,id.WithCodeChars([]rune(defaultABC)),id.WithCodeL(6),id.WithCodeSalt(Salt()),)return rid.String() + "-" + uniqueStr
}

上述代碼,定義了一個 ResourceID 數據類型,其 String 方法和 New 方法,分別用來返回資源的字符串標識和資源的唯一 ID,格式為 <資源標識符>-<6 位隨機數>。使用帶資源前綴的唯一 ID,有利于通過唯一 ID 辨別資源類型,通過前綴避免可能的隨機數沖突。

ResourceID 是一個簡單的、可擴展的統一表示形式,未來可根據需要添加更多的自定義資源,并復用 ResourceID 的方法,生成新資源的唯一 ID,例如 comment-w6irkgNew(counter uint64) 方法中的 counter 通常為數據庫自增 ID,基于數據庫自增 ID 生成唯一標識,不僅可以生成短小、易讀的唯一 ID,還可以隱藏掉自增 ID 的背后的數據規模。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/72017.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/72017.shtml
英文地址,請注明出處:http://en.pswp.cn/web/72017.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

CSDN統計個人創作總字數

前言 不是很懂爬蟲&#xff0c;所以就叫deepseek寫了一個 用起來很簡單&#xff0c;但是有一個小問題&#xff0c;就是統計的是總字符數。代碼片會被統計進去&#xff0c;Markdown語法也會被統計進去。 不過我沒有太多需求&#xff0c;能大概統計一下滿足以下小小的好奇心和成…

React.js 基礎與進階教程

React.js 基礎與進階教程 React.js 是由 Facebook 開發的流行前端 JavaScript 庫&#xff0c;專為構建用戶界面&#xff08;UI&#xff09;設計&#xff0c;尤其適用于單頁面應用&#xff08;SPA&#xff09;。它采用組件化開發模式&#xff0c;使 UI 結構更加清晰、可維護性更…

msf(Metasploit)中Session與Channel的區別與關系解析

在 Metasploit Framework&#xff08;MSF&#xff09;中&#xff0c;Session 和 Channel 都是與目標主機的交互方式&#xff0c;但它們的作用和概念有所不同。本文將解析這兩個術語的區別。 一、Session&#xff08;會話&#xff09; Session 是指通過 Metasploit 成功利用目標…

設計模式-結構型模式-裝飾器模式

概述 裝飾器模式 : Decorator Pattern : 是一種結構型設計模式. 作用 &#xff1a; 允許你動態地給對象添加功能或職責&#xff0c;而無需修改其原始類的代碼,非常的符合 開閉原則。 實現思路 &#xff1a;通過創建一個包裝對象&#xff08;即裝飾器&#xff09;&#xff0c;來…

Qt/C++音視頻開發82-系統音量值獲取和設置/音量大小/靜音

一、前言 在音視頻開發中&#xff0c;音量的控制分兩塊&#xff0c;一個是控制播放器本身的音量&#xff0c;絕大部分場景都是需要控制這個&#xff0c;這個不會影響系統音量的設置。還有一種場景是需要控制系統的音量&#xff0c;因為播放器本身的音量是在系統音量的基礎上控…

基于深度學習的醫學CT圖像肺結節智能檢測與語音提示系統【python源碼+Pyqt5界面+數據集+訓練代碼】

《------往期經典推薦------》 一、AI應用軟件開發實戰專欄【鏈接】 項目名稱項目名稱1.【人臉識別與管理系統開發】2.【車牌識別與自動收費管理系統開發】3.【手勢識別系統開發】4.【人臉面部活體檢測系統開發】5.【圖片風格快速遷移軟件開發】6.【人臉表表情識別系統】7.【…

前端小食堂 | Day14 - Vue 3 の傳送門與懸念

&#x1f300; 今日秘技&#xff1a;Teleport 與 Suspense の時空魔法 1. Teleport 任意門 <template> <!-- &#x1f6aa; 將組件傳送到 body 末尾 --> <Teleport to"body"> <div class"modal"> <h2>重要通知&#x…

emacs使用mongosh的方便工具發布

github項目地址: GitHub - csfreebird/emacs_mongosh: 在emacs中使用mongosh快速登錄mongodb數據庫 * 用途 在emacs中使用mongosh快速登錄mongodb數據庫&#xff0c; 操作方法: M-x mongosh, 輸入數據庫名稱&#xff0c;然后就可以自動登錄&#xff0c;前提是你已經配置好了…

Linux:Ubuntu server 24.02 上搭建 ollama + dify

一、安裝Ubuntu 具體的安裝過程可以參見此鏈接&#xff1a;鏈接&#xff1a;Ubuntu Server 20.04詳細安裝教程&#xff0c;這里主要記錄一下過程中遇到的問題。 安裝時subnet如何填寫 在Ubuntu中subnet填寫255.255.255.0是錯誤的&#xff0c;其格式為 xx.xx.xx.xx/yy &#…

unordered_set 的常用函數

在 C 的標準庫中&#xff0c;std::unordered_set 是基于哈希表實現的哈希集合。下面介紹這種語言里哈希集合的常用函數。 C std::unordered_set 1. 元素操作 insert 功能&#xff1a;向哈希集合中插入元素。如果元素已經存在&#xff0c;則不會重復插入。示例代碼&#xff1a…

starrocks批量啟停腳本

#!/bin/bash # 定義 StarRocks 安裝目錄 STARROCKS_HOME"/path/to/starrocks" # 定義 FE 和 BE 節點列表 FE_NODES("fe_node1_ip" "fe_node2_ip" "fe_node3_ip") BE_NODES("be_node1_ip" "be_node2_ip" "be_…

python 提取視頻中的音頻

在Python中提取視頻中的音頻&#xff0c;你可以使用moviepy庫&#xff0c;這是一個非常強大且易于使用的庫&#xff0c;專門用于視頻編輯。以下是如何使用moviepy來提取視頻中的音頻的步驟&#xff1a; 安裝moviepy 首先&#xff0c;你需要安裝moviepy。你可以通過pip安裝它&a…

大語言模型打卡學習DAY1

學習目標&#xff1a; 語言模型的發展歷程 大模型的技術基礎 學習內容&#xff1a; 1. 語言模型的發展歷程 語言模型通常是指能夠建模自然語言文本生成概率的模型&#xff0c;從語言建模到任務求解&#xff0c;這是科學思維的一次重要躍升。2. 大語言模型技術基礎 定義&#…

boarding_passes(登機牌)表的作用

boarding_passes&#xff08;登機牌&#xff09;表的作用 boarding_passes 這張表的主要作用是記錄旅客的登機信息&#xff0c;包括&#xff1a; 票號 (ticket_no) - 關聯到 tickets 表&#xff0c;表示這張票屬于哪個旅客。航班 ID (flight_id) - 關聯到 flights 表&#xf…

Go語言為什么運行比Java快

文章目錄 前言一、核心區別二、Go Vs Java1.Go 的啟動比 Java 快&#xff1f;2.選 Go Or Java&#xff1f; 總結 前言 Go 和 Java 是兩種廣泛應用的編程語言&#xff0c;它們在語言特性、性能、生態、應用場景等方面存在顯著區別。以下是它們的核心區別&#xff0c;以及在實際…

java生成一個24位的字符串,要求這個字符串由大寫的英文字母和數字組成,長度固定位24位

import java.security.SecureRandom;public class RandomStringGenerator {// 定義允許的字符集&#xff08;大寫字母和數字&#xff09;private static final String ALLOWED_CHARACTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";private static final SecureRando…

基于python的升級隊列加速決策

a-f大等級是3級 a-c建筑每升1級分別需要8天 d-f建筑每升1級分別需要10天 目前以下建筑隊列正在從0級升至1級 建筑A升級需要7天05&#xff1a;16&#xff1a;20 建筑b升級需要06&#xff1a;06&#xff1a;54 建筑c升級需要00&#xff1a;37&#xff1a;00 建筑d升級需要…

【經驗】Ubuntu|VMware 新建虛擬機后打開 SSH 服務、在主機上安裝vscode并連接、配置 git 的 ssh

常常有人問VMware-Tools裝了也復制粘貼不了怎么辦&#xff0c;這個東西影響因素太多了&#xff0c;我總是建議直接用SSH連接虛擬機。但是之前一直都沒有出教程&#xff0c;現在出一個簡單的教程。 文章目錄 在 Ubuntu 虛擬機&#xff08;VMware&#xff09;中開啟 SSH 服務、配…

C++多線程編程 3.互斥量、互斥鎖

目錄 1. 線程安全與互斥鎖&#xff08;std::mutex&#xff09; 2. 互斥量死鎖 3. std::lock_guard 4. std::unique_lock (1)示例 (2)詳細知識點 5. std::this_thread (1)sleep_for (2)sleep_until (3)yield (4)get_id 直接通過示例講解&#xff1a; 1. 線程安全與互…

【redis】hash基本命令和內部編碼

文章目錄 表示形式命令HSET 和 HGET HEXISTSHDELHKEYSHVALSHGETALLHMGETHLENHSETNXHINCRBYHINCRBYFLOAT命令小結內部編碼 表示形式 Redis 自身已經是鍵值對結構了 Redis 自身的鍵值對就是通過哈希的方式來組織的 把 key 這一層組織完成之后&#xff0c;到了 value 這一層&…