第54课:GORM ORM - 高效的数据库交互
1. 学习目标
完成本课学习后,你将能够:
- 理解 ORM 的概念及其在 Go 语言中的优势。
- 使用 GORM 连接 MySQL 数据库并定义数据模型。
- 掌握 GORM 的基本 CRUD(创建、读取、更新、删除)操作。
- 使用 GORM 的自动迁移功能来同步数据表结构。
- 处理 GORM 查询中的关联关系与基本条件筛选。
2. 核心概念
什么是 ORM?
ORM(对象关系映射) 是一种编程技术,它将程序中的对象(Object)与数据库中的表(Relation)进行映射。简单来说,它让你可以用操作 Go 语言结构体(struct)和对象的方式,来直接操作数据库,而无需手动编写复杂的 SQL 语句。
优势:
- 开发效率高:用熟悉的 Go 语法操作数据库,减少 SQL 编写量。
- 可移植性强:更换数据库时,只需修改少量配置,大部分代码无需改动。
- 安全性:GORM 默认使用参数化查询,能有效防止 SQL 注入攻击。
GORM 是什么?
GORM 是 Go 语言中非常流行的一个 ORM 库。它功能强大、设计优雅,并且拥有丰富的文档和社区支持。
GORM 的核心组件:
- 模型(Model):一个普通的 Go 结构体,用于映射数据库表。
- 会话(Session):通过
db *gorm.DB这个对象与数据库进行交互。 - 迁移(Migration):根据模型自动创建或更新数据库表结构。
3. 代码示例
以下是一个完整的示例,展示如何使用 GORM 连接 MySQL、定义模型、执行基本操作并进行自动迁移。
package main
import (
"fmt"
"log"
"time"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
// 1. 定义模型(Model)
// 模型通常继承 gorm.Model,它提供了 ID, CreatedAt, UpdatedAt, DeletedAt 字段。
type Product struct {
gorm.Model
Code string `gorm:"type:varchar(100);uniqueIndex"`
Price uint
Name string `gorm:"size:255"` // 定义字段大小
Status bool `gorm:"default:true"`
}
// 你可以定义自定义的表名
func (Product) TableName() string {
return "products"
}
func main() {
// 2. 连接数据库
// 请根据你的实际情况修改 DSN(数据源名称)
dsn := "root:yourpassword@tcp(127.0.0.1:3306)/yourdb?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info), // 开启SQL日志
})
if err != nil {
log.Fatal("连接数据库失败:", err)
}
fmt.Println("✅ 数据库连接成功")
// 3. 自动迁移
// 根据模型结构自动创建或更新表
err = db.AutoMigrate(&Product{})
if err != nil {
log.Fatal("自动迁移失败:", err)
}
fmt.Println("✅ 自动迁移完成")
// 4. 创建记录 (Create)
product := Product{
Code: "D42",
Price: 100,
Name: "测试商品",
}
result := db.Create(&product)
if result.Error != nil {
log.Fatal("创建记录失败:", result.Error)
}
fmt.Printf("✅ 创建记录成功,影响行数: %d,自增ID: %d\n", result.RowsAffected, product.ID)
// 5. 查询记录 (Read)
// 查询单条记录
var queriedProduct Product
db.First(&queriedProduct, 1) // 根据主键查找
fmt.Printf("🔍 查询ID为1的商品: %+v\n", queriedProduct)
// 根据条件查询
var products []Product
db.Where("price > ?", 50).Find(&products)
fmt.Printf("🔍 价格大于50的商品数量: %d\n", len(products))
// 6. 更新记录 (Update)
// 更新单个字段
db.Model(&queriedProduct).Update("Price", 200)
// 更新多个字段
db.Model(&queriedProduct).Updates(Product{Price: 300, Status: false})
// 使用 map 更新(注意:map 方式不会更新零值字段)
// db.Model(&queriedProduct).Updates(map[string]interface{}{"Price": 300, "Status": false})
// 7. 删除记录 (Delete)
// 注意:GORM 默认是软删除(更新 DeletedAt 字段)
db.Delete(&queriedProduct)
fmt.Println("🗑️ 记录已删除(软删除)")
// 查询会自动排除软删除的记录
var count int64
db.Model(&Product{}).Count(&count)
fmt.Printf("📦 当前商品总数(不含已删除): %d\n", count)
// 8. 事务处理示例(简单)
tx := db.Begin()
// 在事务中执行一些操作
phone := Product{Code: "Phone001", Price: 999, Name: "智能手机"}
if err := tx.Create(&phone).Error; err != nil {
tx.Rollback() // 出错回滚
return
}
tx.Commit() // 提交事务
fmt.Println("✅ 事务提交成功")
}
4. 实践练习
练习1:基础模型与CRUD
要求:
- 定义一个
User结构体,包含ID、Username、Email、Age字段。 - 使用 GORM 自动迁移创建
users表。 - 编写代码,创建 3 个用户记录。
- 查询年龄大于 20 岁的用户,并打印其用户名。
练习2:关联查询(选做)
要求:
- 在上面的
Product模型基础上,定义一个Category模型(包含ID和Name)。 - 在
Product中添加一个CategoryID字段,表示关联关系。 - 创建一个商品分类,并创建属于该分类的商品。
- 查询某个分类下的所有商品(使用
Preload预加载关联)。
练习3:分页与排序
要求:
- 向
products表中插入至少 10 条记录。 - 查询第 2 页的数据(每页 5 条),并按
price降序排序。 - 使用
db.Scopes()封装一个分页函数。
5. 常见错误
-
结构体字段未导出:GORM 要求用于映射的结构体字段必须是大写开头(导出)。小写字段会被忽略。
// 错误 type User struct { name string // 小写开头,GORM 无法访问 } // 正确 type User struct { Name string // 大写开头 } -
自动迁移时主键冲突:如果一个结构体有多个字段,且其中一个不是
ID但被误当作主键,可能会导致问题。确保继承gorm.Model或显式定义primaryKey标签。 -
更新零值字段:使用
Updates方法传入结构体时,GORM 不会更新零值字段(如0、false、"")。如需更新零值字段,请使用map[string]interface{}或Select明确指定字段。db.Model(&user).Select("Age").Update("Age", 0) // 强制将 Age 更新为 0 -
忘记处理错误:GORM 的大多数操作都返回
*gorm.DB对象,其中包含Error字段。务必检查result.Error,否则数据库错误会被静默忽略。// 错误 db.Create(&product) // 正确 result := db.Create(&product) if result.Error != nil { // 处理错误 } -
软删除的误解:
db.Delete(&model)默认执行的是软删除(更新deleted_at字段)。查询时,GORM 会自动添加WHERE deleted_at IS NULL条件。如需物理删除,可使用db.Unscoped().Delete(&model)。
6. 小结
- ORM 是将对象与数据库表进行映射的技术,GORM 是 Go 语言中功能强大且流行的 ORM 库。
- 使用
gorm.Open连接数据库,并通过模型结构体定义数据表。 - 自动迁移 (
AutoMigrate) 是同步模型与数据库表结构的利器,适合在开发阶段使用。 - 核心的 CRUD 操作通过
Create、First/Find、Update、Delete方法实现。 - 务必检查操作返回的错误,并理解软删除与物理删除的区别。
- 通过练习掌握基础后,可以进一步学习 GORM 的关联、预加载、钩子、复杂查询等高级特性。