Files
cs-note/hhs/GORM/01-安装与初始化.md
T
2026-05-24 11:42:38 +08:00

7.6 KiB
Raw Blame History

tags, create time
tags create time
GORM
Go
ORM
安装
初始化
数据库连接
2026-04-28 00:00

安装与初始化

概述

本章介绍 GORM 的安装流程以及三种初始化方式:全局实例、独立 Session 和自定义配置。理解这些基础是后续学习 CRUD 和操作链路的前提。

安装

go get gorm.io/gorm@latest
go get gorm.io/driver/mysql@latest # MySQL
# go get gorm.io/driver/postgres@latest  # PostgreSQL
# go get gorm.io/driver/sqlite@latest    # SQLite

[!tip] 驱动选择 GORM 本身不包含任何数据库驱动,你安装的 gorm.io/driver/xxx 包只是适配器(Adapter)。GORM 底层依赖 database/sql,所以最终执行 SQL 的还是标准库。

核心概念

在动手之前,先厘清三个容易混淆的对象:

flowchart LR
    Adapter["适配器 Driver"] --> ORM["GORM数据库层"]
    ORM --> Builder["查询构建器 *gorm.DB"]
    Builder --> Result["最终SQL"]
    
    style Adapter fill:#EAB308,color:#fff
    style ORM fill:#4FC08D,color:#fff
    style Builder fill:#3B82F6,color:#fff
    style Result fill:#A0AEC0,color:#fff
对象 作用 类比
Driver 将 *sql.DB 转化为 *gorm.DB 翻译官
*gorm.DB (DB) 全局数据库实例,存储连接池和默认配置 数据库连接池
*gorm.DB (Session) 通过 Session() / Where() 等方法产生的新实例,不影响原实例 临时会话

初始化方式

方式一:全局单例(最简单)

import (
    "gorm.io/gorm"
    _ "gorm.io/driver/mysql" // 空白导入驱动包
)

var DB *gorm.DB

func InitDB() error {
    dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
    var err error
    DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
    return err
}

[!warning] 全局变量陷阱 虽然方便,但全局 *gorm.DB 在高并发场景下可能成为瓶颈。生产环境推荐使用函数级局部变量 + 依赖注入。

方式二:函数内局部实例(推荐)

func NewGORMInstance() (*gorm.DB, error) {
    dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
    return gorm.Open(mysql.Open(dsn), &gorm.Config{})
}
// 每次请求或每个服务组件持有一个实例

方式三:函数内局部实例 + 连接池配置(生产推荐)

func NewDB(dsn string) (*gorm.DB, error) {
    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
        Logger: logger.Default.LogMode(logger.Info), // 调试时开启 SQL 日志
    })
    if err != nil {
        return nil, fmt.Errorf("connect db failed: %w", err)
    }

    sqlDB, _ := db.DB() // 获取底层 *sql.DB 用于调优连接池
    sqlDB.SetMaxOpenConns(100)           // 最大打开连接数
    sqlDB.SetMaxIdleConns(20)            // 最大空闲连接数
    sqlDB.SetConnMaxLifetime(time.Hour)  // 连接最大存活时间,避免被 MySQL 8h 超时断开
    
    return db, nil
}

// ---- 依赖注入使用 ----
type UserService struct {
    db *gorm.DB
}

func NewUserService(db *gorm.DB) *UserService {
    return &UserService{db: db}
}

func (s *UserService) List() ([]User, error) {
    var users []User
    return users, s.db.Find(&users).Error // Find 返回 *gorm.DB,取 .Error 得错误
}

[!tip] 为什么不用全局变量?

全局变量 局部 + DI
方便但难以测试 易 mocking、可并行测试
隐藏依赖关系 接口清晰,一眼看出需要什么
并发修改 config 互相影响 各组件配置独立

*gorm.DB 本身是 goroutine-safe 的,共享同一个实例不会有问题。需要的是「一个实例共享」而非「每个请求新建」。

关键配置项 gorm.Config

字段 类型 说明 默认值
DefaultPageSize int 分页默认 Limit 0(无限制)
SkipDefaultTransaction bool 是否跳过默认事务包装 false
DisableForeignKeyConstraintWhenMigrating bool 迁移时是否忽略外键约束 false
IgnoreRelationshipsWhenMigration bool 迁移时是否忽略关联定义 false
PrepareStmt bool 是否启用预编译语句缓存 false
TranslateError bool 是否将错误转为 GORM 格式 false
Logger logger.Interface 自定义日志输出器 Panic
NowFunc func() time.Time 覆盖当前时间(测试用) time.Now

调试模式配置示例

db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
    Logger: logger.Default.LogMode(logger.Info), // 开启详细 SQL 日志
})
// 输出示例:[info] ... [0.12ms] [rows:-] SELECT * FROM users

连接池调优(\*sql.DB)

上面的方式三已经展示了基本用法,这里展开说明每个参数的含义和推荐值:

方法 作用 推荐值 说明
SetMaxOpenConns(n) 最大打开连接数(含在用 + 空闲) 根据 QPS 评估 不超过 MySQL max_connections
SetMaxIdleConns(n) 最大空闲连接数 CPU 核数或 10~20 越多越能应对突发流量
SetConnMaxLifetime(d) 连接最大存活时间 5~10 分钟 必须小于 MySQL 的 wait_timeout(默认 8h),否则 GORM 拿到被服务端断开的连接会报错
SetConnMaxIdleTime(d) 空闲连接回收时间(Go 1.15+) 5 分钟 减少闲置连接占用

[!warning] 常见踩坑

  • 忘记设 SetConnMaxLifetime → 连接池中存活几年的旧连接,MySQL 早已断开,GORM 复用时报 server has gone away。
  • MaxOpenConns = 0(默认)→ 无上限,压测时可能打满数据库连接导致其他业务受影响。
  • MaxIdleConns > MaxOpenConns → panic,空闲连接不能超过最大打开数。

DSN 模板速查

数据库 驱动包 DSN 示例
MySQL gorm.io/driver/mysql user:pass@tcp(host:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local
PostgreSQL gorm.io/driver/postgres host=localhost user=gorm password=gorm dbname=gorm port=5432 sslmode=require TimeZone=Asia/Shanghai
SQLite gorm.io/driver/sqlite /path/to/db.sqlite(只需文件路径)
SQL Server gorm.io/driver/mssql sqlserver://user:pass@host:1433?database=dbname

GORM 操作链路概览

flowchart TD
    Init["创建数据库实例"] --> Model["定义模型结构体"]
    Model --> Migrate["迁移建表 AutoMigrate"]
    Migrate --> Operate["CRUD 数据操作"]
    Operate --> Tx["事务提交或回滚"]
    Operate --> Return["返回结果"]
    
    style Init fill:#4FC08D,color:#fff
    style Migrate fill:#EAB308,color:#fff
    style Operate fill:#3B82F6,color:#fff
    style Tx fill:#EF4444,color:#fff

默认事务机制说明

db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
    SkipDefaultTransaction: true, // 跳过默认事务包装
})

启用后,单次 Create / Update / Delete 操作不再自动包裹事务,性能提升约 10%~20%。但代价是失去了操作原子性保障,如果业务逻辑本身只包含单个 SQL 语句且不需要回滚,可以考虑开启。

GORM 思考题

[!question] 为什么 GORM 的 Create 操作会被自动包裹在事务中?这样做有什么利弊?

详见:01-安装与初始化/为什么GORM的Create会被自动包裹在事务中的

关联笔记