Go语言之GORM框架(二) ——GORM的单表操作

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: Go语言之GORM框架(二) ——GORM的单表操作

前言

在上一篇文章中,我们对Gorm进行了介绍,而在这一篇文章中我们主要介绍GORM的单表查询与Hook函数,在进行今天的内容之前我们先事先说明一下,下面我们对单表进行操作的表结构如下:

type Student struct {
  ID   uint   `gorm:"size:3"`
  Name string `gorm:"size:8"`
  Age  int    `gorm:"size:3"`
  Sex  string `gorm:"size:3"`
  Email *string `gorm:"size:32"`
}

好了,话不多说,开始我们今天的内容

表的初始化

首先我们确定一下我们已经将要演示的数据库内相关内容清空掉了,然后我们就可以开始连接数据库并创建students表了:

package main
import (
  "fmt"
  "gorm.io/driver/mysql"
  "gorm.io/gorm"
)
type Student struct {
  ID    uint    `gorm:"size:3"`
  Name  string  `gorm:"size:8"`
  Age   int     `gorm:"size:3"`
  Sex   string  `gorm:"size:3"`
  Email *string `gorm:"size:32"`
}
var myDB *gorm.DB
func init() {
  user := "root"
  password := "aaaa"
  dbname := "gorm"
  ip := "127.0.0.1"
  port := "3306"
  dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", user, password, ip, port, dbname)
  db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
  if err != nil {
    fmt.Println("数据库连接失败,err:", err)
    return
  }
  fmt.Println("数据库连接成功")
  myDB = db
}
func main() {
  err := myDB.AutoMigrate(&Student{})
  if err != nil {
    fmt.Println("students表创建失败,err:", err)
    return
  }
  fmt.Println("students表创建成功")
}

单表操作

创建并插入数据

插入单条数据

func SingleInsert(student Student) {
  res := myDB.Create(&student)
  if res.Error != nil {
    fmt.Println("插入数据失败,err:", res.Error)
    return
  }
  fmt.Println("插入数据成功")
}
func main() {
  err := myDB.AutoMigrate(&Student{})
  if err != nil {
    fmt.Println("students表创建失败,err:", err)
    return
  }
  fmt.Println("students表创建成功")
  email := "fengxu@163.com"
  student := Student{
    Name:  "fengxu",
    Age:   18,
    Sex:   "男",
    Email: &email,
  }
  SingleInsert(student)
}

运行成功,查找数据库:

这样我们就成功将一条记录插入数据库了

批量插入数据

func Insert(StudentList []Student) {
  var StudentList []Student
  for i := 0; i < 10; i++ {
    email := fmt.Sprintf("No.%d@163.com", i)
    student := Student{
      Name:  fmt.Sprintf("No.%d", i),
      Age:   18 + i,
      Sex:   "男",
      Email: &email,
    }
    StudentList = append(StudentList, student)
  }
  res := myDB.Create(&StudentList)
  if res.Error != nil {
    fmt.Println("插入数据失败,err:", res.Error)
    return
  }
  fmt.Println("插入数据成功")
}

这显示我们成功向students表中批量插入数据。

插入数据的细节

  • 这里的email我为了表示它可以为空,将它的类型设置成了指针,所以我们在传值的时候也要传指针
  • 我们在使用Create函数时,传递的是指针,而不是具体值
  • 由于我们传入到Create函数的是student的指针,所以student在此之后就会出现该记录的其他消息了,比如下面这样:
func SingleInsert(student Student) {
  //插入单条数据
  email := "fengxu@163.com"
  student := Student{
    Name:  "luoyu",
    Age:   18,
    Sex:   "男",
    Email: &email,
  }
  SingleInsert(student)
  res := myDB.Create(&student)
  if res.Error != nil {
    fmt.Println("插入数据失败,err:", res.Error)
    return
  }
  fmt.Println("插入数据成功")
  fmt.Println(student)
}

打印的结果为:

{13 luoyu 18 男 0xc0001e0830}

单表插入的完整代码:

package main
import (
  "fmt"
  "gorm.io/driver/mysql"
  "gorm.io/gorm"
)
type Student struct {
  ID    uint    `gorm:"size:3"`
  Name  string  `gorm:"size:8"`
  Age   int     `gorm:"size:3"`
  Sex   string  `gorm:"size:3"`
  Email *string `gorm:"size:32"`
}
var myDB *gorm.DB
func init() {
  user := "root"
  password := "ba161754"
  dbname := "gorm"
  ip := "127.0.0.1"
  port := "3306"
  dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", user, password, ip, port, dbname)
  db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
  if err != nil {
    fmt.Println("数据库连接失败,err:", err)
    return
  }
  fmt.Println("数据库连接成功")
  myDB = db
}
func SingleInsert(student Student) {
  res := myDB.Create(&student)
  if res.Error != nil {
    fmt.Println("插入数据失败,err:", res.Error)
    return
  }
  fmt.Println("插入数据成功")
  fmt.Println(student)
}
func Insert(StudentList []Student) {
  res := myDB.Create(&StudentList)
  if res.Error != nil {
    fmt.Println("插入数据失败,err:", res.Error)
    return
  }
  fmt.Println("插入数据成功")
}
func main() {
  err := myDB.AutoMigrate(&Student{})
  if err != nil {
    fmt.Println("students表创建失败,err:", err)
    return
  }
  fmt.Println("students表创建成功")
  //插入单条数据
  email := "fengxu@163.com"
  student := Student{
    Name:  "luoyu",
    Age:   18,
    Sex:   "男",
    Email: &email,
  }
  SingleInsert(student)
  //批量插入数据
  var StudentList []Student
  for i := 0; i < 10; i++ {
    email := fmt.Sprintf("No.%d@163.com", i)
    student := Student{
      Name:  fmt.Sprintf("No.%d", i),
      Age:   18 + i,
      Sex:   "男",
      Email: &email,
    }
    StudentList = append(StudentList, student)
  }
  Insert(StudentList)
}

单表查询

前言

在讲解单表查询之前,我们先来看一个很简单的单表查询代码

package main
import (
  "fmt"
  "gorm.io/driver/mysql"
  "gorm.io/gorm"
  "gorm.io/gorm/logger"
  "log"
  "os"
  "time"
)
type Student struct {
  ID    uint    `gorm:"size:3"`
  Name  string  `gorm:"size:8"`
  Age   int     `gorm:"size:3"`
  Sex   string  `gorm:"size:3"`
  Email *string `gorm:"size:32"`
}
var myDB *gorm.DB
var mysqllogger logger.Interface
func init() {
  user := "root"
  password := "ba161754"
  dbname := "gorm"
  ip := "127.0.0.1"
  port := "3306"
  dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", user, password, ip, port, dbname)
  db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
  if err != nil {
    fmt.Println("数据库连接失败,err:", err)
    return
  }
  fmt.Println("数据库连接成功")
  myDB = db
}
func initLogger() {
  var mysqlLogger logger.Interface
  mysqlLogger = logger.Default.LogMode(logger.Info) //设置日志打印级别
  mysqlLogger = logger.New(
    log.New(os.Stdout, "\r\n", log.LstdFlags), // (日志输出的目标,前缀和日志包含的内容)
    logger.Config{
      SlowThreshold:             time.Second, // 慢 SQL 阈值
      LogLevel:                  logger.Info, // 日志级别
      IgnoreRecordNotFoundError: true,        // 忽略ErrRecordNotFound(记录未找到)错误
      Colorful:                  true,        // 使用彩色打印
    },
  )
  myDB.Logger = mysqlLogger
}
func main() {
  var student Student
  initLogger()
  myDB.Session(&gorm.Session{
    Logger: mysqllogger,
  })
  myDB.Take(&student) //默认查找第一条数据
  fmt.Println(student)
  student = Student{}
  myDB.First(&student) //查找第一条数据
  fmt.Println(student)
  student = Student{}
  myDB.Last(&student) //查找最后一条数据
  fmt.Println(student)
}

输出结果为:

这里我在打印出查询结果的同时也打印出了它们原生的sql语句,我们可以看到相对于原生sql语句,GORM的语句相对比较简单。

当然和我们平时利用各种各样的筛选条件来进行查询,我们在GORM也可以利用各种各样的条件来完成查询,,最后我们介绍一下下面我们可能会使用的函数:

  • Take:用于从数据库中检索符合条件的第一条记录,并将其填充到指定的结构体中。如果没有指定特定的条件,它会默认返回第一条记录。
  • Find:用于从数据库中检索符合条件的所有记录,并将它们填充到指定的结构体切片中。你可以使用Where函数来添加条件限制,以便只检索满足特定条件的记录。

单条记录查询

  • 根据主键查询
myDB.Take(&student, 1)
  fmt.Println(student)
  myDB.Take(&student, "1")
  fmt.Println(student)

注意:这里指定主键时可以是数字也可以是字符串

  • 根据其他条件来查询
myDB.Take(&student, "name = ?", "fengxu")
  fmt.Println(student)

这里我们用?来作为占位符,这样可以有效的防止sql注入

  • 根据struct来查询
myDB.Where(&Student{Name: "fengxu"}).Find(&student)
  fmt.Println(student)
  student = Student{}
  • 获取查询结果
var studentList []Student
    err := myDB.Find(&studentList).Error
  switch {
  case errors.Is(err, gorm.ErrRecordNotFound):
    fmt.Println("没有找到数据")
  default:
    fmt.Println("sql语句出现问题")
  }
  count := myDB.Find(&studentList).RowsAffected  //获取查询结果条数
  fmt.Println(count)
}

完整代码:

func SingleSerach() {
  var student Student
  var studentList []Student
  //根据主键查询
  myDB.Take(&student, 1)
  fmt.Println(student)
  student = Student{}
  myDB.Take(&student, "3")
  fmt.Println(student)
  student = Student{}
  //根据字段查询
  myDB.Take(&student, "name = ?", "fengxu")
  fmt.Println(student)
  student = Student{}
  //根据struct来查询
  //student.ID = 4
  myDB.Where(&Student{Name: "fengxu"}).Find(&student)
  fmt.Println(student)
  student = Student{}
  err := myDB.Find(&studentList).Error
  switch {
  case errors.Is(err, gorm.ErrRecordNotFound):
    fmt.Println("没有找到数据")
  default:
    fmt.Println("sql语句出现问题")
  }
  count := myDB.Find(&studentList).RowsAffected
  fmt.Println(count)
}

多条记录查询

  • 简单示例:
func MultipleSearch() {
  var studentList []Student
  myDB.Find(&studentList)
  for _, v := range studentList {
    fmt.Println(v)
  }
  //上面我们打印的email是地址,我们需要对它进行序列化
  for _, v := range studentList {
    data, _ := json.Marshal(v)
    fmt.Println(string(data))
  }
}
  • 按照主键来查询
studentList1 := []Student{}
  myDB.Find(&studentList1, 1, 2, 3, 4, 5, 6)
  for _, v := range studentList1 {
    data, _ := json.Marshal(v)
    fmt.Println(string(data))
  }
  studentList2 := []Student{}
  myDB.Find(&studentList2, []int{1, 2, 3, 4, 5, 6})
  for _, v := range studentList2 {
    data, _ := json.Marshal(v)
    fmt.Println(string(data))
  }

注意:这里其实用不用前片都可以,但是测试结果是切片速度明显较快,建议使用切片

  • 按照其他条件来查询
//根据字段查询
  studentList := []Student{}
  myDB.Where("name in ?", []string{"fengxu", "luoyu"}).Find(&studentList)
  for _, v := range studentList {
    data, _ := json.Marshal(v)
    fmt.Println(string(data))
  }

全部代码:

func MultipleSearch() {
  var studentList []Student
  myDB.Find(&studentList)
  for _, v := range studentList {
    fmt.Println(v)
  }
  //按主键查询
  studentList1 := []Student{}
  myDB.Find(&studentList1, 1, 2, 3, 4, 5, 6)
  for _, v := range studentList1 {
    data, _ := json.Marshal(v)
    fmt.Println(string(data))
  }
  
  studentList2 := []Student{}
  myDB.Find(&studentList2, []int{1, 2, 3, 4, 5, 6})
  for _, v := range studentList2 {
    data, _ := json.Marshal(v)
    fmt.Println(string(data))
  }
  //根据字段查询
  studentList = []Student{}
  myDB.Where("name in ?", []string{"fengxu", "luoyu"}).Find(&studentList)
  for _, v := range studentList {
    data, _ := json.Marshal(v)
    fmt.Println(string(data))
  }
}

单表数据的更新

示例:

func Update() {
  var student Student
  myDB.Take(&student, 1)
  student.Name = "三玖"
  student.Sex = "女"
  myDB.Save(&student)
  fmt.Println(student)
}

注意:

  • Save会保留所有的字段,哪怕我们将字段的值设为0
  • Save函数其实是一个组合操作,如果当前不存在该字段,则是执行Create否则执行Update
  • 不要将 SaveModel一同使用, 这是 未定义的行为

单列更新

  • 更新单列指定字段
//更新指定字段
  student = Student{}
  myDB.Take(&student)
  student.Age = 19
  myDB.Select("age").Save(&student)
  fmt.Println(student)
  • 批量更新
//同时更新单个指定字段
  studentList := []Student{}
  myDB.Where("name like ?", "No.%").Find(&studentList).Update("age", 19)
  for _, v := range studentList {
    fmt.Println(v.Name, v.Age)
  }
  
  //同时更新多个指定字段
  Email := "sanjiu@163.com"
  new_student := Student{
    Age:   20,
    Sex:   "女",
    Email: &Email,
  }
  myDB.Model(&student).Where("name=?", "三玖").Updates(new_student)  //方法一:struct
  
  myDB.Model(&student).Where("name=?", "三玖").Updates(map[string]interface{}{
    "age":   20,
    "sex":   "女",
    "email": "sanjiu@163.com",
  })//  方法二:map

注意:Updates函数在struct方法更新字段时会自动忽略零值,如果想避免建议使用map或使用Select函数说明一下要更新的字段,示例如下:

DB.Model(&Student{}).Where("age = ?", 21).Select("gender", "email").Updates(Student{
  Email:  &email,
  Gender: false,
})

单表删除

func Delete() {
  var student Student
  var studentlist []Student
  myDB.Take(&student, 1)
  myDB.Delete(&student) //单行删除
  myDB.Take(&studentlist, []int{1, 2, 3})
  myDB.Delete(&studentlist) //批量删除
}

代码汇总

以上有关单表操作的全部代码:

package main
import (
  "encoding/json"
  "errors"
  "fmt"
  "gorm.io/driver/mysql"
  "gorm.io/gorm"
  "gorm.io/gorm/logger"
  "log"
  "os"
  "time"
)
type Student struct {
  ID    uint    `gorm:"size:3"`
  Name  string  `gorm:"size:8"`
  Age   int     `gorm:"size:3"`
  Sex   string  `gorm:"size:3"`
  Email *string `gorm:"size:32"`
}
var myDB *gorm.DB
var mysqllogger logger.Interface
func init() {
  user := "root"
  password := "nicai"
  dbname := "gorm"
  ip := "127.0.0.1"
  port := "3306"
  dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", user, password, ip, port, dbname)
  db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
  if err != nil {
    fmt.Println("数据库连接失败,err:", err)
    return
  }
  fmt.Println("数据库连接成功")
  myDB = db
}
func initLogger() {
  var mysqlLogger logger.Interface
  mysqlLogger = logger.Default.LogMode(logger.Info) //设置日志打印级别
  mysqlLogger = logger.New(
    log.New(os.Stdout, "\r\n", log.LstdFlags), // (日志输出的目标,前缀和日志包含的内容)
    logger.Config{
      SlowThreshold:             time.Second, // 慢 SQL 阈值
      LogLevel:                  logger.Info, // 日志级别
      IgnoreRecordNotFoundError: true,        // 忽略ErrRecordNotFound(记录未找到)错误
      Colorful:                  true,        // 使用彩色打印
    },
  )
  myDB.Logger = mysqlLogger
}
func SingleSerach() {
  var student Student
  var studentList []Student
  //根据主键查询
  myDB.Take(&student, 1)
  fmt.Println(student)
  student = Student{}
  myDB.Take(&student, "3")
  fmt.Println(student)
  student = Student{}
  //根据字段查询
  myDB.Take(&student, "name = ?", "fengxu")
  fmt.Println(student)
  student = Student{}
  //根据struct来查询
  //student.ID = 4
  myDB.Where(&Student{Name: "fengxu"}).Find(&student)
  fmt.Println(student)
  student = Student{}
  err := myDB.Find(&studentList).Error
  switch {
  case errors.Is(err, gorm.ErrRecordNotFound):
    fmt.Println("没有找到数据")
  default:
    fmt.Println("sql语句出现问题")
  }
  count := myDB.Find(&studentList).RowsAffected
  fmt.Println(count)
}
func MultipleSearch() {
  var studentList []Student
  myDB.Find(&studentList)
  for _, v := range studentList {
    fmt.Println(v)
  }
  //按主键查询
  studentList1 := []Student{}
  myDB.Find(&studentList1, 1, 2, 3, 4, 5, 6)
  for _, v := range studentList1 {
    data, _ := json.Marshal(v)
    fmt.Println(string(data))
  }
  studentList2 := []Student{}
  myDB.Find(&studentList2, []int{1, 2, 3, 4, 5, 6})
  for _, v := range studentList2 {
    data, _ := json.Marshal(v)
    fmt.Println(string(data))
  }
  //根据字段查询
  studentList = []Student{}
  myDB.Where("name in ?", []string{"fengxu", "luoyu"}).Find(&studentList)
  for _, v := range studentList {
    data, _ := json.Marshal(v)
    fmt.Println(string(data))
  }
}
func Update() {
  //update操作示例
  var student Student
  myDB.Take(&student, 1)
  student.Name = "三玖"
  student.Sex = "女"
  myDB.Save(&student)
  fmt.Println(student)
  //更新指定字段
  student = Student{}
  myDB.Take(&student)
  student.Age = 19
  myDB.Select("age").Save(&student)
  fmt.Println(student)
  //同时更新多列的指定字段
  studentList := []Student{}
  myDB.Where("name like ?", "No.%").Find(&studentList).Update("age", 19)
  for _, v := range studentList {
    fmt.Println(v.Name, v.Age)
  }
  //同时更新多列
  Email := "sanjiu@163.com"
  new_student := Student{
    Age:   20,
    Sex:   "女",
    Email: &Email,
  }
  myDB.Model(&student).Where("name=?", "三玖").Updates(new_student) //方法一:struct
  myDB.Model(&student).Where("name=?", "三玖").Updates(map[string]interface{}{
    "age":   20,
    "sex":   "女",
    "email": "sanjiu@163.com",
  }) //  方法二:map
}
func Delete() {
  var student Student
  var studentlist []Student
  myDB.Take(&student, 1)
  myDB.Delete(&student) //单行删除
  myDB.Take(&studentlist, []int{1, 2, 3})
  myDB.Delete(&studentlist) //批量删除
}
func main() {
  //var student Student
  initLogger()
  myDB.Session(&gorm.Session{
    Logger: mysqllogger,
  })
  //myDB.Take(&student) //默认查找第一条数据
  //fmt.Println(student)
  //student = Student{}
  //
  //myDB.First(&student) //查找第一条数据
  //fmt.Println(student)
  //student = Student{}
  //
  //myDB.Last(&student) //查找最后一条数据
  //fmt.Println(student)
  Delete()
}

结语

上面仅仅是我基于Gorm框架学习的一些笔记,详细可以参考:

GORM官方文档

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1天前
|
Go 定位技术 索引
Go 语言Map(集合) | 19
Go 语言Map(集合) | 19
|
1天前
|
Go
go语言注释,标识符 | 17
go语言注释,标识符 | 17
|
2天前
|
存储 缓存 Go
go语言编程系列(五)
go语言编程系列(五)
|
2天前
|
搜索推荐 Java 编译器
go语言编程系列(四)
go语言编程系列(四)
|
1天前
|
存储 缓存 安全
速成班!去繁存简,一文让你学会go语言基础!!
速成班!去繁存简,一文让你学会go语言基础!!
|
2天前
|
存储 JSON 安全
go语言编程系列(七)
go语言编程系列(七)
|
2天前
|
存储 安全 编译器
go语言编程系列(六)
go语言编程系列(六)
|
8天前
|
Shell Go API
Go语言grequests库并发请求的实战案例
Go语言grequests库并发请求的实战案例
|
5天前
|
安全 Go 调度
探索Go语言的并发模型:Goroutines与Channels的魔术
在这个快节奏的技术世界中,Go语言以其简洁的语法和强大的并发处理能力脱颖而出。本文将带你深入了解Go语言的并发模型,如何通过Goroutines和Channels来优雅地处理并发任务,以及它们如何帮助我们构建高效、可扩展的应用程序。
|
2天前
|
自然语言处理 Java 测试技术
go语言编程系列(二)
go语言编程系列(二)