GORM集成 #

一、GORM安装 #

1.1 安装GORM #

bash
# 安装GORM
go get -u gorm.io/gorm

# 安装MySQL驱动
go get -u gorm.io/driver/mysql

# 安装PostgreSQL驱动
go get -u gorm.io/driver/postgres

# 安装SQLite驱动
go get -u gorm.io/driver/sqlite

1.2 基本配置 #

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

func InitDB() (*gorm.DB, error) {
    dsn := "user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
    
    return gorm.Open(mysql.Open(dsn), &gorm.Config{
        // 日志配置
        Logger: logger.Default.LogMode(logger.Info),
        
        // 跳过默认事务
        SkipDefaultTransaction: true,
        
        // 命名策略
        NamingStrategy: schema.NamingStrategy{
            TablePrefix:   "t_",
            SingularTable: true,
        },
        
        // 禁用外键约束
        DisableForeignKeyConstraintWhenMigrating: true,
    })
}

二、模型定义 #

2.1 基本模型 #

go
type User struct {
    ID        uint           `gorm:"primaryKey" json:"id"`
    Name      string         `gorm:"size:100;not null" json:"name"`
    Email     string         `gorm:"size:100;uniqueIndex" json:"email"`
    Age       int            `gorm:"default:0" json:"age"`
    Status    string         `gorm:"size:20;default:'active'" json:"status"`
    CreatedAt time.Time      `json:"created_at"`
    UpdatedAt time.Time      `json:"updated_at"`
    DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
}

2.2 字段标签 #

go
type User struct {
    // 主键
    ID uint `gorm:"primaryKey"`
    
    // 自增
    ID uint `gorm:"primaryKey;autoIncrement"`
    
    // 字段大小
    Name string `gorm:"size:100"`
    
    // 非空
    Name string `gorm:"not null"`
    
    // 默认值
    Age int `gorm:"default:0"`
    
    // 唯一索引
    Email string `gorm:"uniqueIndex"`
    
    // 普通索引
    Name string `gorm:"index"`
    
    // 复合索引
    Name string `gorm:"index:idx_name_age"`
    Age  int    `gorm:"index:idx_name_age"`
    
    // 外键
    UserID uint `gorm:"foreignKey:UserID"`
    
    // 忽略字段
    Ignore string `gorm:"-"`
    
    // 列名
    Name string `gorm:"column:user_name"`
    
    // 类型
    Bio string `gorm:"type:text"`
}

2.3 嵌入结构体 #

go
type BaseModel struct {
    ID        uint           `gorm:"primaryKey" json:"id"`
    CreatedAt time.Time      `json:"created_at"`
    UpdatedAt time.Time      `json:"updated_at"`
    DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
}

type User struct {
    BaseModel
    Name  string `json:"name"`
    Email string `json:"email"`
}

// 嵌入但不作为单独模型
type Profile struct {
    gorm.Model
    UserID uint
    Bio    string
}

三、关联关系 #

3.1 一对一 #

go
type User struct {
    ID      uint
    Name    string
    Profile Profile  // 一对一
}

type Profile struct {
    ID     uint
    UserID uint   // 外键
    Bio    string
}

// 创建
user := User{
    Name: "Alice",
    Profile: Profile{
        Bio: "Hello, I'm Alice",
    },
}
DB.Create(&user)

// 预加载查询
var user User
DB.Preload("Profile").First(&user, 1)

3.2 一对多 #

go
type User struct {
    ID    uint
    Name  string
    Posts []Post  // 一对多
}

type Post struct {
    ID     uint
    Title  string
    UserID uint  // 外键
}

// 创建
user := User{
    Name: "Alice",
    Posts: []Post{
        {Title: "Post 1"},
        {Title: "Post 2"},
    },
}
DB.Create(&user)

// 预加载查询
var user User
DB.Preload("Posts").First(&user, 1)

3.3 多对多 #

go
type User struct {
    ID      uint
    Name    string
    Roles   []Role `gorm:"many2many:user_roles;"`  // 多对多
}

type Role struct {
    ID    uint
    Name  string
    Users []User `gorm:"many2many:user_roles;"`
}

// 添加关联
user := User{Name: "Alice"}
role := Role{Name: "Admin"}
DB.Create(&user)
DB.Create(&role)

// 关联
DB.Model(&user).Association("Roles").Append(&role)

// 预加载查询
var user User
DB.Preload("Roles").First(&user, 1)

3.4 属于 #

go
type Post struct {
    ID     uint
    Title  string
    UserID uint
    User   User  // 属于
}

type User struct {
    ID   uint
    Name string
}

// 创建
post := Post{
    Title: "My Post",
    User:  User{Name: "Alice"},
}
DB.Create(&post)

// 预加载查询
var post Post
DB.Preload("User").First(&post, 1)

四、钩子函数 #

4.1 生命周期钩子 #

go
type User struct {
    ID        uint
    Name      string
    CreatedAt time.Time
    UpdatedAt time.Time
}

// 创建前
func (u *User) BeforeCreate(tx *gorm.DB) error {
    fmt.Println("BeforeCreate")
    return nil
}

// 创建后
func (u *User) AfterCreate(tx *gorm.DB) error {
    fmt.Println("AfterCreate")
    return nil
}

// 更新前
func (u *User) BeforeUpdate(tx *gorm.DB) error {
    fmt.Println("BeforeUpdate")
    return nil
}

// 更新后
func (u *User) AfterUpdate(tx *gorm.DB) error {
    fmt.Println("AfterUpdate")
    return nil
}

// 删除前
func (u *User) BeforeDelete(tx *gorm.DB) error {
    fmt.Println("BeforeDelete")
    return nil
}

// 删除后
func (u *User) AfterDelete(tx *gorm.DB) error {
    fmt.Println("AfterDelete")
    return nil
}

// 查询后
func (u *User) AfterFind(tx *gorm.DB) error {
    fmt.Println("AfterFind")
    return nil
}

4.2 钩子顺序 #

text
创建: BeforeSave → BeforeCreate → AfterCreate → AfterSave
更新: BeforeSave → BeforeUpdate → AfterUpdate → AfterSave
删除: BeforeDelete → AfterDelete
查询: AfterFind

五、事务处理 #

5.1 自动事务 #

go
func CreateUser() error {
    return DB.Transaction(func(tx *gorm.DB) error {
        // 创建用户
        if err := tx.Create(&User{Name: "Alice"}).Error; err != nil {
            return err
        }
        
        // 创建用户配置
        if err := tx.Create(&Profile{UserID: 1, Bio: "Bio"}).Error; err != nil {
            return err
        }
        
        return nil
    })
}

5.2 手动事务 #

go
func CreateUser() error {
    tx := DB.Begin()
    defer func() {
        if r := recover(); r != nil {
            tx.Rollback()
        }
    }()
    
    if err := tx.Create(&User{Name: "Alice"}).Error; err != nil {
        tx.Rollback()
        return err
    }
    
    if err := tx.Create(&Profile{UserID: 1, Bio: "Bio"}).Error; err != nil {
        tx.Rollback()
        return err
    }
    
    return tx.Commit().Error
}

5.3 SavePoint #

go
func CreateUser() error {
    tx := DB.Begin()
    
    if err := tx.Create(&User{Name: "Alice"}).Error; err != nil {
        tx.Rollback()
        return err
    }
    
    tx.SavePoint("sp1")
    
    if err := tx.Create(&Profile{UserID: 1, Bio: "Bio"}).Error; err != nil {
        tx.RollbackTo("sp1")
        return tx.Commit().Error
    }
    
    return tx.Commit().Error
}

六、日志配置 #

6.1 日志级别 #

go
const (
    Silent logger.LogLevel = iota + 1
    Error
    Warn
    Info
)

// 配置日志级别
DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
    Logger: logger.Default.LogMode(logger.Info),
})

6.2 自定义日志 #

go
type CustomLogger struct {
    logger.Interface
}

func (l *CustomLogger) LogMode(level logger.LogLevel) logger.Interface {
    return l
}

func (l *CustomLogger) Info(ctx context.Context, msg string, data ...interface{}) {
    log.Printf("[INFO] "+msg, data...)
}

func (l *CustomLogger) Warn(ctx context.Context, msg string, data ...interface{}) {
    log.Printf("[WARN] "+msg, data...)
}

func (l *CustomLogger) Error(ctx context.Context, msg string, data ...interface{}) {
    log.Printf("[ERROR] "+msg, data...)
}

func (l *CustomLogger) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {
    sql, rows := fc()
    log.Printf("[SQL] %s | Rows: %d | Error: %v", sql, rows, err)
}

func InitDB() (*gorm.DB, error) {
    return gorm.Open(mysql.Open(dsn), &gorm.Config{
        Logger: &CustomLogger{},
    })
}

七、性能优化 #

7.1 批量插入 #

go
// 批量插入
users := []User{
    {Name: "Alice"},
    {Name: "Bob"},
    {Name: "Charlie"},
}
DB.CreateInBatches(&users, 100)

7.2 选择字段 #

go
// 只查询需要的字段
var users []User
DB.Select("id", "name").Find(&users)

// 排除字段
DB.Select("EXCLUDE", "password").Find(&users)

7.3 分页查询 #

go
func Paginate(page, pageSize int) func(db *gorm.DB) *gorm.DB {
    return func(db *gorm.DB) *gorm.DB {
        if page <= 0 {
            page = 1
        }
        
        switch {
        case pageSize > 100:
            pageSize = 100
        case pageSize <= 0:
            pageSize = 10
        }
        
        offset := (page - 1) * pageSize
        return db.Offset(offset).Limit(pageSize)
    }
}

// 使用
var users []User
var total int64

DB.Model(&User{}).Count(&total)
DB.Scopes(Paginate(1, 10)).Find(&users)

八、总结 #

8.1 核心要点 #

要点 说明
模型定义 使用struct tag定义字段属性
关联关系 一对一、一对多、多对多
钩子函数 生命周期回调
事务处理 保证数据一致性
日志配置 自定义日志输出

8.2 最佳实践 #

实践 说明
模型设计 合理设计模型和关联
索引优化 为常用查询字段添加索引
批量操作 使用批量插入提高性能
事务使用 合理使用事务

8.3 下一步 #

现在你已经掌握了GORM集成,接下来让我们学习 CRUD操作,深入了解数据库操作!

最后更新:2026-03-28