go語言:GORM
阿新 • • 發佈:2020-08-02
7.GORM
-
物件關係對映。
資料表 -> 結構體 資料行 -> 結構體例項 欄位 -> 結構體欄位
-
優點:提高開發效率,缺點犧牲執行效能,靈活性,弱化SQL能力。
-
gorm下載: 官網
go get -u github.com/jinzhu/gorm
7.1資料庫的連線
// 引包 import ( _ "github.com/jinzhu/gorm/dialects/mysql" "github.com/jinzhu/gorm" ) // 結構體建立 type UserInfo struct { Id uint Name string Gender string Hobby string }
- 連線資料庫
// 連線資料庫 這裡指定編碼格式,可解析時間型別,時間取當地時間
db, err :=gorm.Open("mysql", "root:123@(ip:3306)/db1?charset=utf8mb4&parseTime=True&loc=Local")
if err != nil{
panic(err)
}
// 關閉連線
defer db.Close()
7.2資料庫遷移和最簡單CURD
- 建立表 自動遷移。讓結構體和資料表形成對應關係
db.AutoMigrate(&UserInfo{})
- 插入一條資料
/ 建立資料行
user1 := UserInfo{1,"jk","man","籃球"}
db.Create(&user1)
- 查詢第一條資料
var u UserInfo
db.First(&u)
fmt.Println(u)
- 更新一條資料
var u UserInfo
// 查詢到第一條資料,然後更新
db.First(&u)
db.Model(&u).Update("hobby","running")
- 刪除操作
db.Delete(&u)
7.3 GORM模型定義
- GORM 內建一個gorm.Model結構體,gorm.Model是一個包含了ID,CreteAt,UpdateAt,DeleteAt四個字端的Golang結構體。
type Model struct {
ID uint `gorm:"primary_key"`
CreateAt time.Time
UpdateAt time.Time
DeleteAt *time.Time
}
- 你可以自行將它嵌入到你自己模型中:
type User struct {
gorm.Model
Name string
}
- 模型定義示例
type User struct{
gorm.Model// 內嵌Model
Name string
Age sql.NullInt64//零值型別
Birthday *time.Time
Email string `gorm:"type:varchar(100);unique_index"`
Role string `gorm:"size:255"`// 設定字端大小255
MemberNumber string `gorm:"unique;not null"`// 設定會員號,唯一且不為空
Num int `gorm:"AUTO_INCREMENT"`//設定num為自增型別
Address string `gorm:"index:addr"`// 給address設定索引為addr
IgnoreMe int `gorm:"-"`//忽略本字端
}
- 自定義欄位為主鍵
type Animal struct {
AnimalID int64 `gorm:"primary_key"`
Name string
}
- 表的名字預設是結構體複數,你也可以自定義表的名字
type Animal struct {
AnimalID int64 `gorm:"primary_key"`
Name string
}
// 自定義表名字,因唯一指定表名,不會受方法影響
func (Animal) TableName() string{
return "self_animal"
}
- 禁用表的複數形式
db.SingularTable(true)
- 強行指定表名
// 使User結構體建立名叫 mysql_self_name表
db.Table("mysql_self_name").CreateTable(&User{})
- 通過自定義方法設計表名
gorm.DefaultTableNameHandle = func (db *gorm.DB, defaultTableName string) string{
return "SMS_" + defaultTableName
}
// 給建立的預設表名,加字首SMS
- 自定義列的名稱
type User struct {
Age sql.NullInt64 `gorm:"column:user_age"`
}
// 會新增一個user_age字端。
-
時間型別
CreatedAt
db.Create(&user)
db.Model(&user).Update("CreatedAt", time.Now())
UpdatedAt
db.Save(&user)
db.Model(&user).Update("name","xjk")
DeletedAt
- 呼叫刪除記錄,將會設定DeletedAt字端為當前時間,而不是直接將記錄從資料庫中刪除。
7.4 GORM 的CURD
- 首先定義一個模型
type uGroup struct {
Id int64
Name string `gorm:"defalut:'wang'"`
Age int64
}
7.4.1建立記錄
- NewRecord
var u = type uGroup struct {
Id int64
Name string `gorm:"defalut:'wang'"`
Age int64
}{Name:"laobing2", Age:77}
pk := db.NewRecord(&u)
fmt.Println(pk)// true
// NewRecord 查詢當主鍵為空返回true
// 建立使用者
db.Create(&u)
pk2 := db.NewRecord(u)
// 此時使用者已建立,已經有主鍵
fmt.Println(pk2)// false
7.4.2 預設值
- 通過tag定義欄位的預設值。
type uGroup struct {
Id int64
Name string `gorm:"default:'ss'"`
Age int64
}
- 值得注意是,在建立記錄生成SQL語句會排除沒有值或值為零的欄位。在將記錄插入到資料庫後,Gorm會從資料庫載入到那些字端預設值。
var u = uGroup{Name:"",Age:99}
db.Create(&u)
- 所有欄位零值,如: 0 "" false 或者其他零值。都不會儲存到資料庫中,但會使用他們預設值。若果想要避免此情況,可以考慮使用指標.
- 方式1:指標方式實現
type uGroup struct {
Id int64
Name *string `gorm:"default:'ss'"`
Age int64
}
var u = uGroup{Name:new(string), Age:77}
db.Create(&u)
- 方式2:Scanner/Valuer介面方式
type uGroup struct {
Id int64
Name sql.NullString `gorm:"default:'ss'"`
Age int64
}
var u = uGroup{Name:sql.NullString{"", true}, Age:55}
db.Create(&u)
7.4.3普通查詢
- 一般查詢
var u uGroup
// 根據主鍵查詢
db.First(&u)
// 獲取第一條記錄
db.Take(&u)
// 根據主鍵查詢最後一條記錄
db.Last(&u)
// 查詢所有記錄
var us []uGroup
db.Find(&us)
// 查詢主鍵id=3記錄
var u uGroup
db.First(&u, 3)
7.4.4 Where查
- 普通SQL查詢
var u uGroup
// 查詢name=ss 的第一條記錄
db.Where("name=?","ss").First(&u)
// 查詢 所有滿足name=ss的記錄
var us []uGroup
db.Where("name=?","ss").Find(&us)
// 查詢 name <>
db.Where("name <> ?","ss").Find(&us)
// IN 查詢
db.Where("name IN (?)", []string{"ss","jj"}).Find(&us)
// LIKE
db.Where("name LIKE ?","%ss%").Find(&us)
// AND
db.Where("name = ? AND age >= ?", "ss", "25").Find(&us)
// 時間Time
db.Where("updated_at > ?", lastWeek).Find(&us)
db.Where("created_at BETWEEN ? AND ?",last, yest).Find(&us)
7.4.5 struct & Map查詢
- Struct demo
var u uGroup
// 查詢name=ss,age=20
db.Where(&uGroup{Name:"ss",Age:20}).First(&u)
- Map查詢
// Map查詢
var us []uGroup
db.Where(map[string]interface{}{"name":"ss","age":77}).Find(&us)
// SELECT * FROM u_groups WHERE name = "ss" AND age = 77;
- 主鍵的切片
db.Where([]int64{1,2,5}).Find(&us)
// SELECT * FROM u_groups WHERE id IN (1, 2, 5)
- 當通過結構體進行查詢時,GORM將會只通過非零值欄位查詢。這意味著如果你的欄位值為0,"",false或者其他零值時,將不會被用於構建查詢條件。
db.Where(&User{name:"ss",Age:0}).Find(&us)
- 不過你可以通過上面提到2個方法。通過指標或是Scanner/Valuer
7.4.6Not條件查詢
db.Not("name","ss").First(&u)
// SELECT * FROM u_groups WHERE name <> "ss" LIMIT 1
// Not In
db.Not("name", []string{"ss","1234"}).Find(&us)
// 主鍵不在切片中
db.Not([]int64{1,2,3}).First(&u)
// 查詢第一個
db.Not([]int64{}).First(&u)
// Struct
db.Not(uGroup{Name:"xx"}).First(&u)
7.4.7 Or條件查詢
db.Where("name=?","ss").Or("age=?","43").Find(&us)
//SELECT * FROM u_groups WHERE name = 'ss' OR age = 43;
// Struct
db.Where("name='jk'").Or(uGroup{Name:"ming"}).Find(&us)
// Map
db.Where("name='jk'").Or(map[string]interface{}{"name":"ming"}).Find(&us)
//SELECT * FROM u_groups WHERE name = 'ss' OR name = 'ming';
7.4.8內聯條件
- 作用與Where查詢類似,當內聯條件與多個立即執行方法一起使用時,內聯條件不會傳遞給後面立即執行。
db.First(&u,5)//適用整形
db.First(&u,"id=?","string_primary_key")//適用菲整形主鍵
db.Find(&u,"name <> ? AND age > ?","jk", 20)
// struct
db.Find(&us,uGroup{Age:20})
// SELECT * FROM u_groups WHERE age=20;
// Map
db.Find(&u,map[string]interface{}{"age":20})
// SELECT * FROM u_groups WHERE age=20;
7.4.9額外查詢選項
- 為查詢SQL新增額外的SQL操作
db.Set("gorm:query_option","FOR UPDATE").First(&u,5)
// SELECT * FROM u_groups WHERE id=5 FOR UPDATE;
7.4.10FirstOrInit
- 獲取匹配的第一條記錄,否則根據給定的條件初始化一個新的物件(僅支援struct 和 map 條件)
// 當沒有找到
db.FirstOrInit(&u, uGroup{Name:"not exist"})
fmt.Println(u)//返回結果:{0 not exist 0}
// 當找到了
db.Where(uGroup{Name: "ss"}).FirstOrInit(&u)
fmt.Println(u)//返回結果: {1 ss 77}
// 通過map獲取
db.FirstOrInit(&u,map[string]interface{}{"name":"ss"})
- Attrs
- 如果記錄未找到,將使用引數初始化struct
// 1.未找到:
db.Where(uGroup{Name:"not exist"}).Attrs(uGroup{Age:22}).FirstOrInit(&u)
fmt.Println(u)// {0 not exist 22}
// 當你查詢使用者名稱: not exist不存在時候會 預設返回User{Name: "not exist", Age: 20}
// 當然你也可以這麼寫
db.Where(uGroup{Name:"not exist"}).Attrs("age", 20).FirstOrInit(&u)
// 2.找到了
相同寫法。
- Assign
- 無論是否查詢到,都將引數賦值給struct
// 未找到
db.Where(uGroup{Name:"not exist"}).Assign(uGroup{Age:22}).FirstOrInit(&u)
fmt.Println(u)// {0 not exist 22}
// 找到
db.Where(uGroup{Name:"ss"}).Assign(uGroup{Age:77}).FirstOrInit(&u)
fmt.Println(u)// {1 ss 77}
7.4.11FirstOrCreate
- 獲取匹配的第一條記錄,否則根據給定的條件建立一個新的記錄(僅支援struct和map條件)
// 未找到
db.FirstOrCreate(&u, uGroup{Name:"not exist"})
fmt.Println(u)// 沒有找到,直接建立了:{15 not exist 0}
// 找到
db.Where(uGroup{Name: "ss"}).FirstOrCreate(&u)
fmt.Println(u)//{1 ss 77}
- Attrs
- 如果記錄未找到,將使用引數建立struct 和記錄
db.Where(uGroup{Name:"Tom"}).Attrs(uGroup{Age:20}).FirstOrCreate(&u)
fmt.Println(u) //未找到會建立: {16 Tom 20},找到了會返回結果
- Assign
- 不管記錄是否找到,豆漿引數賦值給struct並儲存資料庫
db.Where(uGroup{Name:"Lucy"}).Assign(uGroup{Age:30}).FirstOrCreate(&u)
fmt.Println(u) // 未找到建立:{17 Lucy 30}
db.Where(uGroup{Name:"Lucy"}).Assign(uGroup{Age:32}).FirstOrCreate(&u)// 找到了,更改使用者資訊,age改為32
7.5GORM高階查詢
7.5.1子查詢
- 通過*gorm.expr的子查詢
type Order struct {
DeletedAt interface{}
State string
Amount int
}
var orders Order
// SELECT * FROM "orders" WHERE "orders"."deleted_at" IS NULL AND (amount > (SELECT AVG(amount) FROM "orders" WHERE (state = 'paid')));
db.Where("amount > ?",db.Table("orders").Select("AVG(amount)").Where("state = ?","sk2").SubQuery()).Find(&orders)
fmt.Println(rest)
fmt.Println(orders)
7.5.2選擇欄位
- Select 指定你想從資料庫中檢索的欄位,預設會選擇全部欄位
var userinfo []uGroup
// 方式1
db.Select("name,age").Find(&userinfo)
fmt.Println(userinfo)
//// SELECT name, age FROM users;
// 方式2
db.Select([]string{"name","age"}).Find(&userinfo)
fmt.Println(userinfo)
// 方式3
db.Table("users").Select("COALESCE(age,?)", 42).Rows()
//// SELECT COALESCE(age,'42') FROM users;
7.5.3排序
- Order 指定從資料庫中檢索出記錄順序。設定第二個引數reorder為true ,可以覆蓋前面定義的排序條件。
var users []uGroup
db.Order("age desc,name").Find(&users)
fmt.Println(users)
// SELECT * FROM u_group order by age desc,name
// 多欄位排序
db.Order("age desc").Order("name").Find(&users)
fmt.Println(users)
/ SELECT * FROM users ORDER BY age desc, name;
// 覆蓋排序
db.Order("age desc").Find(&users1).Order("age", true).Find(&users2)
//// SELECT * FROM users ORDER BY age desc; (users1)
//// SELECT * FROM users ORDER BY age; (users2)
7.5.4數量
- limit 指定資料庫檢索的最大記錄數。
var users []uGroup
db.Limit(2).Find(&users)
fmt.Println(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)
7.5.5偏移
- Offset,指定開始返回記錄前要跳過的記錄數。
var users []uGroup
db.Limit(2).Offset(1).Find(&users)
fmt.Println(users)
// SELECT * FROM u_groups limit 3 offset 1;
7.5.6統計總數
var users []uGroup
var count int
db.Where("name = ?", "xiaoming2").Or("name = ?","xiaoming3").Find(&users).Count(&count)
fmt.Println(users,count)
// [{2 xiaoming2 13} {3 xiaoming3 23}] 2
db.Model(&uGroup{}).Where("name = ?","xiaoming2").Count(&count)
fmt.Println(count)
// SELECT count(*) FROM users WHERE name = "xiaoming2"
db.Table("u_groups").Count(&count)
// SELECT count(*) FROM u_groups;
db.Table("u_groups").Select("count(distinct(name))").Count(&count)
//// SELECT count( distinct(name) ) FROM u_groups;
- 注意:Count 必須是鏈式查詢的最後一個操作,因為它會覆蓋前面SELECT,但如果裡面使用count時就不會覆蓋。
7.5.7Group & Having
- 示例1:
type Result struct{
State string
Total int
}
rows, err := db.Table("orders").Select("state,sum(amount) as total").Group("state").Rows()
for rows.Next() {
r := &Result{}
err = db.ScanRows(rows,r)
fmt.Println(r)
if err != nil{
fmt.Println(err)
break
}
}
- 示例2:
var res []Result
db.Table("orders").Select("state,sum(amount) as total").Group("state").Scan(&res)
fmt.Println(res)
- 示例三
// Having
rows, err := db.Table("orders").Select("state,sum(amount) as total").Group("state").Having("sum(amount) > ?",30).Rows()
if err != nil{
fmt.Println(err)
}
for rows.Next() {
r := &Result{}
err = db.ScanRows(rows,r)
if err != nil {
fmt.Println(err)
break
}
fmt.Println(*r)
}
- 示例四:
var res []Result
db.Table("orders").Select("state,sum(amount) as total").Group("state").Having("sum(amount) > ?",30).Scan(&res)
fmt.Println(res)
7.5.8連線
- 通過Join進行連線。
type uGroup struct {
Id int64
Name string `gorm:"default:'ss'"`
Age int64
}
type Email struct {
Id int64
Email string
UserId int64
}
type Result2 struct {
Uname string
Ename string
}
rows, err := db.Table("u_groups").Select("u_groups.name as uname, emails.email as ename").Joins("left join emails on emails.id = u_groups.id").Rows()
for rows.Next(){
r := &Result2{}
err = db.ScanRows(rows,r)
if err != nil{
fmt.Println(err)
break
}
fmt.Println(*r)
}
- 示例2:
type Result2 struct {
Uname string
Ename string
}
var results []Result2
db.Table("u_groups").Select("u_groups.name as uname, emails.email as ename").Joins("right join emails on emails.id = u_groups.id").Scan(&results)
fmt.Println(results)
- 示例3:
var u uGroup
db.Joins("JOIN emails ON emails.id = u_groups.id AND emails.email = ?", "[email protected]").Joins("JOIN credit_cards ON credit_cards.id=u_groups.id").Where("credit_cards.common=?","common1").Find(&u)
fmt.Println(u)
7.5.9Pluck
- Pluck 查詢model中的一個列作為切片,如果想要查詢多個列可以收納櫃Scan
var ages []int64
var users []uGroup
db.Find(&users).Pluck("age",&ages)
fmt.Println(ages)
- Model指定表Struct
var names []string
db.Model(&uGroup{}).Pluck("name",&names)
fmt.Println(names)
- 查詢多個列
db.Select("name,age").Find(&users)
fmt.Println(users)
7.5.10掃描
- Scan掃描結果至一個struct
type Result struct {
Name string
Age int
}
var result Result
db.Table("u_groups").Select("name,age").Where("name=?","xiaoming").Scan(&result)
fmt.Println(result)
- 示例2
var results []Result
db.Table("u_groups").Select("name,age").Where("id > ?",0).Scan(&results)
fmt.Println(results)
- 示例3:
//原生SQL
db.Raw("SELECT name,age from u_groups WHERE name=?","xiaoming").Scan(&results)
fmt.Println(results)
7.5.11 鏈式操作相關
- 鏈式操作,Gorm實現了鏈式操作介面,所以你可以把程式碼寫成這樣。
var results []Result
tx := db.Where("name = ?","xiaoming")
// 新增更多條件
judge:=true
if judge {
tx = tx.Where("age > ?",5)
} else {
tx = tx.Where("age = ?",30)
}
tx.Find(&results)
fmt.Println(results)
- 在呼叫立即執行方法前不能生成Query語句,藉助這個特性你可以i建立一個函式處理一些普通邏輯。
7.5.12範圍
- Scopes,Scopes 是建立在鏈式操作的基礎之上。基於它,你可以抽取一些通用邏輯,寫出更多可重用的函式庫。
func UGroupAge20(db *gorm.DB) *gorm.DB{
return db.Where("age > ?", 20)
}
func NameeqXiaoming(db *gorm.DB) *gorm.DB{
return db.Where("name = ?","xiaoming")
}
func UGroupIdeq1(db *gorm.DB) *gorm.DB{
return db.Where("id = ?", 1)
}
func uGroupInId(ids []int) func(db *gorm.DB) *gorm.DB{
return func(db *gorm.DB) *gorm.DB {
return db.Scopes(UGroupAge20).Where("id IN (?)",ids)
}
}
var us []uGroup
db.Scopes(UGroupAge20,NameeqXiaoming,UGroupIdeq1).Find(&us)
fmt.Println(us)
db.Scopes(uGroupInId([]int{1,2})).Find(&us)
fmt.Println(us)
7.5.13多個立即執行
- 在GORM中使用多個立即執行方法時,後一個立即執行方法會服用前一個
立即執行方法
的條件(不包含內聯條件)
var users []uGroup
var count int64
db.Where("name Like ?", "xiaoming%").Find(&users,"id IN (?)", []int{1,2,3}).Count(&count)
fmt.Println(users)
fmt.Println(count)
7.6 更新
7.6.1更新所有欄位
- 使用Save() 預設會更新所有欄位,即使你沒有去賦值。
var user uGroup
db.First(&user)
fmt.Println(user)
user.Name = "jk"
user.Age = 22
db.Save(&user)
7.6.2 更新修改欄位
- 使用Update或者Updates更新指定欄位
// 更新單個屬性,如果它有變化
db.Model(&user).Update("name","hello")
// 根據條件更新單個屬性
db.Model(&user).Where("age = ?",21).Update("name","jk")
// 使用map更新多個屬性,只會更新其中有變化屬性
db.Model(&user).Updates(map[string]interface{}{"name":"xujunkai","age":18})
// 使用struct 更新多個屬性,只會更新其中變化且非零的欄位
db.Model(&user).Updates(uGroup{Name:"xjk",Age: 20})
// struct更新時,GORM只會更新那些非零值的欄位。
// 對於 "" 0 false 不會發生任何更新
db.Model(&user).Updates(uGroup{Name:"",Age: 0})
7.6.3 更新選定欄位
- 如果你想更新或忽略某些欄位,你可以使用
Select
,Omit
// 下面我們在map中更新name,age.但是我們只選擇了name.所以只更新name
db.Model(&user).Select("name").Updates(map[string]interface{}{"name":"xujunkai","age":19})
// Omit 表示要忽略欄位
db.Model(&user).Omit("name").Updates(map[string]interface{}{"name":"xiaoming","age":30})
7.6.4無Hooks更新
- 上面更新操作會自動執行Model的。BeforeUpdate,AfterUpdate方法。更新UpdateAt時間戳。更新時儲存其Associations.如果你想呼叫這些方法,你可以使用UPdateColume,UpdateColumns
db.First(&user)
db.Model(&user).UpdateColumn("name","hello")
// 更新多個屬性。類似Updates
db.Model(&user).UpdateColums(User{Name:"hello",Age:18})
7.6.5批量更新
- 批量更新時,Hook(鉤子函式)。不會更新。
// map方式批量更新
db.Table("u_groups").Where("id IN (?)",[]int{1,5,11}).Updates(map[string]interface{}{"name":"xjk","age":33})
// struct 批量更新 只會更新非零欄位。如果想更新所有欄位。請使用map
db.Model(uGroup{}).Updates(uGroup{Name:"xiaoming",Age: 15})
// RowsAffected 獲取更新記錄
res := db.Model(uGroup{}).Updates(uGroup{Name: "xjk",Age: 40}).RowsAffected
fmt.Println(res)
7.6.6使用SQL表示式
- 查詢表中第一條資料,然後更新
ar user uGroup
db.First(&user)
db.Model(&user).Update("age",gorm.Expr("age*?+?",4,1))
// UPDATE `u_groups` SET `age` = age * 4 + 1, `updated_at` = '2020-08-01 13:10:20' WHERE `users`.`id` = 1;
db.Model(&user).Updates(map[string]interface{}{"age":gorm.Expr("age - ?",500)})
db.Model(&user).Where("age > 10").UpdateColumn("age",gorm.Expr("age - ?",5))
7.6.7其他更新選項
// 為 update SQL新增其他SQL
db.Model(&user).Set("gorm:update_option","OPTION (OPTIMIZE FOR UNKNOWN)").Update("name","hello")
7.7刪除
7.7.1刪除記錄
- 警告 刪除記錄時,請確保主鍵欄位有值。GORM會通過主鍵去刪除記錄,如果主鍵為空,GORM會刪除該model所有記錄.
var user uGroup
db.First(&user)
db.Delete(&user)
7.7.2批量刪除
- 刪除全部匹配記錄
db.Where("email LIKE ?","%xjk%").Delete(uGroup{})
7.7.3軟刪除
- 如果一個model有,DeletedAt欄位,他將自動獲得軟刪除功能,當呼叫Delete方法時,記錄不會真正的從資料庫中被刪除,只會DeletedAt欄位值會被設定為當前時間
db.Delete(&user)
// 批量刪除
db.Where("age = ?",20).Delete(&u_Group{})
// 查詢會忽略被軟刪除的記錄
db.Where("age = ?",84).Find(&user)
// 通過Unscoped方法可以查詢被軟刪除的記錄
var users []uGroup
db.Unscoped().Where("age=?",84).Find(&users)
fmt.Println(users)
7.7.4物理刪除
db.Unscoped().Delete(&user)
- 詳細參見官方文件:GORM直通車