插入
如果表不存在,则插入失败
type Student struct { ID int // 缺省主键bigint AUTO_INCREMENT Name string `gorm:"size:48"` //`gorm:"not null;type:varchar(48);comment:姓名"` Age byte // byte=>tinyint unsigned Birthday *time.Time // datetime Gender byte //`gorm:"type:tinyint"` } func (s *Student) String() string { return fmt.Sprintf("%d: %s %d", s.ID, s.Name, s.Age) } func main() { // 新增一条 n := time.Now() s := Student{Name: "Tom", Age: 20, Birthday: &n} // 构建实例 fmt.Println(s) result := db.Create(&s) // 新增,传入指针 fmt.Println(s) // 注意前后ID的变化 fmt.Println(result.Error) fmt.Println(result.RowsAffected) }
示例:
1 type Student struct { 2 Id int `gorm:"not null;autoIncrement"` 3 Name string `gorm:"not null;type:varbinary(24);comment:姓名"` 4 Age uint8 `gorm:"not null;default 0"` 5 Birth_day time.Time 6 Address string `gorm:"size:255"` 7 Updated time.Time `gorm:"not null;autoCreateTime"` 8 } 9 10 func main() { 11 // 打开日志文件 12 fi, err := os.OpenFile("logs/info.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) 13 if err != nil { 14 log.Err(err).Msg("info日志文件打开失败") 15 } 16 li := logg.Olog(fi) 17 defer fi.Close() 18 fe, err := os.OpenFile("logs/error.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) 19 if err != nil { 20 log.Err(err).Msg("error日志文件打开失败") 21 } 22 defer fe.Close() 23 le := logg.Olog(fe) 24 25 // 删除表 26 db0.Migrator().DropTable(&Student{}) 27 // 创建表 28 err = db0.Migrator().CreateTable(Student{}) 29 if err != nil { 30 le.Err(err).Send() 31 } else { 32 msg := fmt.Sprintf("%v表创建成功", "students") 33 li.Info().Msg(msg) 34 } 35 36 // 单条插入 37 u0 := Student{Name: "tom", Age: 20, Birth_day: time.Now()} 38 fmt.Printf("%#v\n", u0) 39 r0 := db0.Create(&u0) 40 if r0.Error != nil { 41 le.Err(r0.Error).Send() 42 } else { 43 li.Info().Msg("插入成功") 44 } 45 fmt.Printf("%#v\n", u0) 46 47 time.Sleep(30 * time.Second) 48 // 批量插入 49 var u1 = []Student{{Name: "jerry", Address: "北京丰台", Birth_day: time.Now().Add(-90 * time.Hour)}, 50 {Name: "Hellen", Age: 30, Address: "英国伦敦", Birth_day: time.Now().AddDate(-3, 2, 1)}} 51 fmt.Printf("%+v\n", u1) 52 db0.Create(&u1) 53 if db0.Error != nil { 54 le.Err(db0.Error).Send() 55 } else { 56 li.Info().Msg("插入成功") 57 } 58 fmt.Printf("%+v\n", u1) 59 }
执行结果:
查询
单条查询
可通过Take、Last、First相关方法
db.Find(&user)
将会查询整个表并且只返回第一个对象
// 单条查询 s0 = Student{} db0.Take(&s0) // SELECT * FROM `students` LIMIT 1 fmt.Println(s0) s0 = Student{} db0.First(&s0) // SELECT * FROM `students` ORDER BY `students`.`id` LIMIT 1 fmt.Println(s0) s0 = Student{} db0.Last(&s0) // SELECT * FROM `students` ORDER BY `students`.`id` DESC LIMIT 1 fmt.Println(s0) s0 = Student{} db0.Take(&s0, 4) // SELECT * FROM `students` WHERE `students`.`id` = 4 LIMIT 1 fmt.Println(s0) s1 := Student{Id: 3} db0.Find(&s1) // SELECT * FROM `students` WHERE `students`.`id` = 3 fmt.Println(s1) s1 = Student{Id: 3} db0.Find(s1, "name=tom") // SELECT * FROM `students` WHERE name=tom AND `students`.`id` = 3 fmt.Println(s1)
多条查询
// 多条查询 var s0 []Student // 由于这里只投影了name字段,所以其他字段为零值 db0.Distinct("name").Find(&s0) // SELECT DISTINCT `name` FROM `students` db0.Find(&s0) // SELECT * FROM `students` // select进行投影,其他未投影字段未零值,limit需放在find后,不然无效 db0.Select("name", "age", "birth_day").Limit(2).Offset(2).Find(&s0) // 下面仅对select的使用方式与上面功效一样 db0.Select([]string{"name", "age", "birth_day"}).Find(&s0, "id > ?", 3) db0.Where("id=4").Find(&s0) // 多个where方法表示and db0.Where("id>2").Where("id<4").Find(&s0) // between属于闭区间,左右都包括 db0.Where("id between 2 and 4").Find(&s0) db0.Where("id in ?", []int{2, 4, 6}).Find(&s0) // 模糊匹配,也可参数化 db0.Where("name like 'He%'").Find(&s0) // where后面加or表示或逻辑 db0.Where("id=2").Or("id=5").Or("id>3").Find(&s0) db0.Where("id=2 or id=5").Find(&s0) // 用实例表示条件查询 db0.Where(Student{Id: 3}, Student{Id: 5}).Find(&s0) // 对主键进行条件查询 db0.Where(1,3,5).Find(&s0) db0.Where(map[string]any{"id": 0, "name": "tom"}).Find(&s0) // 当实例作为条件时,主键为0时将忽略此条件;后面的其他参数表示以此为条件 db0.Where(Student{Id: 0, Name: "tom", Age: 100}, "name").Find(&s0) db0.Not(Student{Id: 0, Name: "tom", Age: 100}, "name").Find(&s0) fmt.Println(db0.RowsAffected, db0.Error, s0)
排序
// 排序,SELECT * FROM `students` ORDER BY id,name desc db0.Order("id,name desc").Find(&s0) // 与上面语句等效 db0.Order("id").Order("name desc").Find(&s0) fmt.Println(db0.RowsAffected, db0.Error, s0)
时间类型
在连接字符串中增加parseTime=true,这样时间类型就会自动转化为time.Time类型;也可以 Birthday string ,拿到Birthday字符串后,必要时,自行转换成时间类型。
Create写入的时间,也就是说time.Now()取当前时区时间,但是存入数据库的时间是UTC时间。 Take拿回的时间也是UTC时间,可以通过s.Birthday.Local()转成当前时区时间。 如果想存入的时间或读取的时间直接是当前时区时间,可以使用loc参数loc=Local。
如果loc=Local,存入时,数据库字段中的时间就是当前时区的时间值;读取时,数据库字段中的时间就被解读为当前时区。
统一项目中数据库中时间类型字段的时区。可以考虑统一采用UTC,为了本地化显示转换为当前 时区即可。
分组查询
type result struct { Name string Avg float64 `gorm:"column:a"` } var s0 []result // 通过Table方法指定要查询的表,Find内为接收结果的实例 db0.Table("students").Select("name,avg(age) as a").Group("name").Find(&s0) // 通过Model方法也可指定要查询的表 db0.Model(Student{}).Select("name, avg(age) as a").Group("name").Having("a > 20").Scan(&s0) fmt.Println(s0) // 通过Rows方法来遍历 rs, err := db0.Model(Student{}).Select("name, avg(age) as a").Group("name").Rows() if err != nil { le.Err(err).Send() } r := result{} for rs.Next() { rs.Scan(&r.Name, &r.Avg) fmt.Println(r) }
join查询
先查后改:先查到一个实例,对这个实例属性进行修改,然后调用db.Save()方法保存。 db.Save()方法会保存所有字段,对于没有主键的实例相当于Insert into,有主键的实例相当于Update。
type result struct { EmpNo int `gorm:"primaryKey"` FirstName, LastName string Salary float64 `gorm:"column:sa"` } var r []result db0.Table("employees as e").Select("e.emp_no", "first_name", "last_name", "salary as sa"). Joins("join salaries as s on e.emp_no = s.emp_no").Find(&r) db0.Table("employees as e").Select("e.emp_no, first_name, last_name, avg(salary) as sa"). Joins("join salaries as s on e.emp_no = s.emp_no").Group("e.emp_no"). Find(&r) // 左连接 db0.Table("employees as e").Select("e.emp_no", "first_name", "last_name", "avg(salary) as sa"). Joins("left join salaries as s on e.emp_no=s.emp_no").Group("e.emp_no"). Find(&r) db0.Table("employees as e").Select("e.emp_no,first_name,last_name,salary as sa"). Joins("left join salaries as s on e.emp_no = s.emp_no").Find(&r) // 右连接 db0.Table("employees as e").Select("e.emp_no,first_name,last_name,salary as sa"). Joins("right join salaries as s on e.emp_no = s.emp_no").Scan(&r) if db0.Error != nil { le.Err(db0.Error).Send() } for _, v := range r { fmt.Println(v) }
修改
// 单条更新更新 db0.Model(Student{Id: 1}).Update("address", "宁夏中宁") db0.Model(&Student{}).Where("id = ?", 1).Update("name", "Agatha") db0.Table("students").Where("id = ?", 2).Update("age", "25") // // 先查后改 s0 := Student{} db0.Table("students").Where("id = 5").Find(&s0) fmt.Println(s0) s0.Birth_day = time.Now().AddDate(-20, 3, 9).Local() db0.Save(&s0) fmt.Println(s0) // 多条更新 db0.Model(Student{}).Where("id > 4 and id < 7"). Updates(map[string]any{"age": 32, "address": "马来西亚"}) db0.Table("students").Where("id > 4").Where("id < 7"). Updates(Student{Address: "日本九州"})
删除
删除操作是危险的,需慎重操作!
result := db.Delete(&Student{}) fmt.Println(result.Error) // 报WHERE conditions required错误,这是全表删除,危险
result := db.Delete(&Student{}, 15) // 指定主键 fmt.Println(result.Error) db.Delete(&Student{}, []int{15, 16, 18}) // DELETE FROM `students` WHERE `students`.`id` IN (15,16,18)
result := db.Where("id > ?", 15).Delete(&Student{}) // 删除符合条件的一批 fmt.Println(result.Error)