檢索單個對象
GORM 提供了 First
、Take
、Last
方法,以便從數據庫中檢索單個對象。當查詢數據庫時它添加了 LIMIT 1
條件
// 獲取第一條記錄(主鍵升序) db.First(&user) // SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1 // 獲取第一條記錄,沒有指定排序字段 db.Take(&user) // SELECT * FROM `users` LIMIT 1 // 獲取最后一條記錄,主鍵降序 db.Last(&user) // SELECT * FROM `users` ORDER BY `users`.`id` DESC LIMIT 1 result := db.Last(&user) fmt.Println(result.RowsAffected) // 返回找到的記錄數 fmt.Println(result.Error) // return error or nil b := errors.Is(result.Error, gorm.ErrRecordNotFound) // 判斷錯誤類型,如果是此錯誤類型,返回true,否則返回false fmt.Println(b) 如果你想避免ErrRecordNotFound錯誤,你可以使用Find,比如db.Limit(1).Find(&user),Find方法可以接受struct和slice的數據。
First
和 Last
會根據主鍵排序,分別查詢第一條和最后一條記錄。 只有在目標 struct 是指針或者通過 db.Model()
指定 model 時,該方法才有效。 此外,如果相關 model 沒有定義主鍵,那么將按 model 的第一個字段進行排序。
// 有效,因為目標是結構體指針 db.First(&user) // SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1 // 有效,因為通過db.Model() 指定了model var result map[string]interface{} db.Model(new(User)).First(&result) // SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1
// 無效
var result map[string]interface{}
db.Table("users").First(&result)
fmt.Println(result) // 輸出:map[]
// 配合Take無效
var result map[string]interface{}
db.Table("users").Take(&result)
// 未指定主鍵,會根據第一個字段排序(即:`Code`)
type Language struct {
Code string
Name string
}
db.First(&Language{})
// SELECT * FROM `languages` ORDER BY `languages`.`code` LIMIT 1
用主鍵檢錯
如果主鍵是數字類型,您可以使用內聯條件來檢索對象,傳入字符串參數時,需要特別注意 SQL 注入問題
db.First(&user, 2) // SELECT * FROM `users` WHERE `users`.`id` = 2 ORDER BY `users`.`id` LIMIT 1
db.First(&user, "2")
// SELECT * FROM `users` WHERE `users`.`id` = 2 ORDER BY `users`.`id` LIMIT 1
db.Find(&users, []int{1, 2})
// SELECT * FROM `users` WHERE `users`.`id` IN (1,2)
如果主鍵是字符串(例如像 uuid),查詢將被寫成這樣:
db.First(&user, "uuid = ?", "00000000-0000-0000-0000-000000000000")
// SELECT * FROM `users` WHERE uuid = '00000000-0000-0000-0000-000000000000' ORDER BY `users`.`id` LIMIT 1
檢索全部對象
result := db.Find(&users)
// SELECT * FROM `users`
myPrint(result.RowsAffected) // 返回的記錄行數,相當於 len(users)
myPrint(result.Error) // return error
String 條件
// 獲取第一條匹配的記錄
db.Where("name = ?", "老王").First(&user)
// SELECT * FROM `users` WHERE name = '老王' ORDER BY `users`.`id` LIMIT 1
// 獲取全部匹配的記錄
db.Where("name = ?", "老王").Find(&users)
// SELECT * FROM `users` WHERE name = '老王'
// IN
db.Debug().Where("age IN (?)", []uint8{10, 55}).Find(&users)
// SELECT * FROM `users` WHERE age IN (10,55)
// LIKE
db.Debug().Where("age LIKE ?", "%0%").Find(&users)
// SELECT * FROM `users` WHERE age LIKE '%0%'
// AND
db.Debug().Where("age=? AND name=?", 20, "老王").Find(&users)
// SELECT * FROM `users` WHERE age=20 AND name='老王'
// TIME
db.Debug().Where("created_at < ?", time.Now()).Find(&users)
// SELECT * FROM `users` WHERE created_at < '2021-11-17 13:34:51.519'
// BETWEEN
db.Debug().Where("age BETWEEN ? AND ?", 10, 19).Find(&users)
// SELECT * FROM `users` WHERE age BETWEEN 10 AND 19
Struct & Map 條件
// struct
var users []User
db.Debug().Where(&User{Name: sql.NullString{"老王", true}, Age: 20}).Find(&users)
// SELECT * FROM `users` WHERE `users`.`name` = '老王' AND `users`.`age` = 20
// map
db.Debug().Where(map[string]interface{}{
"name": sql.NullString{"李四", true}, "age": 10,
}).First(&user)
// SELECT * FROM `users` WHERE `age` = 10 AND `name` = '李四' ORDER BY `users`.`id` LIMIT 1
// 主鍵切片條件
var users []User
db.Debug().Where([]uint{1, 5}).Find(&users)
// SELECT * FROM `users` WHERE `users`.`id` IN (1,5)
注意:當使用結構體作為查詢條件時,GORM只會查詢非零值的字段,這意味着如果您的字段為0、false、""、或其它零值時,該字段不會被用於構建查詢條件,例如:
db.Debug().Where(&User{Name: sql.NullString{"老王", true}, Age: 0}).Find(&users)
// SELECT * FROM `users` WHERE `users`.`name` = '老王'
如果你想包含零值的查詢條件,你可以使用map,其會包含所有key-value的查詢條件
db.Debug().Where(map[string]interface{}{
"name": sql.NullString{"老王", true}, "age": 0,
}).Find(&users)
// SELECT * FROM `users` WHERE `age` = 0 AND `name` = '老王'
指定結構體查詢字段
當使用struct進行查詢時,你可以通過想where()傳入struct來指定查詢條件的字段、值、表名,例如
db.Debug().Where(&User{Name: sql.NullString{"老王", true}}, "name", "Age").Find(&users)
// SELECT * FROM `users` WHERE `users`.`name` = '老王' AND `users`.`age` = 0
db.Debug().Where(&User{Name: sql.NullString{"老王", true}, Age: 20}, "age").Find(&users)
// SELECT * FROM `users` WHERE `users`.`age` = 20
也就是說where里面放的結構體無論有幾個字段程序不管,最終只會按照結構體后面的參數字段進行條件查詢
內聯條件
查詢條件也可以被內聯到First或Find之類的方法中,其用法類似於Where
// 根據主鍵獲取記錄,如果是非整型主鍵
db.Debug().First(&user, "ID = ?", 5)
// SELECT * FROM `users` WHERE ID = 5 ORDER BY `users`.`id` LIMIT 1
// plain sql
db.Debug().Find(&users, "name = ?", "老王")
// SELECT * FROM `users` WHERE name = '老王'
db.Debug().Find(&users, "name <> ? AND age = ?", "老王", 20)
// SELECT * FROM `users` WHERE name <> '老王' AND age = 20
// struct
db.Debug().Find(&users, &User{Name: sql.NullString{"趙六", true}})
// SELECT * FROM `users` WHERE `users`.`name` = '趙六'
// map
db.Debug().Find(&users, map[string]interface{}{
"age": 20,
})
// SELECT * FROM `users` WHERE `age` = 20
Not 條件
構建not條件,用法與Where類型
db.Debug().Not("name <> ?", "趙六").First(&user)
// SELECT * FROM `users` WHERE NOT name <> '趙六' ORDER BY `users`.`id` LIMIT 1
// Not IN
db.Debug().Not("name IN (?)", []string{"老王", "李四"}).First(&user)
// SELECT * FROM `users` WHERE NOT name IN ('老王','李四') ORDER BY `users`.`id` LIMIT 1
// map
db.Debug().Not(map[string]interface{}{
"name": []string{"老王", "李四"},
}).First(&user)
// SELECT * FROM `users` WHERE `name` NOT IN ('老王','李四') ORDER BY `users`.`id` LIMIT 1
// struct
db.Debug().Not(User{Age: uint8(10), Name: sql.NullString{"老王", true}}).Find(&users)
// SELECT * FROM `users` WHERE (`users`.`name` <> '老王' AND `users`.`age` <> 10)
// 不在主鍵切片中的記錄
db.Debug().Not([]uint{1, 2, 3, 5}).Find(&users)
// SELECT * FROM `users` WHERE `users`.`id` NOT IN (1,2,3,5)
Or 條件
db.Debug().Where("name = ?", "趙六").Or("age = ?", 10).Find(&users)
// SELECT * FROM `users` WHERE name = '趙六' OR age = 10
// struct
db.Debug().Where("name = ?", "趙六").Or(&User{Age: 55}).Find(&users)
// SELECT * FROM `users` WHERE name = '趙六' OR `users`.`age` = 55
// map
db.Debug().Where("name = ?", "趙六").Or(map[string]interface{}{
"age": 55,
}).Find(&users)
// SELECT * FROM `users` WHERE name = '趙六' OR `age` = 55
選擇特定字段
Select
允許您指定從數據庫中檢索哪些字段, 默認情況下,GORM 會檢索所有字段。
db.Debug().Select("name", "age").Find(&users) // SELECT `name`,`age` FROM `users`
db.Debug().Select([]string{"name", "age"}).Find(&users)
// SELECT `name`,`age` FROM `users`
Order
指定從數據庫檢索記錄時的排序方式
db.Debug().Order("age asc, name desc").Find(&users)
// SELECT * FROM `users` ORDER BY age asc, name desc
// 多個order
db.Debug().Order("age desc").Order("name").Find(&users)
// SELECT * FROM `users` ORDER BY age desc,name
Limit & Offset
limit
指定獲取記錄的最大數量 Offset
指定在開始返回記錄之前要跳過的記錄數量
db.Debug().Limit(3).Find(&users) // SELECT * FROM `users` LIMIT 3
// 通過-1消除limit條件
db.Debug().Limit(1).Find(&users1).Limit(-1).Find(&users2)
// SELECT * FROM `users` LIMIT 1
// SELECT * FROM `users`
db.Debug().Limit(1).Offset(3).Find(&users)
// SELECT * FROM `users` LIMIT 1 OFFSET 3
// 通過-1消除Offset條件
db.Debug().Offset(3).Offset(-1).Find(&users)
// SELECT * FROM `users`
注意:Offset要和Limit一起使用,單獨使用會報錯:Error 1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '3' at line 1
Group By & 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` LIMIT 1 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"
// 智能選擇字段
type APIUser struct {
Name string
Total int
}
rows, err := db.Debug().Table("users").Select("name, sum(age) as total").Group("name").Having("total > ?", 11).Rows()
// SELECT name, sum(age) as total FROM `users` GROUP BY `name` HAVING total > 11
for rows.Next() {
var users APIUser
db.ScanRows(rows, &users)
myPrint(users)
}
/*
type: main.APIUser value: {老王 65}
type: main.APIUser value: {馬亞南 12}
*/
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.Debug().Distinct("date", "total").Order("total desc, date desc").Find(&results) // SELECT DISTINCT `date`,`total` FROM `results` ORDER BY total desc, date desc
Joins
db.Debug().Model(new(User)).Select("users.name, emails.email").Joins("left join emails on users.id = emails.user_id").Scan(&results) // SELECT users.name, emails.email FROM `users` left join emails on users.id = emails.user_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() {
fmt.Println(rows)
}
db.Debug().Table("users").Select("users.name, emails.email").Joins("left join emails on users.id = emails.user_id").Scan(&results)
// SELECT users.name, emails.email FROM `users` left join emails on users.id = emails.user_id
// 帶參數的多表連接
// 方法一
//db.Debug().Joins("JOIN users ON users.id=emails.user_id AND users.name=?", "老王").Find(&emails)
// SELECT `emails`.`user_id`,`emails`.`email` FROM `emails` JOIN users ON users.id=emails.user_id AND users.name='老王'
// 方法二
db.Debug().Joins("JOIN users ON users.id=emails.user_id").Where("users.name=?", "李四").Find(&emails)
// SELECT `emails`.`user_id`,`emails`.`email` FROM `emails` JOIN users ON users.id=emails.user_id WHERE users.name='李四'
Joins 預加載
您可以使用 Joins
實現單條 SQL 預加載關聯記錄,例如:
Scan
scan將結果放到結構體中的方式與Find類似
db.Debug().Table("users").Joins("left join emails on users.id=emails.user_id").Scan(&results) // SELECT * FROM `users` left join emails on users.id=emails.user_id
// 原生SQL
db.Debug().Raw("select name, email from users inner join emails on users.id=emails.user_id where users.name=?", "李四").Scan(&results)
// select name, email from users inner join emails on users.id=emails.user_id where users.name='李四'