本文主要是介绍gorm常用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
一.CURD相关
创建记录
user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()} result := db.Create(&user) // 通过数据的指针来创建 user.ID // 返回插入数据的主键 result.Error // 返回 error result.RowsAffected // 返回插入记录的条数 |
---|
我们还可以使用 Create()
创建多项记录:
users := []*User{ User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}, User{Name: "Jackson", Age: 19, Birthday: time.Now()}, } result := db.Create(users) // 传递切片以插入多行数据 result.Error // 返回 error result.RowsAffected // 返回插入记录的条数 |
---|
NOTE 你无法向 ‘create’ 传递结构体,所以你应该传入数据的指针.
用指定的字段创建记录
创建记录并为指定字段赋值。
db.Select("Name", "Age", "CreatedAt").Create(&user) // INSERT INTO `users` (`name`,`age`,`created_at`) VALUES ("jinzhu", 18, "2020-07-04 11:05:21.775") |
---|
创建记录并忽略传递给 ‘Omit’ 的字段值
db.Omit("Name", "Age", "CreatedAt").Create(&user) // INSERT INTO `users` (`birthday`,`updated_at`) VALUES ("2020-01-01 00:00:00.000", "2020-07-04 11:05:21.775") |
---|
批量插入
要高效地插入大量记录,请将切片传递给Create
方法。 GORM 将生成一条 SQL 来插入所有数据,以返回所有主键值,并触发 Hook
方法。 当这些记录可以被分割成多个批次时,GORM会开启一个事务</0>来处理它们。
var users = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}, {Name: "jinzhu3"}} db.Create(&users) for _, user := range users { user.ID // 1,2,3 } |
---|
你可以通过db.CreateInBatches
方法来指定批量插入的批次大小
var users = []User{{Name: "jinzhu_1"}, ...., {Name: "jinzhu_10000"}} // batch size 100 db.CreateInBatches(users, 100) |
---|
创建钩子
GROM允许用户通过实现这些接口 BeforeSave
, BeforeCreate
, AfterSave
, AfterCreate
来自定义钩子。 这些钩子方法会在创建一条记录时被调用,关于钩子的生命周期请参阅Hooks。
注意 在 GORM 中保存、删除操作会默认运行在事务上, 因此在事务完成之前该事务中所作的更改是不可见的,如果您的钩子返回了任何错误,则修改将被回滚。
Hook (对象生命周期)是在创建、查询、更新、删除等操作之前、之后调用的函数。
如果您已经为模型定义了指定的方法,它会在创建、更新、查询、删除时自动被调用。如果任何回调返回错误,GORM 将停止后续的操作并回滚事务。
钩子方法的函数签名应该是 func(*gorm.DB) error
func (u *User) BeforeCreate(tx *gorm.DB) (err error) { u.UUID = uuid.New() if u.Role == "admin" { return errors.New("invalid role") } return } |
---|
如果你想跳过Hooks
方法,可以使用SkipHooks
会话模式,例子如下
DB.Session(&gorm.Session{SkipHooks: true}).Create(&user) DB.Session(&gorm.Session{SkipHooks: true}).Create(&users) DB.Session(&gorm.Session{SkipHooks: true}).CreateInBatches(users, 100) |
---|
根据 Map 创建
GORM支持通过 map[string]interface{}
与 []map[string]interface{}{}
来创建记录。
db.Model(&User{}).Create(map[string]interface{}{ "Name": "jinzhu", "Age": 18, }) // batch insert from `[]map[string]interface{}{}` db.Model(&User{}).Create([]map[string]interface{}{ {"Name": "jinzhu_1", "Age": 18}, {"Name": "jinzhu_2", "Age": 20}, }) |
---|
注意当使用map来创建时,钩子方法不会执行,关联不会被保存且不会回写主键。
高级选项
关联创建
创建关联数据时,如果关联值非零,这些关联会被upsert,并且它们的Hooks
方法也会被调用。
type CreditCard struct { gorm.Model Number string UserID uint } type User struct { gorm.Model Name string CreditCard CreditCard } db.Create(&User{ Name: "jinzhu", CreditCard: CreditCard{Number: "411111111111"} }) // INSERT INTO `users` ... // INSERT INTO `credit_cards` ... |
---|
你可以通过Select
, Omit
方法来跳过关联更新,示例如下:
db.Omit("CreditCard").Create(&user) // skip all associations db.Omit(clause.Associations).Create(&user) |
---|
默认值
你可以通过结构体Tag default
来定义字段的默认值,示例如下:
type User struct { ID int64 Name string `gorm:"default:galeone"` Age int64 `gorm:"default:18"` } |
---|
这些默认值会被当作结构体字段的零值插入到数据库中
注意,当结构体的字段默认值是零值的时候比如
0
,''
,false
,这些字段值将不会被保存到数据库中,你可以使用指针类型或者Scanner/Valuer来避免这种情况。
type User struct { gorm.Model Name string Age *int `gorm:"default:18"` Active sql.NullBool `gorm:"default:true"` } |
---|
注意,若要让字段在数据库中拥有默认值则必须使用
default
Tag来为结构体字段设置默认值。如果想要在数据库迁移的时候跳过默认值,可以使用default:(-)
,示例如下:
type User struct { ID string `gorm:"default:uuid_generate_v3()"` // db func FirstName string LastName string Age uint8 FullName string `gorm:"->;type:GENERATED ALWAYS AS (concat(firstname,' ',lastname));default:(-);"` } |
---|
注意 SQLite 不支持批量插入的时候使用默认值。 前往 SQLite Insert stmt了解。 下面是一个使用案例:
type Pet struct {
Name string
`gorm:"default:cat"`
} // 在SqlLite中,这是不允许的, 所以GORM会通过构建错误的SQL来返回错误: // INSERT INTO `pets` (`name`) VALUES ("dog"),(DEFAULT) RETURNING `name` db.Create(&[]Pet{{Name: "dog"}, {}})
一个可行的替代方案是通过钩子方法来设置默认字段
func (p *Pet) BeforeCreate(tx *gorm.DB) (err error) { if p.Name == "" { p.Name = "cat" } }
查询 检索单个对象
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 or nil
// 检查 ErrRecordNotFound 错误
errors.Is(result.Error, gorm.ErrRecordNotFound)
如果你想避免
ErrRecordNotFound
错误,你可以使用Find
,比如
db.Limit(1).Find(&user)
,Find
方法可以接受struct和slice的数据。
对单个对象使用
Find
而不带limit,db.Find(&user)
将会查询整个表并且只返回第一个对象,这是性能不高并且不确定的。
First
andLast
方法会按主键排序找到第一条记录和最后一条记录 (分别)。 只有在目标 struct 是指针或者通过db.Model()
指定 model 时,该方法才有效。 此外,如果相关 model 没有定义主键,那么将按 model 的第一个字段进行排序。 例如:
--javascripttypescriptbashsqljsonhtmlcssccppjavarubypythongorustmarkdown
var user User
var users []User// works because destination struct is passed in
db.First(&user)
// SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1// works because model is specified using `db.Model()`
result := map[string]interface{}{}
db.Model(&User{}).First(&result)
// SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1// doesn't work
result := map[string]interface{}{}
db.Table("users").First(&result)// works with Take
result := map[string]interface{}{}
db.Table("users").Take(&result)// no primary key defined, results will be ordered by first field (i.e., `Code`)
type Language struct {Code stringName string
}
db.First(&Language{})
// SELECT * FROM `languages` ORDER BY `languages`.`code` LIMIT 1
检索全部对象
// 获取所有记录 result := db.Find(&users) // SELECT * FROM users; result.RowsAffected // 返回找到的记录数,等于 `len(users)` result.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 用户 WHERE name IN ('jinzhu','jinzhu 2' );
// LIKE
db.Where( "name LIKE ?" , "%jin%" ).Find(&users)
// SELECT * FROM 用户 WHERE name LIKE '%jin%';
// AND
db.Where( "name = ? AND Age >= ?" , "jinzhu" , "22" ).Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' AND Age >= 22;
// 时间
db.Where( "updated_at > ?" , lastWeek).Find(&users)
// SELECT * FROM 用户 WHERE Updated_at > '2000-01-01 00:00:00';
// BETWEEN
db.Where( "created_at BETWEEN ? AND ?" , lastWeek, Today).Find(&users)
// SELECT * FROM 用户 WHEREcreated_at BETWEEN '2000-01-01 00:00:00' AND '2000-01 -08 00:00:00';
如果对象设置了主键,条件查询将不会覆盖主键的值,而是用 And 连接条件。 例如:
var user = User{ ID: 10 } db.Where( "id = ?" , 20 ).First(&user) // SELECT * FROM users WHERE id = 10 and id = 20 ORDER BY id ASC LIMIT 1 |
---|
这个查询将会给出record not found
错误 所以,在你想要使用例如 user
这样的变量从数据库中获取新值前,需要将例如 id
这样的主键设置为nil。
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; // 映射 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); |
---|
**注意:**当使用 struct 进行查询时,GORM 只会查询非零字段,这意味着如果您的字段值为0
、''
或false
其他零值,则不会用于构建查询条件,例如:
db.Where(&User{姓名: "jinzhu" , 年龄: 0 }).Find(&users) // SELECT * FROM 用户 WHERE name = "jinzhu"; |
---|
要在查询条件中包含零值,可以使用映射,它将包含所有键值作为查询条件,例如:
db.Where( map [ string ] interface {}{ "Name" : "jinzhu" , "Age" : 0 }).Find(&users) // SELECT * FROM users WHERE name = "jinzhu" AND Age = 0; |
---|
这篇关于gorm常用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!