插入数据
根据struct进行插入
演示了插入一条数据,插入多条数据,分批插入数据
func Create(db *gorm.DB) {
//插入一条记录
user := User{UserId: rand.IntN(100000), Degree: "本科", Gender: "男", City: "上海", Keywords: []string{"编程", "golang"}}
result := db.Create(&user) //必须传指针,因为要给User的主键赋值。主键为0值时Create会自动给主键赋值,也会给CreatedAt字段赋值
if result.Error != nil {
slog.Error("插入记录失败", "error", result.Error)
}
fmt.Printf("record id is %d\n", user.Id)
fmt.Printf("影响%d行\n", result.RowsAffected)
//会话模式
tx := db.Session(&gorm.Session{SkipHooks: true}) //不执行钩子Hook
// db := db.Session(&gorm.Session{DryRun: true}) //生成SQL,但不执行
//一次性插入多条
user1 := user //发生拷贝
user1.Id = 0 //把主键置为0,因为user.Id是有值的
user1.UserId = rand.IntN(100000) //UserId上有唯一性约束
user2 := user //发生拷贝
user2.Id = 0 //把主键置为0
user2.UserId = rand.IntN(100000)
users := []*User{&user1, &user2} //切片里的元素也可以不是指针
result = tx.Create(users) //一条SQL插入所有数据
fmt.Printf("影响%d行\n", result.RowsAffected)
//量太大时分批插入(SQL语句的长度是有上限的,同时避免长时间阻塞)
batchSize := 1 //通常为几百
user3 := user
user3.Id = 0
user3.UserId = rand.IntN(100000)
user4 := user3//由于没有设置user3=4.Id = 0,会插不进去
user4.Id = 0
db.CreateInBatches([]*User{&user3, &user4}, batchSize) //一个批次一条SQL。且所有批次被放到一个事务中来执行。由于user4插不进去,所以user3也会回滚。但如果设置了SkipDefaultTransaction就没有事务
}
根据map进行插入
演示了插入一条数据和插入多条数据
func CreateByMap(db *gorm.DB) error {
//插入一条记录
err := db.Model(User{}).Create(map[string]any{ // 不会自动给Id和CreatedAt赋值,毕竟也没给Create函数传结构体指针
"uid": rand.IntN(100000), "degree": "本科", "gender": "男", "city": "上海",
"create_time": time.Now(),
}).Error
if err != nil {
return err
}
//一次性插入多条
err = db.Model(User{}).Create([]map[string]any{
{"uid": rand.IntN(100000), "degree": "本科", "gender": "男", "city": "北京", "create_time": time.Now()},
{"uid": rand.IntN(100000), "degree": "本科", "gender": "男", "city": "深圳", "create_time": time.Now()},
}).Error
if err != nil {
return err
}
return nil
}
钩子
如果任何钩子回调返回错误,GORM将停止后续的操作并回滚事务。
Create时钩子的执行时机:
开始事务
BeforeSave (Create/Update 都会触发)
BeforeCreate
关联前的 save
插入记录至 db
关联后的 save
AfterCreate
AfterSave (Create/Update 都会触发)
提交或回滚事务
func (u *User) BeforeSave(db *gorm.DB) (err error) {
db.Logger.Info(context.Background(), "exec hook BeforeSave")
return nil
}
func (u *User) BeforeCreate(db *gorm.DB) (err error) {
db.Logger.Info(context.Background(), "exec hook BeforeCreate")
return nil
}
func (u *User) AfterCreate(db *gorm.DB) (err error) {
db.Logger.Info(context.Background(), "exec hook AfterCreate")
return nil
}
func (u *User) AfterSave(db *gorm.DB) (err error) {
db.Logger.Info(context.Background(), "exec hook AfterSave")
return nil
}
删除数据
func Delete(db *gorm.DB) {
tx := db.Where("degree=?", "本科").Delete(User{})
fmt.Printf("删除%d行\n", tx.RowsAffected)
var user User = User{Id: 10}
db.Delete(user) //暗含的Where条件是id=10
db.Delete(User{}, 1) //暗含的Where条件是id=1
db.Delete(User{}, []int{1, 2, 3}) //暗含的Where条件是id IN (1,2,3)
}
钩子
如果任何钩子回调返回错误,GORM将停止后续的操作并回滚事务。
Delete时钩子的执行时机:
开始事务
BeforeDelete
删除 db 中的数据
AfterDelete
提交或回滚事务
改变数据
save
// Save会保存所有的字段,即使字段是零值。主键为0时Save相当于Create
func Save(db *gorm.DB) {
user := User{UserId: rand.IntN(100000), Degree: "本科", Gender: "男", City: "上海"}
db.Save(&user) //主键为0值,Save相当于Create
var user2 User
db.Last(&user2)
user2.Degree = "硕士"
db.Save(&user2) //必须传指针
}
Update指定需要更新的列
func Update(db *gorm.DB) {
// 根据map更新
tx := db.Model(&User{}). //必须传指针
Where("city=?", "北京").Updates(
map[string]any{"degree": "硕士", "gender": "男"},
)
fmt.Printf("更新了%d行\n", tx.RowsAffected)
//根据结构体更新,只会更新非0值
db.Model(&User{}). //必须传指针
Where("city=?", "北京").Updates(
User{Degree: "本科", Gender: "男", Id: 1},
)
fmt.Printf("更新了%d行\n", tx.RowsAffected)
}
钩子
如果任何钩子回调返回错误,GORM将停止后续的操作并回滚事务。
Update时钩子的执行时机:
开始事务
BeforeSave
BeforeUpdate
关联前的 save
更新 db
关联后的 save
AfterUpdate
AfterSave
提交或回滚事务
查询数据
func Read(db *gorm.DB) {
user := User{City: "HongKong", Id: 3} //Id会自动放到Where条件里,其他非0字段不会
tx := db.
Select("uid,city,gender,keywords"). //参数也可以这样传"uid","city","gender"或者[]string{"uid","city","gender"}。没有Select时默认为select *
Where("uid>100 and degree='大专'"). //容易发生SQL注入攻击
Where("city in ?", []string{"北京", "上海"}). //多个Where之间是and关系
Where("degree like ?", "%科").
Or("gender=?", "女"). //用?占位,避免发生SQL注入攻击
Order("id desc, uid").
Order("city").
Offset(3).
Limit(1).
First(&user) //Find可以传一个结构体,也可以传结构体切片。Take、First、Last查不到结果时会返回gorm.ErrRecordNotFound,但Find不会,Find查无结果时就不去修改结构体
if tx.Error != nil {
if !errors.Is(tx.Error, gorm.ErrRecordNotFound) {
slog.Error("读DB失败", "error", tx.Error)
} else {
slog.Info("查无结果")
}
} else {
if tx.RowsAffected > 0 {
fmt.Printf("read结果:%+v\n", user)
} else {
slog.Info("查无结果", "user", user)
}
}
var user2 *User //不同于var user2 User,还没申请内存空间
// 通过反射给user2赋值时发现还没给user2申请好内存空间
tx = db.Find(user2) //error: invalid value, should be pointer to struct or slice
if tx.Error != nil {
if !errors.Is(tx.Error, gorm.ErrRecordNotFound) {
slog.Error("读DB失败", "error", tx.Error)
} else {
slog.Info("查无结果")
}
}
var user3 *User = new(User)
tx = db.Find(user3)
if tx.Error != nil {
if !errors.Is(tx.Error, gorm.ErrRecordNotFound) {
slog.Error("读DB失败", "error", tx.Error)
} else {
slog.Info("查无结果")
}
} else {
if tx.RowsAffected > 0 {
fmt.Printf("read结果:%+v\n", user3)
} else {
slog.Info("查无结果", "user", user3)
}
}
var users []User
tx = db.Limit(3).Find(&users) //要修改切片的长度,所以要传切片的指针
if tx.Error != nil {
if !errors.Is(tx.Error, gorm.ErrRecordNotFound) {
slog.Error("读DB失败", "error", tx.Error)
} else {
slog.Info("查无结果")
}
} else {
if tx.RowsAffected > 0 {
fmt.Println("多个read结果")
for _, u := range users {
fmt.Printf("%+v\n", u)
}
} else {
slog.Info("查无结果")
}
}
user4 := User{Id: 23212} //给主键赋值
tx = db.Find(&user4) //主键不为0值时暗含了一个where条件:id=47
if tx.Error != nil {
if !errors.Is(tx.Error, gorm.ErrRecordNotFound) {
slog.Error("读DB失败", "error", tx.Error)
} else {
slog.Info("查无结果")
}
} else {
if tx.RowsAffected > 0 {
fmt.Printf("read结果:%+v\n", user4)
} else {
slog.Info("查无结果")
}
}
// SELECT * FROM `user` USE INDEX (`id`,`idx_uid`) WHERE uid>0
db.Where("uid>0").
Clauses(hints.UseIndex("id", "idx_uid")). //给mysql一个建议的索引范围,这个范围之外的索引mysql就不再考虑了
Find(&users)
// SELECT * FROM `user` FORCE INDEX (`idx_uid`) WHERE uid>0
db.Where("uid>0").
Clauses(hints.ForceIndex("idx_uid")). //强制mysql使用某个索引
Find(&users)
}
基于统计的查询
func ReadWithStatistics(db *gorm.DB) {
type Result struct {
City string
Mid float64
}
var results []Result
db.Model(User{}).Select("city,avg(id) as mid").Group("city").Having("mid>0").Find(&results)
fmt.Println("group by having查询结果:")
for _, result := range results {
fmt.Printf("%+v\n", result)
}
db.Table("user").Distinct("city").Find(&results)
fmt.Println("distinct查询结果:")
for _, result := range results {
fmt.Printf("%+v\n", result)
}
var count int64
db.Table("user").Where("city=?", "北京").Count(&count)
fmt.Printf("count=%d\n", count)
}
钩子
如果任何钩子回调返回错误,GORM将停止后续的操作并回滚事务。
查询时钩子的执行时机:
// 从 db 中加载数据
// Preloading (eager loading)
AfterFind

Comments NOTHING