模型基础

模型是 GORM 的核心概念。一个 Go 结构体对应一张数据库表,结构体字段对应表字段。理解模型定义规则,是用好 GORM 的第一步。

基本模型定义

最简单的模型:

type User struct {
    ID   uint
    Name string
    Age  int
}

这个结构体会映射成一张 users 表:

CREATE TABLE `users` (
    `id` bigint unsigned AUTO_INCREMENT,
    `name` longtext,
    `age` bigint,
    PRIMARY KEY (`id`)
);

GORM 的映射规则:

  • 表名 = 结构体名的蛇形复数(User → users)
  • 字段名 = 结构体字段名的蛇形(UserName → user_name)
  • ID 字段自动成为主键

内置模型 gorm.Model

GORM 提供了一个内置的 gorm.Model 结构体:

type Model struct {
    ID        uint           `gorm:"primaryKey"`
    CreatedAt time.Time
    UpdatedAt time.Time
    DeletedAt gorm.DeletedAt `gorm:"index"`
}

嵌入它就自动有了这些字段:

type User struct {
    gorm.Model
    Name string
    Age  int
}

等价于:

type User struct {
    ID        uint           `gorm:"primaryKey"`
    CreatedAt time.Time
    UpdatedAt time.Time
    DeletedAt gorm.DeletedAt `gorm:"index"`
    Name      string
    Age       int
}

DeletedAt 字段让模型支持软删除,删除操作只会设置时间戳而不是真正删除记录。

约定优于配置

GORM 有很多默认约定,减少配置工作:

主键约定

名为 ID 的字段自动成为主键:

type User struct {
    ID   uint    // 主键
    Name string
}

也可以用其他字段名,但需要显式标注:

type User struct {
    UserID uint `gorm:"primaryKey"`
    Name   string
}

表名约定

默认表名是结构体名的蛇形复数:

type User struct {}     // → users
type OrderItem struct {} // → order_items

自定义表名:

func (User) TableName() string {
    return "app_users"
}

字段名约定

字段名转蛇形:

type User struct {
    FirstName string  // → first_name
    LastName  string  // → last_name
}

时间戳约定

名为 CreatedAtUpdatedAtDeletedAt 的字段会自动管理:

  • CreatedAt - 创建记录时自动设置
  • UpdatedAt - 更新记录时自动更新
  • DeletedAt - 软删除时设置

模型与数据库类型对应

Go 类型数据库类型
int, int32int
int64bigint
uint, uint32int unsigned
uint64bigint unsigned
float32float
float64double
stringvarchar(191) 或 longtext
booltinyint(1)
time.Timedatetime
[]byteblob

string 类型默认映射为 varchar(191),可以通过标签指定长度:

type User struct {
    Name string `gorm:"size:100"`  // varchar(100)
    Bio  string `gorm:"type:text"` // text
}

指针类型与 NULL

数据库字段允许 NULL 时,用指针类型:

type User struct {
    Name  string  // NOT NULL
    Email *string // 允许 NULL
}

或者用 sql.NullXxx 类型:

import "database/sql"

type User struct {
    Name      string
    Age       sql.NullInt64
    Bio       sql.NullString
    Active    sql.NullBool
    CreatedAt sql.NullTime
}

指针类型更简洁,sql.NullXxx 可以区分 NULL 和零值。

忽略字段

有些字段不需要存数据库:

type User struct {
    ID       uint
    Name     string
    Password string
    Confirm  string `gorm:"-"`  // 忽略,不映射
}

gorm:"-" 标签让 GORM 完全忽略这个字段。

只读字段

字段只允许读取,不允许写入:

type User struct {
    ID        uint
    Name      string
    CreatedAt time.Time `gorm:"<-:create"` // 只在创建时写入
    UpdatedAt time.Time `gorm:"<-:update"` // 只在更新时写入
    ViewCount int       `gorm:"->:false"`  // 只读,不写入
}

模型方法

模型可以定义方法,GORM 会在特定时机调用:

type User struct {
    ID       uint
    Name     string
    Password string
}

// 创建前自动加密密码
func (u *User) BeforeCreate(tx *gorm.DB) error {
    if u.Password != "" {
        u.Password = hashPassword(u.Password)
    }
    return nil
}

这些方法叫钩子函数,后面章节会详细介绍。

多个模型

一个项目通常有多个模型:

type User struct {
    gorm.Model
    Name  string
    Email string `gorm:"uniqueIndex"`
}

type Post struct {
    gorm.Model
    Title   string
    Content string
    UserID  uint
    User    User
}

type Comment struct {
    gorm.Model
    Content string
    PostID  uint
    Post    Post
    UserID  uint
    User    User
}

模型之间通过外键关联,GORM 会自动处理。

小结

模型定义的核心要点:

  • 结构体对应表,字段对应列
  • gorm.Model 提供常用字段
  • 遵循约定可以少写配置
  • 指针类型处理 NULL 值
  • gorm:"-" 忽略字段
  • 可以定义钩子方法

理解这些基础,就能定义出符合业务需求的模型。下一章详细介绍字段类型和标签。