intro
package mainimport ("gorm.io/gorm""gorm.io/driver/sqlite" // GORM 使用該驅動來連接和操作 SQLite 數據庫。
)type Product struct {gorm.Model // 嵌入GORM 內置的模型結構,包含 ID、CreatedAt、UpdatedAt、DeletedAt 四個字段Code stringPrice uint
}func main() {db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{}) // 使用 SQLite 驅動打開名為 "test.db" 的數據庫文件,并用默認配置初始化 GORMif err != nil {panic("failed to connect database")}// 遷移 schemadb.AutoMigrate(&Product{}) // 根據 Product 結構體自動創建或更新數據庫中的表結構,確保數據表與結構體一致。// Create 在數據庫中插入一條新記錄db.Create(&Product{Code: "D42", Price: 100})// Readvar product Product // 用來存儲查詢結果db.First(&product, 1) // 根據整型主鍵查找db.First(&product, "code = ?", "D42") // 通過條件查找,查找 code 字段值為 D42 的記錄// Update - 將 product 的 price 更新為 200db.Model(&product).Update("Price", 200)// Update - 更新多個字段// 使用結構體 Product 作為參數更新多個字段,此方式只會更新結構體中非零值的字段。db.Model(&product).Updates(Product{Price: 200, Code: "F42"}) // 僅更新非零值字段// 使用 map 更新字段,可以同時更新多個字段,不受非零值的限制db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"})// Delete - 刪除 productdb.Delete(&product, 1)
}
gorm.Config{}
:這是一個結構體字面量,用于創建一個gorm.Config
類型的實例,其中的各個字段都被賦予了默認的零值(如果沒有顯式指定)。&
運算符:在結構體字面量前加&
,表示取這個實例的地址,也就是生成一個指向gorm.Config
實例的指針。- 傳遞指針而非值,可以使函數
gorm.Open
修改或讀取配置,同時也避免了結構體值的拷貝。
數據庫遷移(schema migration)
- 定義:數據庫遷移是指對數據庫表結構進行創建、修改或刪除的過程。
- 自動遷移:GORM 提供了
AutoMigrate
方法,可以根據你的 Go 結構體(模型)自動創建或更新數據庫中的表結構。- 示例:
db.AutoMigrate(&User{})
會檢查User
模型,并自動生成或調整表結構來匹配這個模型。
- 示例:
- 優點:
- 讓你無需手動編寫 SQL 語句來創建或修改表結構。
- 保證模型和數據庫結構保持一致。
- 局限性:
- 對于復雜的結構變更(如大規模數據遷移、索引優化等),可能需要手動編寫遷移腳本。
結構體標簽和反射
-
結構體標簽:了解 Go 中標簽的語法和使用場景,特別是如何利用標簽為字段添加元信息。
- 概念:在 Go 中,你可以在結構體字段后面加上反引號(```)內的標簽,這些標簽是一些字符串,用來存儲額外的信息。
- 用途:
- 用于告訴 GORM 如何將這個字段映射到數據庫中的列,比如指定列名、數據類型、約束條件等。
- 也常見于 JSON 序列化,告訴程序如何將字段轉換為 JSON 格式。
type User struct {Name string `gorm:"column:user_name;type:varchar(100);not null" json:"name"`Email string `gorm:"unique;not null" json:"email"` } // Name 字段在數據庫中將映射為 user_name 列,類型為 varchar(100) 且不能為空 // 同時在 JSON 中使用 name 作為鍵
-
反射機制:雖然不必深入,但需要了解反射是如何在運行時讀取結構體標簽并據此執行操作的。
-
反射是 Go 語言的一種特性,它允許程序在運行時檢查變量的類型、結構以及標簽等信息。
-
用途:
- GORM 就是通過反射讀取結構體標簽,從而了解如何把結構體字段和數據庫表的列對應起來。
- 反射可以動態地獲取類型信息,使得代碼更靈活,但同時也會帶來一定的性能開銷。
import ("fmt""reflect" )type User struct {Name string `gorm:"column:user_name" json:"name"` }func main() {user := User{Name: "Alice"}t := reflect.TypeOf(user)field, _ := t.FieldByName("Name")fmt.Println("GORM標簽:", field.Tag.Get("gorm"))fmt.Println("JSON標簽:", field.Tag.Get("json")) }
這段代碼利用反射獲取了
User
結構體中Name
字段的gorm
和json
標簽信息。
-
time.Time
的基本用法和常見操作
GORM 會自動處理時間戳字段。
獲取當前時間:now := time.Now()// 使用 Format 方法將時間轉換為字符串
formatted := now.Format("2006-01-02 15:04:05")// 使用 time.Parse 將字符串轉換為 time.Time 類型
t, err := time.Parse("2006-01-02", "2025-04-06")// 比較時間:可以使用 Before、After、Equal 等方法比較兩個時間的先后。
模型定義
模型定義
- 映射機制:GORM 將 Go 語言中的結構體(struct)映射為數據庫中的表。定義模型就是編寫對應的結構體。
- 字段類型:模型字段可以使用基本類型(如
uint
、string
、uint8
)、指針(如*string
、*time.Time
)以及特殊類型(如sql.NullString
、sql.NullTime
)來處理可空值。 - 自動管理的時間戳:如果定義了
CreatedAt
和UpdatedAt
字段,GORM 會在創建和更新記錄時自動填充當前時間。 - 非導出字段:結構體中首字母小寫的字段不會映射到數據庫中。
內置模型(gorm.Model)
- GORM 提供了預定義結構體
gorm.Model
,包含了常用的字段:ID
(主鍵)CreatedAt
(創建時間)UpdatedAt
(更新時間)DeletedAt
(軟刪除字段,支持索引)
- 嵌入
gorm.Model
可以讓你快速擁有一套標準的字段。
約定與自動行為
-
主鍵約定:默認使用名為
ID
的字段作為主鍵。 -
表名和列名:GORM 會自動將結構體名轉換為 snake_case 的復數形式(例如
User
變為表名users
),而字段名也轉換為 snake_case。 -
自動時間戳:除了自動填充
CreatedAt
與UpdatedAt
外,還支持通過標簽(如autoCreateTime
、autoUpdateTime
)自定義時間精度(秒、毫秒、納秒)。type User struct {CreatedAt time.Time // 在創建時,如果該字段值為零值,則使用當前時間填充UpdatedAt int // 在創建時該字段值為零值或者在更新時,使用當前時間戳秒數填充Updated int64 `gorm:"autoUpdateTime:nano"` // 使用時間戳納秒數填充更新時間Updated int64 `gorm:"autoUpdateTime:milli"` // 使用時間戳毫秒數填充更新時間Created int64 `gorm:"autoCreateTime"` // 使用時間戳秒數填充創建時間 }
標簽和字段配置
-
字段級控制:使用標簽可以設置字段的數據庫列名、數據類型、大小、默認值以及約束(如非空、唯一、索引等)。
type Product struct {ID uint `gorm:"primaryKey"` // 主鍵字段Code string `gorm:"column:product_code;type:varchar(100);not null;unique;index"` // 指定列名為 product_code,數據類型為 varchar(100),非空、唯一并創建索引Price uint `gorm:"default:0;not null"` // 默認值為 0,且不允許為空Description string `gorm:"type:text;default:'no description'"` // 指定數據類型為 text,并設置默認描述 }
- column:將
Code
字段映射到數據庫的product_code
列。 - type:設置數據庫中的數據類型。
- not null、unique、index:添加非空、唯一和索引約束。
- default:指定默認值。
- column:將
-
權限控制:標簽還支持配置字段在 CRUD 操作中的讀寫權限(例如只創建、只更新或完全忽略)。
type User struct {Name string `gorm:"<-:create"` // 允許讀和創建Name string `gorm:"<-:update"` // 允許讀和更新Name string `gorm:"<-"` // 允許讀和寫(創建和更新)Name string `gorm:"<-:false"` // 允許讀,禁止寫Name string `gorm:"->"` // 只讀(除非有自定義配置,否則禁止寫)Name string `gorm:"->;<-:create"` // 允許讀和寫Name string `gorm:"->:false;<-:create"` // 僅創建(禁止從 db 讀)Name string `gorm:"-"` // 通過 struct 讀寫會忽略該字段Name string `gorm:"-:all"` // 通過 struct 讀寫、遷移會忽略該字段Name string `gorm:"-:migration"` // 通過 struct 遷移會忽略該字段 }
-
理解箭頭方向
-
<-
表示寫入(寫操作)數據從外部傳入數據庫時走
<-
。(意味著在寫入操作(無論創建或更新)時允許該字段參與。) -
->
表示讀取(讀操作)數據從數據庫中輸出時走
->
。(意味著在讀取操作時允許該字段參與。)
-
-
后綴部分(如
:create
、:update
、false
)用于進一步限制操作時機:<-[操作]
限定寫入操作<-:create
:僅在創建時寫入,更新時不允許寫入。<-:update
:僅在更新時寫入,創建時不允許寫入。<-
:創建和更新都允許寫入。<-:false
:禁止寫入,相當于只讀。
>[操作]
限定讀取操作>
:只允許讀取(寫入禁止)。>:false
:禁止讀取(但可能允許寫入,具體配合<-
說明)。
-
組合寫法
>;<-:create
:讀取始終允許,但寫入僅在創建時允許;更新時不能寫。>:false;<-:create
:禁止從數據庫中讀取(>:false
),但在創建時可以寫入。
-
忽略字段的寫法
"-"
:完全忽略該字段,既不讀也不寫。"-:all"
:在所有操作(讀、寫、遷移)中都忽略。"-:migration"
:僅在遷移操作時忽略該字段。
-
-
嵌入結構體:通過匿名嵌入或使用
embedded
標簽,可以將一個結構體的字段直接嵌入到父結構體中,并且可以用embeddedPrefix
添加前綴。type Address struct {City stringState string }type Customer struct {ID uint `gorm:"primaryKey"`Name string// 匿名嵌入 Address 結構體,其字段會直接映射到 Customer 表中Address// 使用 embedded 標簽嵌入 Address 結構體,并添加前綴 contact_Contact Address `gorm:"embedded;embeddedPrefix:contact_"` }
高級選項
- 除了基礎的映射和約定,GORM 允許使用序列化標簽(
serializer
)來自定義數據的存儲和讀取方式,增強靈活性。 - 對于字段更新的細粒度控制,如僅更新非零值字段、使用 map 或結構體進行批量更新,均由標簽和方法調用來實現。