Go語言 GORM框架 使用指南

在 Go 語言社區中,數據庫交互一直是開發者們關注的重點領域,不同開發者基于自身的需求和偏好,形成了兩種主要的技術選型流派。一部分開發者鐘情于像sqlx這類簡潔的庫,盡管其功能并非一應俱全,但它賦予開發者對 SQL 語句的絕對控制權,便于開發者將性能優化到極致,從而滿足對性能有嚴苛要求的場景。與之相對的另一部分開發者,則更傾向于為提升開發效率而生的 ORM(對象關系映射)框架。借助 ORM,開發者能夠省去諸多繁瑣的數據庫操作細節,極大地加速開發進程。

在 Go 語言的 ORM 領域,gorm無疑占據著舉足輕重的地位,作為一款歷史悠久且成熟的 ORM 框架,深受廣大開發者的喜愛。與gorm類似的,還有相對年輕的xorment等框架,它們各自憑借獨特的優勢,在 Go 語言社區中也擁有著一批忠實的用戶。

本文聚焦于gorm框架,主要為大家介紹其基礎入門知識,希望能為讀者開啟探索gorm世界的大門。若想深入了解gorm的更多細節,推薦閱讀官方文檔,其完善的中文文檔為開發者提供了詳盡的學習資料。

  • 官方文檔:GORM - The fantastic ORM library for Golang, aims to be developer friendly.
  • 開源倉庫:go-gorm/gorm: The fantastic ORM library for Golang, aims to be developer friendly (github.com)

特點

  1. 全功能 ORM:涵蓋了豐富的數據庫操作功能,為開發者提供一站式解決方案。
  2. 關聯關系支持:全面支持多種關聯關系,如擁有一個(Has One)、擁有多個(Has Many)、屬于(Belongs To)、多對多(Many To Many)、多態(Polymorphism)以及單表繼承(Single-table inheritance),滿足復雜業務場景下的數據關系建模需求。
  3. 鉤子方法:在 Create、Save、Update、Delete、Find 等操作中均提供了鉤子方法,方便開發者在數據庫操作前后進行自定義邏輯處理。
  4. 預加載功能:支持PreloadJoins的預加載方式,有效減少數據庫查詢次數,提升數據獲取效率。
  5. 事務管理:提供完善的事務管理機制,包括事務、嵌套事務、Save Point 以及 Rollback To Saved Point 等功能,確保數據操作的原子性和一致性。
  6. 多種模式支持:支持 Context、預編譯模式(Prepared Statement Mode)和 DryRun 模式,為開發者提供更多的靈活性和調試便利性。
  7. 高效的數據操作:具備批量插入、FindInBatches、Find/Create with Map 等功能,并且支持使用 SQL 表達式、Context Valuer 進行 CRUD 操作,滿足不同場景下的數據操作需求。
  8. 強大的 SQL 構建能力:擁有 SQL 構建器,支持 Upsert、鎖機制、Optimizer/Index/Comment Hint、命名參數以及子查詢等高級 SQL 特性,讓開發者能夠靈活構建復雜的 SQL 語句。
  9. 數據庫結構管理:支持復合主鍵、索引和約束的創建與管理,同時提供自動遷移功能,能夠根據定義的結構體自動同步數據庫表結構,減少手動維護數據庫結構的工作量。
  10. 自定義日志:允許開發者自定義 Logger,方便記錄和跟蹤數據庫操作日志,便于排查問題和進行性能分析。
  11. 靈活的插件擴展:提供靈活可擴展的插件 API,例如 Database Resolver(支持多數據庫、讀寫分離)、Prometheus 等插件,滿足不同業務場景下的擴展需求。
  12. 嚴格的測試保障:每個特性都經過了嚴格的測試,確保框架的穩定性和可靠性。
  13. 開發者友好:設計理念注重開發者體驗,提供簡潔易懂的 API 和豐富的文檔,降低開發者的學習成本。

當然,gorm并非完美無缺。例如,其幾乎所有方法的參數都采用空接口類型,這使得參數的傳遞方式較為模糊,若不查閱文檔,開發者很難明確在不同場景下應傳遞何種參數,有時可傳遞結構體,有時是字符串、map 或切片。此外,在許多情況下,開發者仍需自行編寫 SQL 語句來滿足復雜的業務需求。

其中,gorm作為 Go 生態中歷史悠久的 ORM 框架,憑借其全面的功能支持和良好的社區活躍度,成為眾多項目的首選。本文將圍繞gorm展開基礎入門介紹,旨在幫助快速上手。

安裝

$ go get -u gorm.io/gorm

gorm 目前支持以下幾種數據庫

  • MySQL :"gorm.io/driver/mysql"
  • PostgreSQL: "gorm.io/driver/postgres"
  • SQLite:"gorm.io/driver/sqlite"
  • SQL Server:"gorm.io/driver/sqlserver"
  • TIDB:"gorm.io/driver/mysql",TIDB 兼容 mysql 協議
  • ClickHouse:"gorm.io/driver/clickhouse"

本文接下來將使用 MySQL 來進行演示,使用的什么數據庫,就需要安裝什么驅動,這里安裝 Mysql 的 gorm 驅動。

$ go get -u gorm.io/driver/mysql  # 以MySQL為例

然后使用 dsn(data source name)連接到數據庫,驅動庫會自行將 dsn 解析為對應的配置

package mainimport ("gorm.io/driver/mysql""gorm.io/gorm""log/slog"
)func main() {dsn := "root:123456@tcp(192.168.48.138:3306)/hello?charset=utf8mb4&parseTime=True&loc=Local"db, err := gorm.Open(mysql.Open(dsn))if err != nil {slog.Error("db connect error", err)}slog.Info("db connect success")
}

或者手動傳入配置

package mainimport ("gorm.io/driver/mysql""gorm.io/gorm""log/slog"
)func main() {db, err := gorm.Open(mysql.New(mysql.Config{}))if err != nil {slog.Error("db connect error", err)}slog.Info("db connect success")
}

兩種方法都是等價的,看自己使用習慣。

連接配置

通過傳入gorm.Config配置結構體,我們可以控制 gorm 的一些行為

db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})

模型

gorm.Model

為了方便模型定義,GORM內置了一個gorm.Model結構體。gorm.Model是一個包含了ID, CreatedAt, UpdatedAt, DeletedAt四個字段的Golang結構體。

// gorm.Model 定義
type Model struct {ID        uint `gorm:"primary_key"`CreatedAt time.TimeUpdatedAt time.TimeDeletedAt *time.Time
}

你可以將它嵌入到你自己的模型中:

// 將 `ID`, `CreatedAt`, `UpdatedAt`, `DeletedAt`字段注入到`User`模型中
type User struct {gorm.Model // 內嵌gorm.Model,包含ID, CreatedAt, UpdatedAt, DeletedAt字段    Name      string `gorm:"type:varchar(100);not null"`Email     string `gorm:"type:varchar(100);uniqueIndex"`Age       int    `gorm:"default:18"`IsActive  bool   `gorm:"default:true"`
}

當然你也可以完全自己定義模型:

// 不使用gorm.Model,自行定義模型
type User struct {ID   intName string
}

結構體標記(tags)

使用結構體聲明模型時,標記(tags)是可選項。gorm支持以下標記:

支持的結構體標記(Struct tags)

結構體標記(Tag)描述
Column指定列名
Type指定列數據類型
Size指定列大小, 默認值255
PRIMARY_KEY將列指定為主鍵
UNIQUE將列指定為唯一
DEFAULT指定列默認值
PRECISION指定列精度
NOT NULL將列指定為非 NULL
AUTO_INCREMENT指定列是否為自增類型
INDEX創建具有或不帶名稱的索引, 如果多個索引同名則創建復合索引
UNIQUE_INDEXINDEX 類似,只不過創建的是唯一索引
EMBEDDED將結構設置為嵌入
EMBEDDED_PREFIX設置嵌入結構的前綴
-忽略此字段

關聯相關標記(tags)

結構體標記(Tag)描述
MANY2MANY指定連接表
FOREIGNKEY設置外鍵
ASSOCIATION_FOREIGNKEY設置關聯外鍵
POLYMORPHIC指定多態類型
POLYMORPHIC_VALUE指定多態值
JOINTABLE_FOREIGNKEY指定連接表的外鍵
ASSOCIATION_JOINTABLE_FOREIGNKEY指定連接表的關聯外鍵
SAVE_ASSOCIATIONS是否自動完成 save 的相關操作
ASSOCIATION_AUTOUPDATE是否自動完成 update 的相關操作
ASSOCIATION_AUTOCREATE是否自動完成 create 的相關操作
ASSOCIATION_SAVE_REFERENCE是否自動完成引用的 save 的相關操作
PRELOAD是否自動完成預加載的相關操作

主鍵、表名、列名的約定

主鍵(Primary Key)

GORM 默認會使用名為ID的字段作為表的主鍵。

type User struct {ID   string // 名為`ID`的字段會默認作為表的主鍵Name string
}// 使用`AnimalID`作為主鍵
type Animal struct {AnimalID int64 `gorm:"primary_key"`Name     stringAge      int64
}

表名(Table Name)

表名默認就是結構體名稱的復數,例如:

type User struct {} // 默認表名是 `users`// 將 User 的表名設置為 `profiles`
func (User) TableName() string {return "profiles"
}func (u User) TableName() string {if u.Role == "admin" {return "admin_users"} else {return "users"}
}// 禁用默認表名的復數形式,如果置為 true,則 `User` 的默認表名是 `user`
db.SingularTable(true)

也可以通過Table()指定表名:

// 使用User結構體創建名為`deleted_users`的表
db.Table("deleted_users").CreateTable(&User{})var deleted_users []User
db.Table("deleted_users").Find(&deleted_users)
 SELECT * FROM deleted_users;db.Table("deleted_users").Where("name = ?", "jinzhu").Delete()
 DELETE FROM deleted_users WHERE name = 'jinzhu';

GORM還支持更改默認表名稱規則:

gorm.DefaultTableNameHandler = func (db *gorm.DB, defaultTableName string) string  {return "prefix_" + defaultTableName;
}

列名(Column Name)

列名由字段名稱進行下劃線分割來生成

type User struct {ID        uint      // column name is `id`Name      string    // column name is `name`Birthday  time.Time // column name is `birthday`CreatedAt time.Time // column name is `created_at`
}

可以使用結構體tag指定列名:

type Animal struct {AnimalId    int64     `gorm:"column:beast_id"`         // set column name to `beast_id`Birthday    time.Time `gorm:"column:day_of_the_beast"` // set column name to `day_of_the_beast`Age         int64     `gorm:"column:age_of_the_beast"` // set column name to `age_of_the_beast`
}

時間戳跟蹤

CreatedAt

如果模型有 CreatedAt字段,該字段的值將會是初次創建記錄的時間。

db.Create(&user) // `CreatedAt`將會是當前時間// 可以使用`Update`方法來改變`CreateAt`的值
db.Model(&user).Update("CreatedAt", time.Now())

UpdatedAt

如果模型有UpdatedAt字段,該字段的值將會是每次更新記錄的時間。

db.Save(&user) // `UpdatedAt`將會是當前時間db.Model(&user).Update("name", "jinzhu") // `UpdatedAt`將會是當前時間

DeletedAt

如果模型有DeletedAt字段,調用Delete刪除該記錄時,將會設置DeletedAt字段為當前時間,而不是直接將記錄從數據庫中刪除。

CRUD接口

簡單的列舉了,創建、查詢、修改、刪除的使用,詳情可以看官方文檔。

創建 (Create)

// 創建單個記錄
user := User{Name: "張三", Age: 20}
result := db.Create(&user) 
// 執行SQL: INSERT INTO users (name, age) VALUES ('張三', 20);// 批量創建
users := []User{{Name: "李四", Age: 22},{Name: "王五", Age: 23},
}
db.Create(&users)
// 執行SQL: INSERT INTO users (name, age) VALUES ('李四', 22), ('王五', 23);

查詢 (Read)

單條查詢

var user User
db.First(&user)
// 執行SQL: SELECT * FROM users ORDER BY id LIMIT 1;db.Where("name = ?", "張三").First(&user)
// 執行SQL: SELECT * FROM users WHERE name = '張三' ORDER BY id LIMIT 1;db.First(&user, 10)
// 執行SQL: SELECT * FROM users WHERE id = 10;

多條查詢

var users []User
db.Where("age > ?", 20).Find(&users)
// 執行SQL: SELECT * FROM users WHERE age > 20;db.Where(map[string]interface{}{"name": "張三", "age": 20}).Find(&users)
// 執行SQL: SELECT * FROM users WHERE name = '張三' AND age = 20;

高級查詢

db.Select("name", "age").Find(&users)
// 執行SQL: SELECT name, age FROM users;db.Order("age desc").Find(&users)
// 執行SQL: SELECT * FROM users ORDER BY age DESC;db.Limit(10).Offset(5).Find(&users)
// 執行SQL: SELECT * FROM users LIMIT 10 OFFSET 5;var count int64
db.Model(&User{}).Where("age > ?", 20).Count(&count)
// 執行SQL: SELECT COUNT(*) FROM users WHERE age > 20;

更新 (Update)

db.Save(&user)
// 執行SQL: UPDATE users SET name='張三', age=20 WHERE id=1;db.Model(&user).Update("name", "李四")
// 執行SQL: UPDATE users SET name='李四' WHERE id=1;db.Model(&user).Updates(User{Name: "李四", Age: 21})
// 執行SQL: UPDATE users SET name='李四', age=21 WHERE id=1;db.Model(&User{}).Where("age < ?", 20).Update("name", "未成年人")
// 執行SQL: UPDATE users SET name='未成年人' WHERE age < 20;

刪除 (Delete)

db.Delete(&user)
// 執行SQL: DELETE FROM users WHERE id=1;db.Delete(&User{}, 10)
// 執行SQL: DELETE FROM users WHERE id=10;db.Where("age < ?", 20).Delete(&User{})
// 執行SQL: DELETE FROM users WHERE age < 20;

事務

自動事務

db.Transaction(func(tx *gorm.DB) error {if err := tx.Create(&user1).Error; err != nil {return err}// 執行SQL: INSERT INTO users (name, age) VALUES ('user1', 20);if err := tx.Create(&user2).Error; err != nil {return err}// 執行SQL: INSERT INTO users (name, age) VALUES ('user2', 22);return nil
})
// 如果成功執行SQL: COMMIT;
// 如果失敗執行SQL: ROLLBACK;

手動事務

tx := db.Begin()
// 執行SQL: BEGIN;tx.Create(&user1)
// 執行SQL: INSERT INTO users (name, age) VALUES ('user1', 20);tx.Create(&user2)
// 執行SQL: INSERT INTO users (name, age) VALUES ('user2', 22);tx.Commit()
// 執行SQL: COMMIT;// 或者出錯時
tx.Rollback()
// 執行SQL: ROLLBACK;

嵌套事務

db.Transaction(func(tx *gorm.DB) error {tx.Create(&user1)// 執行SQL: INSERT INTO users (name, age) VALUES ('user1', 20);tx.Transaction(func(tx2 *gorm.DB) error {tx2.Create(&user2)// 執行SQL: SAVEPOINT sp1;// 執行SQL: INSERT INTO users (name, age) VALUES ('user2', 22);return errors.New("inner error")// 執行SQL: ROLLBACK TO sp1;})return nil// 執行SQL: COMMIT;
})

保存點 (SavePoint)

tx := db.Begin()
// 執行SQL: BEGIN;tx.Create(&user1)
// 執行SQL: INSERT INTO users (name, age) VALUES ('user1', 20);tx.SavePoint("sp1")
// 執行SQL: SAVEPOINT sp1;tx.Create(&user2)
// 執行SQL: INSERT INTO users (name, age) VALUES ('user2', 22);tx.RollbackTo("sp1")
// 執行SQL: ROLLBACK TO sp1;tx.Commit()
// 執行SQL: COMMIT;

參考資料

GORM 指南

Golang 中文學習文檔 - 第三方庫 - GORM

李文周的博客 - GORM入門指南

李文周的博客 - GORM CRUD指南

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

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

相關文章

從零開始學習three.js(18):一文詳解three.js中的著色器Shader

在WebGL和Three.js的3D圖形渲染中&#xff0c;著色器&#xff08;Shader&#xff09; 是實現復雜視覺效果的核心工具。通過編寫自定義的著色器代碼&#xff0c;開發者可以直接操作GPU&#xff0c;實現從基礎顏色渲染到動態光照、粒子效果等高級圖形技術。本文將深入解析Three.j…

Python函數庫調用實戰:以數據分析為例

一、引言 Python之所以在編程領域廣受歡迎&#xff0c;很大程度上得益于其豐富且強大的函數庫。這些函數庫涵蓋了從數據分析、科學計算到Web開發、機器學習等眾多領域&#xff0c;極大地提高了開發效率。本文將以數據分析為例&#xff0c;介紹如何調用Python的一些常用函數庫。…

shell腳本之條件判斷,循環控制,exit詳解

if條件語句的語法及案例 一、基本語法結構 1. 單條件判斷 if [ 條件 ]; then命令1命令2... fi2. 雙分支&#xff08;if-else&#xff09; if [ 條件 ]; then條件為真時執行的命令 else條件為假時執行的命令 fi3. 多分支&#xff08;if-elif-else&#xff09; if [ 條件1 ]…

現代 Web 自動化測試框架對比:Playwright 與 Selenium 的深度剖析

現代 Web 自動化測試框架對比&#xff1a;Playwright 與 Selenium 的深度剖析 摘要&#xff1a;本文對 Playwright 與 Selenium 在開發適配性、使用難度、場景適用性及性能表現等方面進行了全面深入的對比分析。通過詳細的技術實現細節闡述與實測數據支撐&#xff0c;為開發者…

系統架構設計(十):結構化編程

定義 結構化編程是一種遵循清晰邏輯結構、避免使用 goto 的編程方法。它強調使用有限的三種基本控制結構來組織程序&#xff0c;提高程序的可讀性、可維護性和可測試性。 它是現代程序設計的基礎&#xff0c;被廣泛應用于命令式語言&#xff08;如 C、Pascal、Java&#xff0…

TC3xx學習筆記-UCB BMHD使用詳解(二)

文章目錄 前言Confirmation的定義Dual UCB: Confirmation StatesDual UCB: Errored State or ECC Error in the UCB Confirmation CodesECC Error in the UCB ContentDual Password UCB ORIG and COPY Re-programming UCB_BMHDx_ORIG and UCB_BMHDx_COPY (x 0-3)BMHD Protecti…

OTA與boot loader

OTA指的是無線升級&#xff0c;通常用于更新設備的固件或軟件&#xff0c;用戶不用手動操作&#xff0c;非常方便。而bootloader是啟動時加載操作系統的程序&#xff0c;負責硬件初始化和啟動流程。 首先&#xff0c;OTA是如何通過bootloader工作的。OTA下載更新包后&#xff0…

實驗六:FPGA序列檢測器實驗

FPGA序列檢測器實驗(遠程實驗系統) 文章目錄 FPGA序列檢測器實驗(遠程實驗系統)一、數字電路基礎知識1. 時鐘與同步2. 按鍵消抖原理代碼講解:分頻與消抖3. 有限狀態機(FSM)設計代碼講解:狀態機編碼與轉移4. 邊沿檢測與信號同步5. 模塊化設計二、實驗數字電路整體思想三…

jenkins部署

開發者將代碼push到git運維人員通過jenkins部署&#xff0c;自動到git上pull代碼通過maven構建成jar包&#xff0c;并結合dockerfile打包成鏡像&#xff0c;push docker鏡像到docker registry通過k8s發起 發布/更新 服務 操作 通過Jenkins部署&#xff0c;自動到Git上PULL代碼 …

BBR 的 buffer 動力學觀感

這周很忙&#xff0c;今天還加了一天班&#xff0c;但還是抽空實現了五一在安徽涇縣山區喝著一壺酒寫的 BBR ProbeRTT 的想法&#xff0c;沒多少行代碼&#xff0c;它真就消除了帶寬鋸齒&#xff0c;皮了個鞋&#x1f45e;&#xff0c;昨天我還在群里說了今天再說說 BBR 的&…

第9講、深入理解Scaled Dot-Product Attention

Scaled Dot-Product Attention是Transformer架構的核心組件&#xff0c;也是現代深度學習中最重要的注意力機制之一。本文將從原理、實現和應用三個方面深入剖析這一機制。 1. 基本原理 Scaled Dot-Product Attention的本質是一種加權求和機制&#xff0c;通過計算查詢(Query…

el-tree結合checkbox實現數據回顯

組件代碼 <el-tree:data"vertiList"show-checkboxnode-key"id":props"defaultProps"ref"treeRefx"class"custom-tree"check-change"handleCheckChange"> </el-tree>獲取選擇的節點 handleCheckChan…

OpenResty 深度解析:構建高性能 Web 服務的終極方案

引言 openresty是什么&#xff1f;在我個人對它的理解來看相當于嵌入了lua的nginx; 我們在nginx中嵌入lua是為了不需要再重新編譯,我們只需要重新修改lua腳本,隨后重啟即可; 一.lua指令序列 我們分別從初始化階段&#xff0c;重寫/訪問階段&#xff0c;內容階段&#xff0c;日志…

多商戶商城系統源碼解析:開發直播電商APP的技術底層實戰詳解

隨著直播電商的火爆&#xff0c;越來越多的創業者和企業都在尋求打造自己的多商戶商城系統&#xff0c;以實現“人、貨、場”三者的深度融合。然而&#xff0c;從一個簡單的電商平臺到一個功能完善的直播電商APP&#xff0c;其技術底層架構和實現過程并非一蹴而就。本文將從架構…

桌面端進程通信

以下是關于 Electron 桌面端進程通信的基本知識點總結: 一、Electron 進程模型基礎 1. 進程類型與職責 進程類型職責權限主進程(Main)創建窗口、系統級操作、IPC中樞完全Node.js訪問權限渲染進程(Renderer)展示Web內容、UI交互默認受限(可配置開啟Node.js)預加載腳本(Prelo…

openEuler24.03 LTS下安裝MySQL8.0.42

目錄 前提步驟 刪除原有mysql及maridb數據庫 安裝MySQL 啟動MySQL 啟動查看MySQL狀態 設置MySQL開機自啟動 查看登錄密碼 登錄MySQL 修改密碼及支持遠程連接 遠程連接MySQL 前提步驟 擁有openEuler24.03 LTS環境&#xff0c;可參考&#xff1a;Vmware下安裝openEule…

idea 保證舊版本配置的同時,如何從低版本升到高版本

文章目錄 前言idea 保證舊版本配置的同時,如何從低版本升到高版本1. 備份項目2. 下載最新的idea3. 安裝安裝包4. 導入idea2019舊配置5. 驗證前言 如果您覺得有用的話,記得給博主點個贊,評論,收藏一鍵三連啊,寫作不易啊^ _ ^。 ??而且聽說點贊的人每天的運氣都不會太差,…

填坑記: 古董項目Apache POI 依賴異常排除

當你看到NoSuchMethodError的時候&#xff0c;不要慌&#xff0c;深呼吸&#xff0c;這可能只是JAR包版本的問題… 引子&#xff1a;一個平靜的周二下午 那是一個看似平常的周二下午&#xff0c;系統運行良好&#xff0c;開發團隊在有條不紊地推進著新功能的開發。突然&#x…

CAPL Class: TcpSocket (此類用于實現 TCP 網絡通信 )

目錄 Class: TcpSocketacceptopenclosebindconnectgetLastSocketErrorgetLastSocketErrorAsStringlistenreceivesendsetSocketOptionshutdown函數調用的基本流程服務器端的基本流程客戶端的基本流程Class: TcpSocket學習筆記。來自CANoe幫助文檔。 Class: TcpSocket accept /…

微信小程序的開發及問題解決

HttpClient 測試例子 SpringBootTest public class HttpClientTest {/*** 測試通過httpclient發送get方式的請求*/Testpublic void testGET() throws IOException {//創建httpclient對象CloseableHttpClient httpClient HttpClients.createDefault();//創建請求對象HttpGet ht…