GORM 学习笔记
介绍
gorm是GoLang实现的,在GitHub上活跃度很高的对象关系映射框架(ORM)。它的作用是在关系型数据库和对象之间作一个映射,这样,我们在具体的操作数据库的时候,就不需要再去和复杂的SQL语句打交道,只要像平时操作对象一样操作它就可以了。
GORM 目前支持的数据库有 MySQL、SQLServer、PostgreSQL、SQLite。
GORM 在使用有以下几点约定:
- GORM 使用名为 ID 的字段作为主键
- 如果没有 TableName 函数,使用结构体的蛇形复数作为表名
- 字段名的蛇形作为列表
- 使用 CreatedAt、UpdateAt 字段作为创建时间、更新时间
环境准备
如果我们使用的是 mysql 数据库,需要先在项目中下载 gorm 与 mysql 驱动的依赖包:
## 安装gorm
go get -u gorm.io/gorm
## 安装相应的数据库驱动。GORM 官方支持的数据库类型有: MySQL, PostgreSQL, SQlite, SQL Server
go get -u gorm.io/driver/mysql
连接数据库
package mysql
import (
"log"
"time"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func main() {
// 数据库连接
db, err := gorm.Open(mysql.New(mysql.Config{
DSN: "gorm:gorm@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local", // DSN data source name
DefaultStringSize: 256, // string 类型字段的默认长度
DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
}), &gorm.Config{})
if err != nil {
log.Fatal(err)
}
}
定义模型
在使用 ORM 工具时,通常我们需要在代码中定义模型(Models)与数据库中的数据表进行映射,在 GORM 中模型(Models)通常是正常定义的结构体、基本的 Go 类型或它们的指针。
创建一个 User 类型的结构体,结构体里的字段和 mysql 的 user 表字段一一对应。
type User struct {
ID uint `gorm:"primarykey"` // 声明主键
Name string `gorm:"column:name"`
Age int8 `gorm:"column:age"`
}
gorm.Model 结构体
为了方便模型定义,GORM 内置了一个 gorm.Model 结构体。gorm.Model 是一个包含了ID, CreatedAt, UpdatedAt, DeletedAt 四个字段的 Golang 结构体。
可以将它嵌入到自己的模型中:
type Product struct {
gorm.Model
ID uint
User string `gorm:"column:name"`
Age uint `gorm:"column:age"`
}
当然也可以完全自定义模型。
自动迁移
GORM 提供了自动迁移功能,可以根据模型结构自动创建或更新数据库表。
func main() {
// 数据库连接
db, err := gorm.Open(mysql.New(mysql.Config{
DSN: "gorm:gorm@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local", // DSN data source name
DefaultStringSize: 256, // string 类型字段的默认长度
DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
}), &gorm.Config{})
if err != nil {
log.Fatal(err)
}
// 自动迁移
err := db.AutoMigrate(&User{})
if err != nil {
log.Fatalf("自动迁移失败: %v", err)
}
log.Println("自动迁移成功")
}
注意: 如果我们不使用自动迁移,而是自己创建了数据库表,刚才创建的结构体还有一个问题,结构体名为 User,按照约定 GORM 默认会认为表名是 users (小写复数),但我们创建的数据库中的表名为 user,因此要创建一个对应关系,方式为给 User 结构体绑定一个 TableName 方法。
func (u User) TableName() string {
return "user"
}
增删改查
1. 创建(Create)
我首先尝试了如何使用 GORM 创建一个新用户:
user := User{Name: "wind", Age: 25}
result := db.Create(&user)
if result.Error != nil {
log.Fatalf("创建用户失败: %v", result.Error)
}
log.Printf("创建用户成功: %v", user)
这里我通过 db.Create() 方法将 User 实例插入到数据库中,result 包含了执行操作的结果。如果创建成功,返回的 user 对象将会包含自动生成的 ID。
2. 查询(Read)
(1)根据主键查询
var user User
result := db.First(&user, 1) // 根据主键查询 ID 为 1 的用户
if result.Error != nil {
log.Fatalf("查询用户失败: %v", result.Error)
}
log.Printf("查询用户成功: %v", user)
使用 db.First() 方法根据主键(ID)查询用户。
(2)根据条件查询
var users []User
result := db.Where("age > ?", 20).Find(&users)
if result.Error != nil {
log.Fatalf("查询用户失败: %v", result.Error)
}
log.Printf("查询用户成功: %v", users)
通过 db.Where() 方法,我可以根据特定条件查询用户。在这个例子中,我查询了所有年龄大于 20 岁的用户。
更多用法:
// Get all matched records
db.Where("name <> ?", "jinzhu").Find(&users)
// SELECT * FROM users WHERE name <> 'jinzhu';
// IN
db.Where("name IN ?", []string{"jinzhu", "jinzhu 2"}).Find(&users)
// SELECT * FROM users WHERE name IN ('jinzhu','jinzhu 2');
// LIKE
db.Where("name LIKE ?", "%jin%").Find(&users)
// SELECT * FROM users WHERE name LIKE '%jin%';
// AND
db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;
// Time
db.Where("updated_at > ?", lastWeek).Find(&users)
// SELECT * FROM users WHERE updated_at > '2000-01-01 00:00:00';
// BETWEEN
db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)
// SELECT * FROM users WHERE created_at BETWEEN '2000-01-01 00:00:00' AND '2000-01-08 00:00:00';
3. 更新(Update)
(1)更新单个字段
result := db.Model(&user).Update("Age", 26)
if result.Error != nil {
log.Fatalf("更新用户失败: %v", result.Error)
}
log.Println("更新用户年龄成功")
使用 db.Model() 和 Update() 方法可以更新指定字段。在这个例子中,我更新了用户的 Age 字段。
(2)更新多个字段
result := db.Model(&user).Updates(User{Name: "sun", Age: 27})
if result.Error != nil {
log.Fatalf("更新用户失败: %v", result.Error)
}
log.Println("更新用户信息成功")
如果要更新多个字段,可以直接传递一个结构体给 Updates() 方法。
4. 删除(Delete)
result := db.Delete(&user)
if result.Error != nil {
log.Fatalf("删除用户失败: %v", result.Error)
}
log.Println("删除用户成功")
使用 db.Delete() 删除指定的记录。在这里,我删除了一个用户。
八、完整代码
将前面的代码整合在一起,完整的 main.go 文件如下:
package main
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
"log"
"time"
)
type User struct {
ID uint `gorm:"primaryKey"`
Name string
Age uint8
}
func main() {
// 数据库连接
db, err := gorm.Open(mysql.New(mysql.Config{
DSN: "gorm:gorm@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local", // DSN data source name
DefaultStringSize: 256, // string 类型字段的默认长度
DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
}), &gorm.Config{})
if err != nil {
log.Fatal(err)
}
// 自动迁移
err = db.AutoMigrate(&User{})
if err != nil {
log.Fatalf("自动迁移失败: %v", err)
}
log.Println("自动迁移成功")
// 创建用户
user := User{Name: "wind", Age: 25}
result := db.Create(&user)
if result.Error != nil {
log.Fatalf("创建用户失败: %v", result.Error)
}
log.Printf("创建用户成功: %v", user)
// 查询用户
var queryUser User
result = db.First(&queryUser, user.ID)
if result.Error != nil {
log.Fatalf("查询用户失败: %v", result.Error)
}
log.Printf("查询用户成功: %v", queryUser)
// 更新用户
result = db.Model(&queryUser).Updates(User{Name: "sun", Age: 27})
if result.Error != nil {
log.Fatalf("更新用户失败: %v", result.Error)
}
log.Println("更新用户信息成功")
// 删除用户
result = db.Delete(&queryUser)
if result.Error != nil {
log.Fatalf("删除用户失败: %v", result.Error)
}
log.Println("删除用户成功")
}