Go ORM

8 min

:open_book:什么是ORM

  • 对象和数据库表中原本没有直接映射关系
  • 为了解决这个问题有了ORM技术

Gorm

安装命令

go get -u gorm.io/gorm
go get -u gorm.io/driver/sqlite
  • 检查是否安装
    go list -m gorm.io/gorm

模型定义

  • 从数据库读取的数据会先保存到预先定义的模型对象,然后我们就可以从模型对象得到我们想要的数据。
  • 插入数据到数据库也是先新建一个模型对象,然后把想要保存的数据先保存到模型对象,然后把模型对象保存到数据库。
  1. 定义gorm model

    1. //字段注释说明了gorm库把struct字段转换为表字段名长什么样子。
        type Food struct {
      Id         int  //表字段名为:id
      Name       string //表字段名为:name
      Price      float64 //表字段名为:price
      TypeId     int  //表字段名为:type_id
            
      //字段定义后面使用两个反引号``包裹起来的字符串部分叫做标签定义,这个是golang的基础语法,不同的库会定义不同的标签,有不同的含义
      CreateTime int64 `gorm:"column:createtime"`  //表字段名为:createtime
       }
    2. 结构体的嵌套

      • 在golang中gorm模型定义是通过struct实现的,这样我们就可以通过gorm库实现struct类型和mysql表数据的映射。
      • GORM 定义一个 gorm.Model 结构体,其包括字段 ID、CreatedAt、UpdatedAt、DeletedAt。
      // gorm.Model 的定义
      type Model struct {
        ID        uint           `gorm:"primaryKey"`
        CreatedAt time.Time
        UpdatedAt time.Time
        DeletedAt gorm.DeletedAt `gorm:"index"`
      }

      以将它嵌入到我们的结构体中,就以包含这几个字段,类似继承的效果。

    3. 例子:

      type User struct {
        gorm.Model // 嵌入gorm.Model的字段
        Name string
      }
  2. gorm模型标签

    • gorm:"column:createtime"这样的标签定义语法,定义struct字段的列名(表字段名)。 gorm标签语法:gorm:"标签定义"
  3. 自动更新时间

    • GORM 约定使用 CreatedAt、UpdatedAt 追踪创建/更新时间。如果定义了这种字段,GORM 在创建、更新时会自动填充当前时间。

    • 要使用不同名称的字段,您可以配置 autoCreateTime、autoUpdateTime 标签

    • 如果想要保存 UNIX(毫/纳)秒时间戳,而不是 time,只需简单地将 time.Time 修改为 int 即可。

      • 例子:

        type User struct {
          CreatedAt time.Time // 默认创建时间字段, 在创建时,如果该字段值为零值,则使用当前时间填充
          UpdatedAt int       // 默认更新时间字段, 在创建时该字段值为零值或者在更新时,使用当前时间戳秒数填充
          Updated   int64 `gorm:"autoUpdateTime:nano"` // 自定义字段, 使用时间戳填纳秒数充更新时间
          Updated   int64 `gorm:"autoUpdateTime:milli"` //自定义字段, 使用时间戳毫秒数填充更新时间
          Created   int64 `gorm:"autoCreateTime"`      //自定义字段, 使用时间戳秒数填充创建时间

定义表名

这是想自定义表名的情况,如果没有这个函数,那么表名就是你AutoMigrate传入的结构体的名字

  • 可以通过定义struct类型的TableName函数实现定义模型的表名

    func (p Product) TableName() string{
        return "product"
    }
    • 为结构体实现TableName的接口

    • 返回的字符串就是表名,这里是product

自动建表

  • 在 Gorm 中,你通过定义模型结构体来表示数据库中的表。每个模型结构体对应一个数据库表,结构体中的字段对应表中的列。然后,通过 Gorm 的 AutoMigrate 方法,你可以自动生成或更新数据库中的表结构。

连接数据库

两个步骤

  1. 配置DSN

    :bookmark:数据库的名字是在dsn中起的

    1. DSN(数据源名称)

      • 格式

        username:password@tcp(host:port)/Dbname?charset=utf8&parseTime=True&loc=Local\

        参数描述
        username数据库账号
        password数据库密码
        host数据库连接地址,可以是 IP 或域名
        port数据库端口(例如:3306)
        dbname数据库名
        charset客户端字符集(例如:utf8
        parseTime支持把数据库 datetimedate 类型转换为 Go 的 time.Time 类型(例如:true
        loc使用系统本地时区(例如:Local
  2. 使用gorm.Open连接数据库

    • 使用gorm.Open连接数据库

      db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
      if err != nil {
      		panic("连接数据库失败, error=" + err.Error())
      	}
    • 返回的db是gorm.DB

      • 这个是GORM(一个流行的Go语言ORM库)的主要数据类型
      • db封装了数据库连接、会话和操作结果,大部分操作都要它

连接池

CRUD

  1. 创建数据

    //定义一个用户,并初始化数据
    u := User{
    	Username:"tizi365",
    	Password:"123456",
    	CreateTime:time.Now().Unix(),
    }
    //插入一条用户数据
    db.Create(&u)
    
    //一般项目中我们会类似下面的写法,通过Error对象检测,插入数据有没有成功,如果没有错误那就是数据写入成功了。
    if err := db.Create(&u).Error; err != nil {
    	fmt.Println("插入失败", err)
    	return
    }
  2. 查询数据

    传入的参数是model类型的指针,在gorm中是用模型来创建表,这样就间接定位到了要查哪个表

    :heavy_check_mark:我们需要定义一个结构体,用来存放查询的结果

    //定义接收查询结果的结构体变量
    food := Food{}
    //查询 
    db.Take(&food)
    • Take换成First/Last就是查询第一条/最后一条数据

    :heavy_check_mark: 把Take换成Find就是查询多条数据

    Find函数返回的是一个数组, 我们需要定义一个数组,用来存放查询的结果

    //因为Find返回的是数组,所以定义一个商品数组用来接收结果
    var foods []Food
    
    db.Find(&foods)
    • 查询错误处理
      • 通过db.Error属性判断查询结果是否出错, Error属性不等于nil表示有错误发生。

        if err := db.Take(&food).Error; err != nil {
            fmt.Println("查询失败", err)
        }
  3. 更新数据

    :heavy_check_mark: Save (可以修改整个模型的值,比如food模型)

    db.Save(&food)

    :heavy_check_mark: Update(更新单个字段值)

    db.Model(&food).Update("price", 25)

    :heavy_check_mark:Updates (更新多个字段)

    food := Food{}
    updataFood := Food{
    		Price:120,
    		Title:"柠檬雪碧",
    	}
    db.Model(&food).Updates(&updataFood)
    //传入要更新的model(告诉要更新的结构体为food),之后传入要更新的结构体,都要为指针,因为要反写回来

    提示: 通过结构体变量更新字段值, gorm库会忽略零值字段。就是字段值等于0, nil, "", false这些值会被忽略掉,不会更新。如果想更新零值,可以使用map类型替代结构体。

  4. 删除数据

    db.Delete(&food)

常见错误

  1. UpdateAlphabet数据库更新错误: WHERE conditions required 表明在更新记录时,数据库要求提供 WHERE 条件,但没有找到相应的条件。这通常是因为模型对象缺少主键(或唯一标识符)信息。

:thinking: 参考文档

  1. GORM查询数据 - 梯子教程网 (tizi365.com)