1.安装配置
创建一个Go项目用来学习使用GORM,并初始化Go模块。
-
在项目根目录下,执行如下命令安装GORM:
go get -u gorm.io/gorm -
同样在项目根目录下,执行如下命令安装
MySQL的驱动:go get -u gorm.io/driver/mysqlGORM目前支持MySQL、SQLServer、PostgreSQL、SQLite
注意:在Windows系统上,安装SQLite来连接Mysql数据库,可以安装如下驱动,但是
gorm.io/driver/sqlite不可以会报没有CGO错误- 使用
modernc.org/sqlite作为 SQLite 的 Go 驱动。这是一个纯 Go 编写的 SQLite 库,不需要 CGO 支持。安装命令:go get modernc.org/sqlite
- 使用
-
快速启动一个GORM项目
注意:要在管理员身份下运行该代码,否则没有权限访问数据库
代码如下:
package main import ( "gorm.io/driver/mysql" "gorm.io/gorm" ) // User结构体定义,对应数据库中的表 type User struct { gorm.Model Name string Age int } func main() { // 连接字符串,根据实际情况修改用户名、密码、主机和数据库名 dsn := "username:password@tcp(127.0.0.1:3306)/gormstudy?charset=utf8mb4&parseTime=True&loc=Local" // 打开数据库连接 db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { panic("连接数据库失败: " + err.Error()) } // 自动迁移,创建User表(如果不存在) db.AutoMigrate(&User{}) // 创建一个新用户 newUser := User{Name: "John", Age: 30} result := db.Create(&newUser) if result.Error != nil { panic("插入数据失败: " + result.Error.Error()) } // 查询所有用户 var users []User db.Find(&users) for _, user := range users { println(user.Name, user.Age) } }运行结果:
GORM 通过将 Go 结构体(Go structs) 映射到数据库表来简化数据库交互。 了解如何在GORM中定义模型,是充分利用GORM全部功能的基础。
2.模型定义
2.1 GORM的约定
2.1.1 主键
-
使用
ID作为主键默认情况下,GORM 会使用
ID作为表的主键。
type User struct {
ID string // 默认情况下,名为 `ID` 的字段会作为表的主键
Name string
}
- 通过标签
primaryKey将其它字段设为主键
// 将 `UUID` 设为主键
type Animal struct {
ID int64
UUID string `gorm:"primaryKey"`
Name string
Age int64
}
2.1.2 表名
默认情况下,GORM将结构体名称转换为snake_case并为表名加上复数形式。例如,一个User结构体在数据库中的表名变为users
TableName
可以实现 Tabler 接口来更改默认表名,例如:
type Tabler interface {
TableName() string
}
// TableName 会将 User 的表名重写为 `profiles`
func (User) TableName() string {
return "profiles"
}
2.1.3 列名
GORM自动将结构体字段名称转为snake_case作为数据库中的列名
type User struct {
ID uint // 列名是 `id`
Name string // 列名是 `name`
Birthday time.Time // 列名是 `birthday`
CreatedAt time.Time // 列名是 `created_at`
}
可以使用 column 标签或 命名策略 来覆盖列名
type Animal struct {
AnimalID int64 `gorm:"column:beast_id"` // 将列名设为 `beast_id`
Birthday time.Time `gorm:"column:day_of_the_beast"` // 将列名设为 `day_of_the_beast`
Age int64 `gorm:"column:age_of_the_beast"` // 将列名设为 `age_of_the_beast`
}
2.1.4 时间戳字段
GORM使用字段CreateAt和UpdatedAt来自动跟踪记录的创建和更新时间
2.2 gorm.Model
GORM提供了一个预定义的结构体,名为gorm.Model,其中包含常用字段:
// gorm.Model 的定义
type Model struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
- 将其嵌入在结构体中: 可以直接在结构体中嵌入
gorm.Model,以便自动包含这些字段。 这对于在不同模型之间保持一致性并利用GORM内置的约定非常有用。 - 包含的字段:
ID:每个记录的唯一标识符(主键)。CreatedAt:在创建记录时自动设置为当前时间。UpdatedAt:每当记录更新时,自动更新为当前时间。DeletedAt:用于软删除(将记录标记为已删除,而实际上并未从数据库中删除)。
2.3 使用默认值
通过default标签为字段定义默认值
type User struct{
ID int64
Name string `gorm:"default:ctc"`
Age int64 `gorm:"defualt:18"`
}
3.连接到数据库
MySQL
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func main() {
// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
}
4.创建
创建记录
p := &Product{Code: "D42"}
res := db.Create(p)//通过数据的指针来创建
fmt.Println(res.Error) //获取err
fmt.Println(res.RowsAffected) //返回插入记录的条数
fmt.Println(p.ID) //返回输入数据的ID
还可以使用 Create() 创建多项记录:
//创建多条数据
products := []*Product{
{Code: "D41"},
{Code: "D42"},
{Code: "D43"},
}
res = db.Create(products)
fmt.Println(res.Error)
for _, p := range products {
fmt.Println(p.ID)
}
运行结果:
注意: 在GORM中增删改查函数,如
Create(),这些都是FinishAPI后面的where操作是不会执行的,因为在增删改查函数前已经将语句提交到数据库了
根据 Map 创建
GORM支持通过 map[string]interface{} 与 []map[string]interface{}{}来创建记录。
//根据map创建记录一条记录
res := db.Model(&Product{}).Create(map[string]interface{}{
"Code": "F41",
"Price": 1000,
})
if res.Error != nil {
panic("map插入数据失败: " + res.Error.Error())
}
//根据map创建多条记录
res = db.Model(&Product{}).Create([]map[string]interface{}{
{"Code": "F51"},
{"Code": "F52"},
})
if res.Error != nil {
panic("map批量插入数据失败: " + res.Error.Error())
}
运行结果:
使用Upsert
使用clause.OnConflict处理数据冲突
//以不处理冲突为例,创建一条数据
p := &Product{Code: "042", ID: 1}
db.Clauses(clause.OnConflict{DoNothing: true}).Create(&p)
5.查询
检索单个对象
GORM 提供了 First、Take、Last 方法,以便从数据库中检索单个对象。当查询数据库时它添加了 LIMIT 1 条件,且没有找到记录时,它会返回 ErrRecordNotFound 错误
// 获取一条记录,没有指定排序字段
Takep := &Product{}
db.Take(Takep)
fmt.Printf("Take:%+v\n", Takep) // SELECT * FROM products LIMIT 1;
//获取最后一条记录(主键降序)
Lastp := &Product{}
db.Last(Lastp)
fmt.Printf("Last:%+v\n", Lastp) // SELECT * FROM products ORDER BY id DESC LIMIT 1;
注意: First 的使用踩坑 使用 First 时,需要注意查询不到数据会返回 ErrRecordNotFound. 使用 Find 查询多条数据,查询不到数 据不会返回错误。
运行结果:
注意: 当使用结构作为条件查询时,GORM只会查询非零值字段。这意味着如果您的字段值为 0、"、 false 或其他零值,该字段不会被用于构建查询条件,使用Map来构建查询条件。因为Go语言中的零值初始化机制,所以不知道是用户设置的,还是未被初始化
6.更新
更新单列
当使用 Update 更新单列时,需要有一些条件,否则将会引起ErrMissingWhereClause 错误
db.Model(&Product{}).Where("code =?", "D42").Update("price", 2000)
运行结果:
更新多列
Updates 方法支持 struct 和 map[string]interface{} 参数。当使用 struct 更新时,默认情况下GORM 只会更新非零值的字段
db.Model(&Product{}).Where("code =?", "F52").Updates(map[string]interface{}{"code": "F62", "price": 1200})
运行结果:
7.删除
物理删除
删除单条数据
根据主键删除
GORM 允许通过主键(可以是复合主键)和内联条件来删除对象,它可以使用数字,代码如下:
db.Delete(&Product{}, 10)
// DELETE FROM products WHERE id = 10;
db.Delete(&Product{}, "10")
// DELETE FROM products WHERE id = 10;
db.Delete(&Product{}, []int{1,2,3})
// DELETE FROM products WHERE id IN (1,2,3);
运行结果:
根据条件删除
// 带额外条件的删除
db.Where("Code = ?", "D43").Delete(&product)
运行结果:
批量删除
如果指定的值不包括主属性,那么 GORM 会执行批量删除,它将删除所有匹配的记录
db.Where("Code LIKE ?", "F%").Delete(&Product{})
db.Delete(&Product{}, "products LIKE ?", "F%")
运行结果:
可以将一个主键切片传递给Delete 方法,以便更高效的删除数据量大的记录
var products = []Product{{ID: 6}, {ID: 7}}
db.Delete(&products)
// DELETE FROM products WHERE id IN (1,2,3);
db.Delete(&products, "Code LIKE ?", "D%")
// DELETE FROM products WHERE name LIKE "D%" AND id IN (1,2,3);
软删除
如果模型包含了 gorm.DeletedAt字段(该字段也被包含在gorm.Model中),那么该模型将会自动获得软删除的能力!
db.Delete(&user)
// Batch Delete
db.Where("age = ?", 30).Delete(&User{})
// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE age = 30;
// Soft deleted records will be ignored when querying
db.Where("age = 30").Find(&user)
// SELECT * FROM users WHERE age = 30 AND deleted_at IS NULL;