Gorm | 青训营笔记

150 阅读4分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 12 天

Gorm -> The fantastic ORM library for Golang aims to be developer friendly.

本文介绍V2版本的使用

安装和导入

使用MySQL数据库

$ go get -u gorm.io/gorm 
$ go get -u gorm.io/driver/mysql 
import(
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    "fmt"
)

目前 Gorm 支持MySQL、SQLServer、PosrgreSQL、SQLite数据库,若需使用其他数据库,需下载和导入不同的驱动或自行开发驱动

基本使用

Gorm 支持 Migration 特性,支持根据 Go Struct 结构自动生成对应的表结构。

定义Grom Model

gorm.Model 是一个包含一些基本字段的结构体, 包含的字段有 IDCreatedAt, UpdatedAt, DeletedAt。可以用它来嵌入到结构体中

type Product struct {
    gorm.Model
    ID uint
    Code string
    Price uint
}

Gorm 默认使用ID字段作为主键,使用结构体的蛇形复数作为表名(即 type Product struct {} 默认表名是products)

如果设置禁用表名复数形式属性为 true,Product 的表名将是 product

db.SingularTable(true)

使用字段名的蛇形作为列名(或者使用 column 字段重写列名),并使用 CreatedAtUpdatedAt 字段追踪创建和更新时间

可以使用 default 标签为字段定义默认值

type Product struct {
    gorm.Model
    ID uint     `gorm:"primarykey"
    Code string `gorm:"default:A"`
    Price uint  `gorm:"default:0"`
}

还可以使用标签控制字段级别的权限

image.png

为model定义表名

如果不使用结构体复数作为表名,还可以设置为其他的

func (p Product) TableName() String {
    return "product"
}

连接数据库

db, err := gorm.Open(
    mysql.Open("username:password@tcp(localhost:3306)/dbname?charset=utf8"),
    &gorm.Config(){})
if err != nil {
    panic("fail to connect database")
}

建表

通过 AutoMigrate() 快速建表,如果表已经存在不会重复创建

// 根据 Product 结构体,自动创建表结构.
db.AutoMigrate(&Product{})

或者使用 Schema 方法

  1. 检查表是否存在
// 检测Product结构体对应的表是否存在
db.Migrator().HasTable(&Product{})

// 检测表名product是否存在
db.Migrator().HasTable("product")
  1. 建表
// 根据Product结构体建表
db.Migrator().CreateTable(&Product{})
  1. 删除表
// 删除Product结构体对应的表
db.Migrator().DropTable(&Product{})

// 删除表名为product的表
db.Migrator().DropTable("product")

创建数据

创建单条数据

db.Create(&Product{Code: "ABC", Price: 100})

p := &Product{Code: "DE"}
res := db.Create(p)
fmt.Println(res.err)
fmt.Println(res.ID)

还可使用clause.OnConflict处理数据冲突

p := &Product{Code: "DE", ID: 2}
db.Clause(clause.OnConflict{DoNothing: true}).Create(&p)

创建多条数据

products := []*Product{{Code: "FG", Price: 888}, {Code: "H"}}
res := db.Create(products)
fmt.Println(res.err)
for _, p := range products {
    fmt.Println(p.ID)
}

查询数据

按照主键升序获取第一条记录,First()查询不到数据返回ErrRecordNotFound

var product Product
db.First(&product)

db.Last(&product)

使用结构体查询多条数据,Find()查询不到数据不会返回错误

products := &Product{}
result := db.Where("price > 500").Find(&products)
//相当于 SELECT * FROM product WHERE price > 500;
fmt.Println(result.RowsAffected)
fmt.Println(result.Error)

db.Where(&Product{Code: "H", Price: 0}).Find(&products)

当使用结构体作为查询条件时,Gorm 只会查询非零字段。若字段值为0、false或者其他零值,该字段不会被用于构建查询条件,而使用Map可以构建条件

使用Map查询

db.Where(map[string]interface{}{"Code": "ABC", "Price": 100}).Find(&products)

也可以使用 IN LIKE AND OR 等关键字

更新数据

使用结构体更新只会更新非零值

db.Model(&product).Update("Price", 300)
db.Model(&product).Updates(Product{Price: 300, Code: "FG"})

使用Save()会默认更新该对象的所有字段,即使你没有赋值

var product Product 
db.Debug().Last(&product) 
fmt.Printf("product: %#v",product) 
product.Code = "AAA" 
product.Price = 1000 s
db.Debug().Save(&product) //会更新所有的字段

使用Map或者Select()可以更新零值

db.Model(&product).Updates(map[string]interface{}{"Price": 500, "Code": "HIJK"})

删除数据

硬删除

db.Delete(&product,1)
db.Delete(&product, []int{1,2,3})

软删除

如果模型包含了一个 gorm.deletedat 字段(gorm.Model 已经包含了该字段),它将自动获得软删除的能力!

记录不会被从数据库中真正删除,但 Gorm 会将 DeletedAt 置为当前时间。且正常的查询方法查询不到该记录

使用Unscoped可以查询到被软删除的数据

db.Unscoped().Where("price = 100").Find(&products)

也可以使用 Unscoped 永久删除匹配的记录

Gorm 事务

Begin() Commit() Rollback()

tx := db.Begin()
if err = tx.Create(&Product{Code: "IJK"}).Error; err != nil {
    tx.Rollback()
    return
}
tx.Commit()

或者使用Transaction()自动提交事务,避免漏写Commit()

if err = db.Transaction(func(tx *gorm.DB) error {
    if err = tx.Create(&Product{Code: "LMN"}).Error; err != nil {
        return err
    }
    return nil
}); err != nil {
    return
}

Gorm Hook

在创建、查询、更新、删除等操作前后自动调用的函数

Gorm允许用户自定义 BeforeSave, BeforeCreate, AfterSave, AfterCreate 等

若任何Hook返回错误,Gorm将停止后续操作并回滚事务

使用原生SQL

原生查询

var product Product
db.Raw("SELECT ID, code, price FROM product WHERE code = ?", "AAA").Scan(&product)

Exec 原生SQL

db.Exec("DROP TABLE product")

db.Exec("UPDATE product SET price = ? WHERE code = ?", gorm.Expr("price * ? + ?", 10, 500), "DE")

如果想更系统的学习Gorm的使用,还是看官网的文档比较好❤👌


参考资料

‍‌⁡‬Go 框架三件套详解.pptx - 飞书云文档 (feishu.cn)

Gorm框架 自动建表(Migration特性)

关于镜像 | 前言 |《GORM 中文文档 v2》| Go 技术论坛 (learnku.com)

GORM · Go语言中文文档 (topgoer.com)

GORM 指南 | GORM - The fantastic ORM library for Golang, aims to be developer friendly.