Go语言之Gorm框架(一) ——初窥Gorm框架

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群版 2核4GB 100GB
推荐场景:
搭建个人博客
日志服务 SLS,月写入数据量 50GB 1个月
简介: Go语言之Gorm框架(一) ——初窥Gorm框架

Gorm和Mysql驱动的安装

打开终端,输入下列命令即可:

go get gorm.io/driver/mysql
go get gorm.io/gorm

Gorm连接数据库

示例

package main
import (
  "fmt"
  "github.com/sirupsen/logrus"
  "gorm.io/driver/mysql"
  "gorm.io/gorm"
)
func init() {
  //数据库连接信息
  username := "root"
  password := "123456"
  databasename := "gorm"
  localHost := "localhost"
  port := 3306
  dns := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local",
    username, password, localHost, port, databasename)
  db, err := gorm.Open(mysql.Open(dns))
  if err != nil {
    logrus.Error("数据库连接失败", err)
  }
  fmt.Println("数据库连接成功", db)
}
func main() {
}

数据库连接的细节

  • 跳过默认事务
    为了保证数据一致性,Gorm会在事务中去执行去执行增删查改,如果我们没有这个需求可以选择跳过默认事务:
db, err := gorm.Open(mysql.Open(dns),&gorm.Config{SkipDefaultTransaction: true})
  • 命名策略
    在grom中默认表名是复数,字段是单数,比如下面我们创建一张student表,代码是这样的:
package main
import (
  "fmt"
  "github.com/sirupsen/logrus"
  "gorm.io/driver/mysql"
  "gorm.io/gorm"
)
var dB *gorm.DB
type Student struct {
  Name string
  Age  int
  Sex  string
}
func init() {
  //数据库连接信息
  username := "root"
  password := "ba161754"
  databasename := "gorm"
  localHost := "localhost"
  port := 3306
  dns := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local",
    username, password, localHost, port, databasename)
  db, err := gorm.Open(mysql.Open(dns), &gorm.Config{SkipDefaultTransaction: true})
  if err != nil {
    logrus.Error("数据库连接失败", err)
  }
  dB = db
}
func main() {
  err := dB.AutoMigrate(Student{})
  if err != nil {
    logrus.Error("数据库迁移失败", err)
  }
  fmt.Println("创建表成功")
}

创建出来的表是这样的:

当然我们也可以尝试修改这种命名策略:

db, err := gorm.Open(mysql.Open(dns), &gorm.Config{SkipDefaultTransaction: true,
    NamingStrategy: schema.NamingStrategy{   //
      TablePrefix:   "t_",  //表名前缀
      SingularTable: false, //禁用表名复数
      NoLowerCase:   false, //禁用小写
    }})
  • 日志显示
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,        // 使用彩色打印
    },
  )
  dB.Logger = mysqlLogger
}

完整代码,仅供参考:

package main
import (
  "fmt"
  "github.com/sirupsen/logrus"
  "gorm.io/driver/mysql"
  "gorm.io/gorm"
  "gorm.io/gorm/logger"
  "gorm.io/gorm/schema"
  "log"
  "os"
  "time"
)
var dB *gorm.DB
type Student struct {
  Name string
  Age  int
  Sex  string
}
func ConnectDB() {
  //数据库连接信息
  username := "root"
  password := "ba161754"
  databasename := "gorm"
  localHost := "localhost"
  port := 3306
  var err error
  dns := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local",
    username, password, localHost, port, databasename)
  dB, err = gorm.Open(mysql.Open(dns), &gorm.Config{SkipDefaultTransaction: true,
    NamingStrategy: schema.NamingStrategy{ //
      TablePrefix:   "t_",  //表名前缀
      SingularTable: false, //禁用表名复数
      NoLowerCase:   false, //禁用小写
    }})
  if err != nil {
    logrus.Error("数据库连接失败", err)
  }
}
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,        // 使用彩色打印
    },
  )
  dB.Logger = mysqlLogger
}
func init() {
  ConnectDB()
  initLogger()
}
func main() {
  err := dB.AutoMigrate(Student{})
  if err != nil {
    logrus.Error("数据库迁移失败", err)
  }
  fmt.Println("创建表成功")
}

模型定义

模型定义示例

模型是使用普通结构体定义的。 这些结构体可以包含具有基本Go类型、指针或这些类型的别名,甚至是自定义类型(只需要实现 database/sql 包中的Scanner和Valuer接口)我们来看一下Gorm给出的user模型示例:

type User struct {
  ID           uint           // Standard field for the primary key
  Name         string         // 一个常规字符串字段
  Email        *string        // 一个指向字符串的指针, allowing for null values
  Age          uint8          // 一个未签名的8位整数
  Birthday     *time.Time     // A pointer to time.Time, can be null
  MemberNumber sql.NullString // Uses sql.NullString to handle nullable strings
  ActivatedAt  sql.NullTime   // Uses sql.NullTime for nullable time fields
  CreatedAt    time.Time      // 创建时间(由GORM自动管理)
  UpdatedAt    time.Time      // 最后一次更新时间(由GORM自动管理)
}

这里常见的uint这种类型就不做过多介绍了,这里主要是 有两个类型我们这里进行一下介绍:

  • *string(指针类型) :如果我们在这里使用string类型的话,这里我们是可以写空值的,如果我们用string类型是不允许出现空值的
  • sql.NullString sql.NullString 是 Go 语言标准库中的一个数据类型,位于 database/sql 包中。它用于表示数据库中可能为 NULL 的字符串值。它由两个字段组成:String 用于保存字符串值(如果不为 NULL),Valid 是一个布尔标志,指示字符串值是否为 NULL。在与允许字符串列包含 NULL 值的数据库一起工作时,这种类型特别有用。
type NullString struct {
  String string
  Valid  bool // Valid is true if String is not NULL
}

gorm.Model

在开始介绍gorm.Model之前,我们先讲一下几条在gorm的约定:

-主键:GORM 使用一个名为ID 的字段作为每个模型的默认主键。

  • 表名:默认情况下,GORM 将结构体名称转换为 snake_case 并为表名加上复数形式。 例如,一个 User 结构体在数据库中的表名变为 users 。
  • 列名:GORM 自动将结构体字段名称转换为 snake_case 作为数据库中的列名。
  • 时间戳字段:GORM使用字段 CreatedAt 和 UpdatedAt 来自动跟踪记录的创建和更新时间。

而在grom中存在gorm.Model这一预定义的结构体,我们可以将它直接嵌入我们所定义的结构体中,这保证了不同模型之间保持一致性并利用GORM内置的约定,gorm.model的定义如下:

type Model struct {
  ID        uint `gorm:"primarykey"` 
  CreatedAt time.Time
  UpdatedAt time.Time
  DeletedAt DeletedAt `gorm:"index"`
}

它主要包含以下字段:

  • ID :每个记录的唯一标识符(主键)。
  • CreatedAt :在创建记录时自动设置为当前时间。
  • UpdatedAt:每当记录更新时,自动更新为当前时间。
  • DeletedAt:用于软删除(将记录标记为已删除,而实际上并未从数据库中删除)。

字段标签

gorm中我们一般使用字段标签来表示字段的类型,常见的字段类型主要有以下几种:

  • type:定义字段类型
  • size:字段大小
  • column 自定义别名
  • primaryKey 将列定义为主键
  • unique 将列定义为唯一键
  • default 定义列的默认值
  • not null 不可为空
  • embedded 嵌套字段
  • embeddedPrefix 嵌套字段前缀
  • comment 注释

示例:

type StudentInfo struct {
 Email  *string `gorm:"size:32"` // 使用指针是为了存空值
 Addr   string  `gorm:"column:y_addr;size:16"`
 Gender bool    `gorm:"default:true"`
}
type Student struct {
 Name string      `gorm:"type:varchar(12);not null;comment:用户名"`
 UUID string      `gorm:"primaryKey;unique;comment:主键"`
 Info StudentInfo `gorm:"embedded;embeddedPrefix:s_"`
}
相关文章
|
5天前
|
存储 Go API
一个go语言编码的例子
【7月更文挑战第2天】本文介绍Go语言使用Unicode字符集和UTF-8编码。Go中,`unicode/utf8`包处理编码转换,如`EncodeRune`和`DecodeRune`。`golang.org/x/text`库支持更多编码转换,如GBK到UTF-8。编码规则覆盖7位至21位的不同长度码点。
70 1
一个go语言编码的例子
|
8天前
|
JSON 算法 测试技术
在go语言中调试程序
【6月更文挑战第29天】Go语言内置`testing`包支持单元测试、基准测试和模糊测试。`go test`命令可执行测试,如`-run`选择特定测试,`-bench`运行基准测试,`-fuzz`进行模糊测试。
17 2
在go语言中调试程序
|
6天前
|
安全 Go
Go语言的iota关键字有什么用途?
**Go语言中的`iota`是常量生成器,用于在`const`声明中创建递增的常量。`iota`在每个新的`const`块重置为0,然后逐行递增,简化了枚举类型或常量序列的定义。例如,定义星期枚举:** ```markdown ```go type Weekday int const ( Sunday Weekday = iota // 0 Monday // 1 Tuesday // 2 ... ) ``` 同样,`iota`可用于定义不同组的常量,如状态码和标志位,保持各自组内的递增,提高代码可读性。
|
2天前
|
监控 搜索推荐 Go
万字详解!在 Go 语言中操作 ElasticSearch
本文档通过示例代码详细介绍了如何在Go应用中使用`olivere/elastic`库,涵盖了从连接到Elasticsearch、管理索引到执行复杂查询的整个流程。
8 0
|
6天前
|
IDE Linux Go
记录一个go语言与IDE之间的问题
【7月更文挑战第1天】本文介绍在IDE中调试Go应用可能遇到的问题。当问题与IDE的自动完成有关,可以试着使用其他编辑器如Linux的vim是否无此问题。这可以验证表明IDE可能不完全兼容最新语言版本,建议使用无自动检测工具临时解决。
22 0
|
15天前
|
安全 测试技术 Go
Go语言在高并发场景下的应用
在当今互联网高速发展的时代,高并发已成为众多应用系统面临的核心问题。本文探讨了Go语言在高并发场景下的优势,并通过具体实例展示了其在实际应用中的效果和性能表现。
|
12天前
|
Go
go语言map、实现set
go语言map、实现set
14 0
|
12天前
|
Go
go语言数组与切片
go语言数组与切片
17 0
|
14天前
|
存储 中间件 Go
在go语言服务中封装路由和示例
【6月更文挑战第23天】本文介绍golang后端按协议处理、中间件(一次性与每次请求执行)划分、以及服务架构Controller、Logic/Service、DAO/Repository和Routers划分。代码仓库在GitHub上提供。使用框架简化了交互和处理。后续章节深入探讨服务构建。
107 5
在go语言服务中封装路由和示例