GORM Go 语言 ORM 的 CRUD 操作

FreeGuideOnline 最新 2026-07-08

GORM Go语言ORM的CRUD操作完全指南

什么是GORM?

GORM是Go语言中最受欢迎的ORM库,它通过结构体与数据库表映射,让开发者可以用面向对象的方式操作数据库,而无需编写原始SQL。它支持MySQL、PostgreSQL、SQLite等多种数据库,提供自动迁移、钩子、事务等高级特性,大幅提升开发效率。

环境准备与连接数据库

开始之前,请确保已安装Go语言环境。在项目目录执行以下命令安装GORM及数据库驱动(以MySQL为例):

go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql

连接数据库示例:

import (
    "gorm.io/gorm"
    "gorm.io/driver/mysql"
)

func main() {
    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{})
    if err != nil {
        panic("failed to connect database")
    }
}

定义模型(Model)

GORM使用结构体定义数据库表结构,通过标签指定字段映射、约束等。

type User struct {
    ID        uint   `gorm:"primaryKey"`
    Name      string `gorm:"size:100;not null"`
    Email     string `gorm:"uniqueIndex;size:255"`
    Age       int    `gorm:"default:0"`
    CreatedAt time.Time
    UpdatedAt time.Time
}

执行自动迁移(AutoMigrate)会根据结构体创建或更新表:

db.AutoMigrate(&User{})

创建记录(Create)

单条记录创建

使用Create方法传入结构体指针,GORM会填充主键、时间戳等字段。

user := User{Name: "张三", Email: "zhangsan@example.com", Age: 25}
result := db.Create(&user)
// user.ID 会被赋值为插入后的主键
fmt.Println(user.ID, result.RowsAffected, result.Error)

批量插入

传入切片可一次性插入多条记录,提高性能。

users := []User{
    {Name: "李四", Email: "lisi@example.com"},
    {Name: "王五", Email: "wangwu@example.com"},
}
db.Create(&users)

指定字段创建

使用SelectOmit控制插入的字段。

db.Select("Name", "Age").Create(&user) // 只插入Name和Age
db.Omit("Email").Create(&user) // 忽略Email字段

创建钩子

GORM提供BeforeCreateAfterCreate等钩子,可在创建前后执行自定义逻辑。

func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
    u.Name = strings.TrimSpace(u.Name)
    return
}

查询记录(Read)

查询单条记录

  • First:按主键升序取第一条
  • Take:无排序取一条
  • Last:按主键降序取最后一条
var user User
// 根据主键查询
db.First(&user, 1)   // SELECT * FROM users WHERE id = 1 ORDER BY id LIMIT 1;
// 根据条件
db.First(&user, "email = ?", "zhangsan@example.com")

查询多条记录

使用Find返回所有符合条件的记录。

var users []User
db.Where("age > ?", 18).Find(&users)  // 注意:切片需要零值但已分配内存
// 使用指针形式: db.Where("age > ?", 18).Find(&users)

条件查询(Where)

常用的链式条件方法:

db.Where("name = ? AND age >= ?", "张三", 20).Find(&users)
db.Where(&User{Name: "张三", Age: 20}).Find(&users) // 结构体条件,零值字段会被忽略
db.Where(map[string]interface{}{"name": "张三", "age": 20}).Find(&users) // Map条件,零值会包含
db.Not("name = ?", "张三").Find(&users)
db.Or("email = ?", "lisi@example.com").Find(&users)

排序、分页与限制

db.Order("age desc, name").Limit(10).Offset(5).Find(&users)
// Limit指定返回条数,Offset指定跳过条数

选择特定字段

db.Select("name", "age").Find(&users)

关联查询(Preload / Joins)

GORM可以方便地处理关联关系,使用Preload预加载关联数据,或Joins进行连接查询(需要在结构体中定义关联)。

type Order struct {
    ID     uint
    UserID uint
    User   User
}
db.Preload("User").Find(&orders)

更新记录(Update)

更新单个字段

db.Model(&User{}).Where("id = ?", 1).Update("name", "张三丰")

更新多个字段

使用Updates传入结构体或Map,注意结构体零值字段不会被更新。

// 结构体方式(忽略零值)
db.Model(&user).Updates(User{Name: "新名字", Age: 0}) // Age不会被更新
// Map方式(包含零值)
db.Model(&user).Updates(map[string]interface{}{"name": "新名字", "age": 0})

批量更新

省略Model的主键或结合Where条件可更新多条记录。

db.Model(&User{}).Where("age > ?", 30).Update("status", "active")

使用Save保存整个结构体

Save会更新所有字段,包括零值(根据主键判断是插入还是更新)。

user.Name = "新名字"
db.Save(&user) // 根据user.ID存在则更新全部字段

更新钩子

类似创建,GORM提供BeforeUpdateAfterUpdate


删除记录(Delete)

软删除与物理删除

如果模型包含gorm.DeletedAt字段(gorm.Model已包含),则默认启用软删除。删除时只是设置删除时间,实际记录仍留存。

type User struct {
    gorm.Model  // 包含ID, CreatedAt, UpdatedAt, DeletedAt
    Name string
}
// 软删除
db.Delete(&user)      // 仅设置DeletedAt
db.Where("id = ?", 1).Delete(&User{}) // 批量软删除
// 物理删除(永久删除)
db.Unscoped().Delete(&user)

根据主键或条件删除

db.Delete(&User{}, 10) // 删除id=10的用户
db.Where("email LIKE ?", "%@test.com").Delete(&User{})

删除钩子

可利用BeforeDeleteAfterDelete执行清理逻辑。


总结与实践建议

  • 模型定义清晰,标签使用恰当,可大幅减少手动配置。
  • 查询时优先使用占位符避免SQL注入,使用结构体或Map灵活构造条件。
  • 更新操作注意零值问题,敏感操作推荐使用Map形式的Updates。
  • 生产环境中建议启用PrepareStmt(&gorm.Config{PrepareStmt: true})以提升性能。
  • 结合事务(db.Transaction)保证数据一致性,尤其是多表关联操作。

通过以上CRUD核心知识,你可以快速上手GORM进行数据库开发,进一步学习可深入研究关联、事务和自定义SQL。