10分钟了解Go的CRUD基础——GORM + MySQL
Go学习系列 Part7 了解Go的CRUD——GORM + MySQL
关于GORM
GORM 是一个流行的 Go 语言 ORM(对象关系映射)库,它允许开发者使用 Go 结构体来表示数据库中的表,并使用 Go 的方法来执行常见的数据库操作,如查询、插入、更新和删除。
特性
- 全功能 ORM
- 关联 (Has One,Has Many,Belongs To,Many To Many,多态,单表继承)
- Create,Save,Update,Delete,Find 中钩子方法
- 支持
Preload
、Joins
的预加载 - 事务,嵌套事务,Save Point,Rollback To Saved Point
- Context、预编译模式、DryRun 模式
- 批量插入,FindInBatches,Find/Create with Map,使用 SQL 表达式、Context Valuer 进行 CRUD
- SQL 构建器,Upsert,数据库锁,Optimizer/Index/Comment Hint,命名参数,子查询
- 复合主键,索引,约束
- Auto Migration
- 自定义 Logger
- 灵活的可扩展插件 API:Database Resolver(多数据库,读写分离)、Prometheus…
- 每个特性都经过了测试的重重考验
- 开发者友好
安装
- 首先确保项目已经初识化了Go Modules,在项目目录下运行:
go mod init <your-module-name>
- 安装GORM:
go get -u gorm.io/gorm
- 安装对应数据库驱动,支持多种数据库驱动,如MySQL、PostgreSQL、SQLite等, 🌰:
go get -u gorm.io/driver/mysql
连接MySQL数据库
在Go文件里引入GORM包和MySQL驱动
import (
"gorm.io/gorm"
"gorm.io/driver/mysql"
)
提供数据库的DSN字符串,使用mysql.Open()
方法 和 gorm.Open()
方法连接到MySQL数据库。
dsn := "username:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
// 将 username password dbname 替换为实际需要的
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
// 处理错误
panic("failed to connect database")
}
GORM 模型
gorm.Module
是GORM提供的一个预定义的结构体,包含常用字段:
type Model struct {
ID unit `gorm:"primaryKey"` // 记录唯一标识(主键)
Create time.Time // 创建记录自动设置当前时间
UpdateAt time.Time // 修改记录自动更新当前时间
DeleteAt gorm.DeleteAt `gorm:"index"` // 软删除,记录标识已删除
}
约定
- 主键:GORM 使用一个名为
ID
的字段作为每个模型的默认主键。 - 表名:默认情况下,GORM 将结构体名称转换为
snake_case
并为表名加上复数形式。 例如,一个User
结构体在数据库中的表名变为users
可以通过实现Tabler
接口更改默认表名 (见后续CRUD操作) 也可以通过Table()
方法,临时指定表名 (见后续CRUD操作) - 列名:GORM 自动将结构体字段名称转换为
snake_case
作为数据库中的列名。 - 时间戳字段:GORM使用字段
CreatedAt
和UpdatedAt
来自动跟踪记录的创建和更新时间。
更多可查看约定文档
字段级权限限制
gorm:"<-:create"
:允许读、创建gorm:"<-:update"
:允许读、更新gorm:"<-:"
:允许读、写gorm:"<-:false"
:允许读,禁止写gorm:"->"
:只读gorm:"->;<-:create"
:允许读、写gorm:"->:false;<-:create"
:仅创建(禁止从db读)gorm:"-"
:通过struct读写会忽略该字段gorm:"-:all"
:通过struct读写、迁移会忽略该字段gorm:"-:migration"
:通过struct迁移会忽略该字段
字段标签
- 设置主键:
gorm:"primarykey"
- 指定列名:
gorm:"column:cloumn_name"
- 指定值唯一:
gorm:"unique"
- 创建索引:
gorm:"index"
- 自动递增,用于整数类型:
gorm:"autoIncrement"
- 设置字符串/字节数组大小:
gorm:"size:[number]"
- 指定精度和小数位数:
gorm:"precision:[number],scale:[number]"
用于decimal类型 - 默认值:
gorm:"default:[value]"
- 忽略该字段,不映射到数据库:
gorm:"-"
db.Model
用于指定后续数据库操作的目标模型或表,不执行任何数据库操作,而是设置一个上下文, 后续接一个GORM的方法。
常搭配使用:
- 更新操作:
Update
或Updates
- 删除操作:
Delete
- 查询操作
- 关联操作
创建 Create
创建新表
GORM不需要直接编写SQL创建表,可以通过使用AutoMigrate
方法,根据定义的模型自动创建或修改表。
如果需要使用SQL语句建表,可以使用Exec
方法。
🌰:
type Product struc {
gorm.Model
Code string
Price unit
}
// 修改默认表名
func (Product) TableName() string {
return "product"
}
func main() {
dsn := "username:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
// 处理错误
panic("failed to connect database")
}
// 创建表或修改表,原来默认表名为:products,实现了Table接口后,表名为product
db.AutoMigrate(&Product{})
// 根据Product的字段创建‘tb_product'表
db.Table("tb_product").AutoMigrate(&Product{})
// 使用SQL语句建表
db.Exec("CREATE TABLE product_copy (" +
"id INT AUTO_INCREMENT PRIMARY KEY," +
"created_at DATETIME," +
"updated_at DATETIME," +
"deleted_at DATETIME," +
"code VARCHAR(255) NOT NULL," +
"price DECIMAL(10,2) NOT NULL" +
")")
}
插入记录
使用Create()
方法,其需要传入数据的指针。
使用Select()
搭配,可以为指定字段赋值。
使用Omit()
方法,能忽略指定字符,不给予赋值。
p := Product{Code: "001", Name: "Apple", Price: 3, Count: 200}
result := db.Create(&p)
fmt.Println("插入条数:", result.RowsAffected) // 1
fmt.Println("返回 Error:", result.Error) // <nil>
fmt.Println("Product ID:", p.ID)
// 搭配Select
p2 := Product{Code: "002", Name: "Banne", Price: 2, Count: 200}
db.Select("Code", "Name").Create(&p2) // 数据库里Price 和Count 则没有赋值
// 等同于
db.Omit("Code", "Name").Create(&p2) // 数据库里Code、 Name 则没有赋值,只有,Price 和 Count
批量插入,将切片传给Create
方法。当这些记录可以被分割成多批次时,GORM开启一个事务处理。
可以通过CreateInBatches
方法指定批量插入的批次大小。
// 插入多条记录
// 存放的是指向Product类型的指针
productArr := []*Product{
{Code: "003", Name: "Orange", Price: 5, Count: 34},
{Code: "004", Name: "Pear", Price: 6, Count: 55},
{Code: "005", Name: "Lemon", Price: 8, Count: 23},
{Code: "006", Name: "Watermelon", Price: 3.99, Count: 2},
}
db.Create(productArr)
// 指定每批次大小
db.CreateInBatches(productArr, 2)
当然我们也可以在初始化GORM实例的时候,提前设定Batche的大小,避免后续频繁使用CreateInBatches
。
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
+ CreateBatchSize: 1000
})
更新
使用Save()
保存所有字段,即使值为零。但不能与Model()
方法一起使用。如果保存的数据里不包含主键,则将执行Create()
。
var product Product
db.First(&product)
fmt.Println(product)
product.Count = 77
// 更新第一条数据
db.Save(&product)
// 没有主键,插入一条新的
db.Save(&Product{Name: "Apple", Count: 89, Code: "001"})
单列更新:使用Update(列名, 值)
多列更新:使用Updates()
,支持struct
和map[string]interface{}
;struct
时,默认只更新非零值字段
选择更新某字段:使用Select(列名).Updates()
选择忽略某字段:使用Omit(列名).Updates()
查询
检索单个对象:
First()
:获取第一条记录,等同于Select * From tableName ORDER BY ID LIMIT 1
Last()
:获取最后一条记录,等同于Select * From tableName LIMIT 1
Take()
:获取一条记录,等同于Select * From tableName ORDER BY ID DESC LIMIT 1
有主键时,根据主键进行排序;如果没有主键,则按第一个字段进行排序。 还可以通过内联条件进行WHERE检索
检索全部对象:使用Find()
,等同于Select *
条件检索:使用Where()
。
// 🌰
db.Where("name = ?", "jinzhu").First(&user)
//SELECT * FROM users WHERE name = 'jinzhu' ORDER BY id LIMIT 1;
需要注意,如果使用struct进行查询时,GORM只会对非零字段进行查询。如果要查询零值,可以使用map,map包含所有键值作为查询条件
指定结构体查询字段:可以通过将相关字段名或dbname传递到Where()
来指定要在查询条件中使用的结构中的特定值
内联查询:以Where()
类似的方法内联到First
、Find
等方法中。
db.Find(&users, "name <> ? AND age > ?", "jinzhu", 20)
// SELECT * FROM users WHERE name <> "jinzhu" AND age > 20;
NOT条件:Not()
,使用方法类似Where()
OR条件:Or()
,使用方法类似Where()
选择特定字段:Select()
,一般搭配Find()
,指定查询某些字段,否则默认选择全部。
排序:Order()
、OrderBy()
,指定从数据库检索记录时的顺序。
LIMIT:Limit()
,指定要检索的最大记录数。
排序:Offset()
,指定在开始返回记录之前要跳过的记录数。
Group By:Group()
Having:Having()
Join:Joins()
,指定联接条件。
Distinct:Distinct()
,从模型中选择不同的值。\
删除
使用Delete()
删除记录。删除一条记录时,需要指定主键,否则会触发批量删除。
如果模型里包含gorm.DeletedAt
字段,则该模型自动获取软删除能力。可以使用Unscoped()
搭配Find()
查询被软删除的记录;如果搭配Delete()
,则会永久删除匹配的记录
删除标志:默认情况下,gorm.Model
使用*time.Time
作为DeletedAt
的字段类型。
但是软删除插件支持其他数据格式:
type User struct {
...
// 使用Unix时间戳作为删除标识
DeleteAt soft_delete.DeleteAt
// 指定毫秒milli或纳秒nano作为值
DeleteAt soft_delete.DeleteAt `gorm:"softDelete:milli"`
// 指定 `1` / `0` 作为值
IsDel soft_delete.DeleteAt `gorm:"softDelete:flag"`
// 混合模式,使用 `0`,`1`或者unix时间戳来标记数据是否被软删除,并同时可以保存被删除时间
DeleteAt time.Time
IsDel soft_delete.DeleteAt `gorm:"softDelete:flag,DeletedAtField:DeletedAt"`
}
原始SQL
执行原始SQL进行查询可以使用Raw()
方法,接收一个SQL语句作为字符串参数,以及任何必要参数值(用于参数绑定,防止SQL注入)。查询结束可以使用Scan()
将结果映射到相应结构体切片或单个结构体中。
type User struct {
ID uint
Name string
Age int
// 其他字段...
}
var users []User
db.Raw("SELECT * FROM users WHERE age > ?", 18).Scan(&users)
转载自:https://juejin.cn/post/7367607757208944674