go语言gorm的CRUD

发布时间 2023-07-26 18:37:14作者: 自然洒脱

插入

如果表不存在,则插入失败

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)
    }
join查询

 修改

    // 单条更新更新
    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)