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

本文涉及的产品
云数据库 RDS MySQL,集群版 2核4GB 100GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
日志服务 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_"`
}
相关文章
|
2天前
|
JSON 测试技术 Go
零值在go语言和初始化数据
【7月更文挑战第10天】本文介绍在Go语言中如何初始化数据,未初始化的变量会有对应的零值:bool为`false`,int为`0`,byte和string为空,pointer、function、interface及channel为`nil`,slice和map也为`nil`。。本文档作为指南,帮助理解Go的数据结构和正确使用它们。
51 22
零值在go语言和初始化数据
|
3天前
|
JSON Java Go
Go 语言性能优化技巧
在Go语言中优化性能涉及数字字符串转换(如用`strconv.Itoa()`代替`fmt.Sprintf()`)、避免不必要的字符串到字节切片转换、预分配切片容量、使用`strings.Builder`拼接、有效利用并发(`goroutine`和`sync.WaitGroup`)、减少内存分配、对象重用(`sync.Pool`)、无锁编程、I/O缓冲、正则预编译和选择高效的序列化方法。这些策略能显著提升代码执行效率和系统资源利用率。
38 13
|
3天前
|
设计模式 Go
Go语言设计模式:使用Option模式简化类的初始化
在Go语言中,面对构造函数参数过多导致的复杂性问题,可以采用Option模式。Option模式通过函数选项提供灵活的配置,增强了构造函数的可读性和可扩展性。以`Foo`为例,通过定义如`WithName`、`WithAge`、`WithDB`等设置器函数,调用者可以选择性地传递所需参数,避免了记忆参数顺序和类型。这种模式提升了代码的维护性和灵活性,特别是在处理多配置场景时。
38 8
|
2天前
|
存储 Go
go语言中fmt格式化包和内置函数汇总
【7月更文挑战第10天】本文介绍fmt包和`Errorf`用于创建格式化的错误消息。`fmt`包还涉及一些接口,如`Formatter`、`GoStringer`、`ScanState`、`Scanner`和`Stringer`,支持自定义格式化和输入/输出处理。
16 1
|
2天前
|
Go
go语言中格式化输出的占位符
【7月更文挑战第10天】`fmt` 包在 Go 语言中用于格式化输出,包括不同类型的占位符:%v(默认格式)、%+v(带字段名的结构体)、%#v(Go语法表示)、%T(类型表示)、%%(百分号)。布尔值用%t,整数有%b、%c、%d、%o、%q、%x、%X和%U。浮点数和复数用%b、%e、%E、%f、%g、%G。字符串和字节切片用%s、%q、%x、%X。指针用%p。占位符可配合+、-、#、空格和0进行调整。宽度和精度控制输出格式,例如 %.4g 控制小数精度。Go 没有 `%u`,但无符号整数默认打印为正数。运算符包括逻辑、比较、加减、乘除、移位、按位和按位异或等。
15 1
|
17天前
|
Go
go语言map、实现set
go语言map、实现set
26 0
|
17天前
|
Go
go语言数组与切片
go语言数组与切片
25 0
|
4天前
|
安全 算法 程序员
在go语言中使用泛型和反射
【7月更文挑战第8天】本文介绍go支持泛型后,提升了代码复用,如操作切片、映射、通道的函数,以及自定义数据结构。 泛型适用于通用数据结构和函数,减少接口使用和类型断言。
62 1
在go语言中使用泛型和反射
|
6天前
|
缓存 编译器 Shell
回顾go语言基础中一些特别的概念
【7月更文挑战第6天】本文介绍Go语言基础涵盖包声明、导入、函数、变量、语句和表达式以及注释。零值可用类型如切片、互斥锁和缓冲,支持预分配容量以优化性能。
39 2
回顾go语言基础中一些特别的概念