胖sir :接著,給你一個餡餅兒
兵長 : 來嘞!!
一篇來自ORM的整理筆記…
1 什麼是ORM?為什麼要⽤ORM?
什麼是ORM ,即Object-Relationl Mapping,它的作⽤是在關係型資料庫和物件之間作⼀個對映,
這樣,我們在具體的 運算元據庫的時候,就不需要再去和複雜的SQL語句打交道,只要像平時操作物件⼀樣操作它就可以了 。
ORM解決的主要問題是物件關係的對映。域模型和關係模型分別是建⽴在概念模型的基礎上的。
- 域模型是⾯向對 象的
- 關係模型是⾯向關係的
⼀般情況下,⼀個持久化類和⼀個表對應,類的每個例項對應表中的⼀條記錄,
類的每個屬性對應表的每個欄位。
ORM技術特點:
提⾼了開發效率。
由於ORM可以⾃動對Entity物件與資料庫中的Table進⾏欄位與屬性的對映,所以我們實際 可能已經不需要⼀個專⽤的、龐⼤的資料訪問層。
ORM提供了對資料庫的對映,不⽤sql直接編碼,能夠像操作物件⼀樣從資料庫獲取資料。
ORM的缺點
ORM的缺點是會犧牲程式的執⾏效率和會固定思維模式。
從系統結構上來看,採⽤ORM的系統⼀般都是多層系統,系統的層次多了,效率就會降低。ORM是⼀種完全的 ⾯向物件的做法,⽽⾯向物件的做法也會對效能產⽣⼀定的影響。
2 ORM運算元據
package main
import (
"fmt"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
)
// UserInfo 使用者資訊
//orm會預設根據結構體建立table , orm採用的是linux命名方式 即小寫加下劃線,且會在名字後面加 s
//會建立user_infos 表
type UserInfo struct {
gorm.Model
ID uint
Name string
Gender string
Hobby string
}
// truncate table 表名
// 資料庫需要提前建立好 例如mygorm
// parseTime是查詢結果是否⾃動解析為時間
// loc是MySQL的時區設定
// charset是編碼方式
func main() {
fmt.Println("try open mysql connection....")
db, err := gorm.Open("mysql", "root:123456@(localhost:3306)/mygorm?charset=utf8mb4&parseTime=True&loc=Local")
if err != nil {
panic(err)
}
fmt.Println("successful")
defer db.Close()
// 自動遷移
//若該表不存在則建立該表,若該表存在且結構體發生變化則更新表結構
db.AutoMigrate(&UserInfo{})
u1 := UserInfo{gorm.Model{}, 1, "xiaozhu", "man", "playing"}
// 建立記錄
result := db.Create(&u1)
fmt.Println("result:", result.RowsAffected)
// 查詢
var u = new(UserInfo)
//查詢一條記錄
db.First(u)
fmt.Printf("First: %#v\n", u)
//按照條件查詢
var uu UserInfo
db.Find(&uu, "name=?", "xiaozhu")
fmt.Printf("Find: %#v\n", uu)
// 更新
db.Model(&uu).Update("hobby", "sing")
// 刪除 , 此處刪除記錄,是不會將資料表中的資料刪除掉,而是deleted_at 會更新刪除時間
db.Delete(&uu)
}
- 使用gorm必須要先建立好資料庫
- gorm會自動建立資料表,且表結構可以動態變化
- gorm建立的表命名方式為 程式碼中結構體命名的轉換, 例如 結構體命名為UserInfo,則table會命名為user_infos
- gorm修改表結構非常的容易
- gorm是完全物件導向的思想
3 模型定義
模型是標準的 struct,由 Go 的基本資料型別、實現了 Scanner 和 Valuer 介面的自定義型別及其指標或別名組成
例如:
type User struct {
ID uint
Name string
Email *string
Age uint8
Birthday *time.Time
MemberNumber sql.NullString
ActivatedAt sql.NullTime
CreatedAt time.Time
UpdatedAt time.Time
}
自定義模型
遵循 GORM 已有的約定,可以減少您的配置和程式碼量。
type User struct {
gorm.Model // 內嵌
Name string
Age sql.NullInt64
Birthday *time.Time
Email string `gorm:"type:varchar(100);uniqueIndex"`
Role string `gorm:"size:255"` // 設定欄位大小為255
MemberNumber *string `gorm:"unique;not null"` // 設定會員號(member number)唯一併且不為空
Num int `gorm:"AUTO_INCREMENT"` // 設定 num 為自增型別
Address string `gorm:"index:addr"` // 給address欄位建立名為addr的索引
IgnoreMe int `gorm:"-"` // 忽略本欄位
}
欄位標籤
宣告 model 時,tag 是可選的,GORM 支援以下 tag: tag 名大小寫不敏感,但建議使用 camelCase
風格
標籤名 | 說明 |
---|---|
column | 指定 db 列名 |
type | 列資料型別,推薦使用相容性好的通用型別,例如:所有資料庫都支援 bool、int、uint、float、string、time、bytes 並且可以和其他標籤一起使用,例如:not null 、size , autoIncrement … 像 varbinary(8) 這樣指定資料庫資料型別也是支援的。在使用指定資料庫資料型別時,它需要是完整的資料庫資料型別,如:MEDIUMINT UNSIGNED not NULL AUTO_INSTREMENT |
size | 指定列大小,例如:size:256 |
primaryKey | 指定列為主鍵 |
unique | 指定列為唯一 |
default | 指定列的預設值 |
precision | 指定列的精度 |
scale | 指定列大小 |
not null | 指定列為 NOT NULL |
autoIncrement | 指定列為自動增長 |
autoIncrementIncrement | 自動步長,控制連續記錄之間的間隔 |
embedded | 巢狀欄位 |
embeddedPrefix | 嵌入欄位的列名字首 |
autoCreateTime | 建立時追蹤當前時間,對於 int 欄位,它會追蹤秒級時間戳,您可以使用 nano /milli 來追蹤納秒、毫秒時間戳,例如:autoCreateTime:nano |
autoUpdateTime | 建立/更新時追蹤當前時間,對於 int 欄位,它會追蹤秒級時間戳,您可以使用 nano /milli 來追蹤納秒、毫秒時間戳,例如:autoUpdateTime:milli |
index | 根據引數建立索引,多個欄位使用相同的名稱則建立複合索引,檢視 索引 獲取詳情 |
uniqueIndex | 與 index 相同,但建立的是唯一索引 |
check | 建立檢查約束,例如 check:age > 13 ,檢視 約束 獲取詳情 |
<- | 設定欄位寫入的許可權, <-:create 只建立、<-:update 只更新、<-:false 無寫入許可權、<- 建立和更新許可權 |
-> | 設定欄位讀的許可權,->:false 無讀許可權 |
- | 忽略該欄位,- 無讀寫許可權 |
comment | 遷移時為欄位新增註釋 |
關聯標籤
標籤 | 描述 |
---|---|
foreignKey | 指定當前模型的列作為連線表的外來鍵 |
references | 指定引用表的列名,其將被對映為連線表外來鍵 |
polymorphic | 指定多型型別,比如模型名 |
polymorphicValue | 指定多型值、預設表名 |
many2many | 指定連線表表名 |
joinForeignKey | 指定連線表的外來鍵列名,其將被對映到當前表 |
joinReferences | 指定連線表的外來鍵列名,其將被對映到引用表 |
constraint | 關係約束,例如:OnUpdate 、OnDelete |
4 主鍵、表名、列名的約定
主鍵(Primary Key)
GORM 預設會使⽤名為ID的欄位作為表的主鍵。
type User struct {
ID string // 名為`ID`的欄位會預設作為表的主鍵
Name string
}
// 使⽤`AnimalID`作為主鍵
type Animal struct {
AnimalID int64 `gorm:"primary_key"`
Name string
Age 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 co lumn 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 欄位為當前時間,⽽
不是直接將記錄從資料庫中刪除。
5 CURD
建立記錄
user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}
result := db.Create(&user) // 通過資料的指標來建立
user.ID // 返回插入資料的主鍵
result.Error // 返回 error
result.RowsAffected // 返回插入記錄的條數
批量插入
要有效地插入大量記錄,請將一個 slice
傳遞給 Create
方法。 將切片資料傳遞給 Create 方法,GORM 將生成一個單一的 SQL 語句來插入所有資料,並回填主鍵的值,鉤子方法也會被呼叫。
var users = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}, {Name: "jinzhu3"}}
db.Create(&users)
for _, user := range users {
user.ID // 1,2,3
}
使用 CreateInBatches
建立時,你還可以指定建立的數量,例如:
var users = []User{{name: "jinzhu_1"}, ...., {Name: "jinzhu_10000"}}
// 數量為 100
db.CreateInBatches(users, 100)
預設值
您可以通過標籤 default
為欄位定義預設值,如:
type User struct {
ID int64
Name string `gorm:"default:galeone"`
Age int64 `gorm:"default:18"`
}
插入記錄到資料庫時,預設值 會被用於 填充值為 零值 的欄位
查詢
檢索單個物件
GORM 提供了 First
、Take
、Last
方法,以便從資料庫中檢索單個物件。當查詢資料庫時它新增了 LIMIT 1
條件,且沒有找到記錄時,它會返回 ErrRecordNotFound
錯誤
// 獲取第一條記錄(主鍵升序)
db.First(&user)
// SELECT * FROM users ORDER BY id LIMIT 1;
// 獲取一條記錄,沒有指定排序欄位
db.Take(&user)
// SELECT * FROM users LIMIT 1;
// 獲取最後一條記錄(主鍵降序)
db.Last(&user)
// SELECT * FROM users ORDER BY id DESC LIMIT 1;
result := db.First(&user)
result.RowsAffected // 返回找到的記錄數
result.Error // returns error
// 檢查 ErrRecordNotFound 錯誤
errors.Is(result.Error, gorm.ErrRecordNotFound)
如果你想避免
ErrRecordNotFound
錯誤,你可以使用Find
,比如db.Limit(1).Find(&user)
,Find
方法可以接受struct和slice的資料。
First
, Last
方法將按主鍵排序查詢第一/最後一條記錄,只有在用struct查詢或提供model value時才有效,如果當前model沒有定義主鍵,將按第一個欄位排序,例如:
var user User
var users []User
// 可以
db.First(&user)
// SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1
// 可以
result := map[string]interface{}{}
db.Model(&User{}).First(&result)
// SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1
// 不行
result := map[string]interface{}{}
db.Table("users").First(&result)
// 但可以配合 Take 使用
result := map[string]interface{}{}
db.Table("users").Take(&result)
// 根據第一個欄位排序
type Language struct {
Code string
Name string
}
db.First(&Language{})
// SELECT * FROM `languages` ORDER BY `languages`.`code` LIMIT 1
用主鍵檢索
如果主鍵是數值型別,也可以通過 內聯條件 傳入主鍵來檢索物件。如果主鍵是 string 型別,要小心避免 SQL 注入,檢視 安全 獲取詳情
db.First(&user, 10)
// SELECT * FROM users WHERE id = 10;
db.First(&user, "10")
// SELECT * FROM users WHERE id = 10;
db.Find(&users, []int{1,2,3})
// SELECT * FROM users WHERE id IN (1,2,3);
如果主鍵是像 uuid 這樣的字串,您需要這要寫:
db.First(&user, "id = ?", "1b74413f-f3b8-409f-ac47-e8c062e3472a")
// SELECT * FROM users WHERE id = "1b74413f-f3b8-409f-ac47-e8c062e3472a";
檢索全部物件
// 獲取全部記錄
result := db.Find(&users)
// SELECT * FROM users;
result.RowsAffected // 返回找到的記錄數,相當於 `len(users)`
result.Error // returns error
條件
String 條件
// 獲取第一條匹配的記錄
db.Where("name = ?", "jinzhu").First(&user)
// SELECT * FROM users WHERE name = 'jinzhu' ORDER BY id LIMIT 1;
// 獲取全部匹配的記錄
db.Where("name <> ?", "jinzhu").Find(&users)
// SELECT * FROM users WHERE name <> 'jinzhu';
// IN
db.Where("name IN ?", []string{"jinzhu", "jinzhu 2"}).Find(&users)
// SELECT * FROM users WHERE name IN ('jinzhu','jinzhu 2');
// LIKE
db.Where("name LIKE ?", "%jin%").Find(&users)
// SELECT * FROM users WHERE name LIKE '%jin%';
// AND
db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;
// Time
db.Where("updated_at > ?", lastWeek).Find(&users)
// SELECT * FROM users WHERE updated_at > '2000-01-01 00:00:00';
// BETWEEN
db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)
// SELECT * FROM users WHERE created_at BETWEEN '2000-01-01 00:00:00' AND '2000-01-08 00:00:00';
Struct & Map 條件
// Struct
db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 20 ORDER BY id LIMIT 1;
// Map
db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&users)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 20;
// 主鍵切片條件
db.Where([]int64{20, 21, 22}).Find(&users)
// SELECT * FROM users WHERE id IN (20, 21, 22);
注意 當使用結構作為條件查詢時,GORM 只會查詢非零值欄位。這意味著如果您的欄位值為
0
、''
、false
或其他 零值,該欄位不會被用於構建查詢條件,例如:
db.Where(&User{Name: "jinzhu", Age: 0}).Find(&users)
// SELECT * FROM users WHERE name = "jinzhu";
你可以使用 map 來構建查詢條件,它會使用所有的值,例如:
db.Where(map[string]interface{}{"Name": "jinzhu", "Age": 0}).Find(&users)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 0;
或檢視 指定結構體查詢欄位 獲取詳情
指定結構體查詢欄位
當使用結構體進行查詢時,你可以使用它的欄位名或其 dbname 列名作為引數來指定查詢的欄位,例如:
db.Where(&User{Name: "jinzhu"}, "name", "Age").Find(&users)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 0;
db.Where(&User{Name: "jinzhu"}, "Age").Find(&users)
// SELECT * FROM users WHERE age = 0;
內聯條件
用法與 Where
類似
// SELECT * FROM users WHERE id = 23;
// 根據主鍵獲取記錄,如果是非整型主鍵
db.First(&user, "id = ?", "string_primary_key")
// SELECT * FROM users WHERE id = 'string_primary_key';
// Plain SQL
db.Find(&user, "name = ?", "jinzhu")
// SELECT * FROM users WHERE name = "jinzhu";
db.Find(&users, "name <> ? AND age > ?", "jinzhu", 20)
// SELECT * FROM users WHERE name <> "jinzhu" AND age > 20;
// Struct
db.Find(&users, User{Age: 20})
// SELECT * FROM users WHERE age = 20;
// Map
db.Find(&users, map[string]interface{}{"age": 20})
// SELECT * FROM users WHERE age = 20;
Not 條件
構建 NOT 條件,用法與 Where
類似
db.Not("name = ?", "jinzhu").First(&user)
// SELECT * FROM users WHERE NOT name = "jinzhu" ORDER BY id LIMIT 1;
// Not In
db.Not(map[string]interface{}{"name": []string{"jinzhu", "jinzhu 2"}}).Find(&users)
// SELECT * FROM users WHERE name NOT IN ("jinzhu", "jinzhu 2");
// Struct
db.Not(User{Name: "jinzhu", Age: 18}).First(&user)
// SELECT * FROM users WHERE name <> "jinzhu" AND age <> 18 ORDER BY id LIMIT 1;
// 不在主鍵切片中的記錄
db.Not([]int64{1,2,3}).First(&user)
// SELECT * FROM users WHERE id NOT IN (1,2,3) ORDER BY id LIMIT 1;
Or 條件
db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&users)
// SELECT * FROM users WHERE role = 'admin' OR role = 'super_admin';
// Struct
db.Where("name = 'jinzhu'").Or(User{Name: "jinzhu 2", Age: 18}).Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' OR (name = 'jinzhu 2' AND age = 18);
// Map
db.Where("name = 'jinzhu'").Or(map[string]interface{}{"name": "jinzhu 2", "age": 18}).Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' OR (name = 'jinzhu 2' AND age = 18);
您還可以檢視高階查詢中的 分組條件,它被用於編寫複雜 SQL
選擇特定欄位
選擇您想從資料庫中檢索的欄位,預設情況下會選擇全部欄位
db.Select("name", "age").Find(&users)
// SELECT name, age FROM users;
db.Select([]string{"name", "age"}).Find(&users)
// SELECT name, age FROM users;
db.Table("users").Select("COALESCE(age,?)", 42).Rows()
// SELECT COALESCE(age,'42') FROM users;
還可以看一看 智慧選擇欄位
Order
指定從資料庫檢索記錄時的排序方式
db.Order("age desc, name").Find(&users)
// SELECT * FROM users ORDER BY age desc, name;
// 多個 order
db.Order("age desc").Order("name").Find(&users)
// SELECT * FROM users ORDER BY age desc, name;
db.Clauses(clause.OrderBy{
Expression: clause.Expr{SQL: "FIELD(id,?)", Vars: []interface{}{[]int{1, 2, 3}}, WithoutParentheses: true},
}).Find(&User{})
// SELECT * FROM users ORDER BY FIELD(id,1,2,3)
Limit & Offset
Limit
指定獲取記錄的最大數量 Offset
指定在開始返回記錄之前要跳過的記錄數量
db.Limit(3).Find(&users)
// SELECT * FROM users LIMIT 3;
// 通過 -1 消除 Limit 條件
db.Limit(10).Find(&users1).Limit(-1).Find(&users2)
// SELECT * FROM users LIMIT 10; (users1)
// SELECT * FROM users; (users2)
db.Offset(3).Find(&users)
// SELECT * FROM users OFFSET 3;
db.Limit(10).Offset(5).Find(&users)
// SELECT * FROM users OFFSET 5 LIMIT 10;
// 通過 -1 消除 Offset 條件
db.Offset(10).Find(&users1).Offset(-1).Find(&users2)
// SELECT * FROM users OFFSET 10; (users1)
// SELECT * FROM users; (users2)
檢視 Pagination 學習如何寫一個分頁器
Group & Having
type result struct {
Date time.Time
Total int
}
db.Model(&User{}).Select("name, sum(age) as total").Where("name LIKE ?", "group%").Group("name").First(&result)
// SELECT name, sum(age) as total FROM `users` WHERE name LIKE "group%" GROUP BY `name`
db.Model(&User{}).Select("name, sum(age) as total").Group("name").Having("name = ?", "group").Find(&result)
// SELECT name, sum(age) as total FROM `users` GROUP BY `name` HAVING name = "group"
rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Rows()
for rows.Next() {
...
}
rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Rows()
for rows.Next() {
...
}
type Result struct {
Date time.Time
Total int64
}
db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Scan(&results)
Distinct
從模型中選擇不相同的值
db.Distinct("name", "age").Order("name, age desc").Find(&results)
Distinct
也可以配合 Pluck
, Count
使用
Joins
指定 Joins 條件
type result struct {
Name string
Email string
}
db.Model(&User{}).Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&result{})
// SELECT users.name, emails.email FROM `users` left join emails on emails.user_id = users.id
rows, err := db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Rows()
for rows.Next() {
...
}
db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&results)
// 帶引數的多表連線
db.Joins("JOIN emails ON emails.user_id = users.id AND emails.email = ?", "jinzhu@example.org").Joins("JOIN credit_cards ON credit_cards.user_id = users.id").Where("credit_cards.number = ?", "411111111111").Find(&user)
Joins 預載入
您可以使用 Joins
實現單條 SQL 預載入關聯記錄,例如:
db.Joins("Company").Find(&users)
// SELECT `users`.`id`,`users`.`name`,`users`.`age`,`Company`.`id` AS `Company__id`,`Company`.`name` AS `Company__name` FROM `users` LEFT JOIN `companies` AS `Company` ON `users`.`company_id` = `Company`.`id`;
Scan
Scan 結果至 struct,用法與 Find
類似
type Result struct {
Name string
Age int
}
var result Result
db.Table("users").Select("name", "age").Where("name = ?", "Antonio").Scan(&result)
// 原生 SQL
db.Raw("SELECT name, age FROM users WHERE name = ?", "Antonio").Scan(&result)
處理錯誤
GORM 的錯誤處理與常見的 Go 程式碼不同,因為 GORM 提供的是鏈式 API。
如果遇到任何錯誤,GORM 會設定 *gorm.DB
的 Error
欄位,您需要像這樣檢查它:
if err := db.Where("name = ?", "jinzhu").First(&user).Error; err != nil {
// 處理錯誤...
}
或者
if result := db.Where("name = ?", "jinzhu").First(&user); result.Error != nil {
// 處理錯誤...
}
ErrRecordNotFound
當 First
、Last
、Take
方法找不到記錄時,GORM 會返回 ErrRecordNotFound
錯誤。如果發生了多個錯誤,你可以通過 errors.Is
判斷錯誤是否為 ErrRecordNotFound
,例如:
// 檢查錯誤是否為 RecordNotFound
err := db.First(&user, 100).Error
errors.Is(err, gorm.ErrRecordNotFound)
技術是開放的,我們的心態,更應是開放的。擁抱變化,向陽而生,努力向前行。
更多高階用法和細節 可以檢視 中文GROM網站
作者:小魔童哪吒
本作品採用《CC 協議》,轉載必須註明作者和本文連結