掌握gorm基础需完成三件事:能跑通crud、理解automigrate“只增不减”特性(不改类型/长度、不删字段)、避开time.time扫描错误——必须在dsn中加parsetime=true和loc=local。

能跑通 CRUD、理解 AutoMigrate 行为、避开 time.Time 扫描错误,这三件事做完,你就已经掌握 GORM 基础用法了。
连接数据库时必须加 parseTime=true
不加这个参数,只要模型里有 CreatedAt、UpdatedAt 或自定义的 time.Time 字段,查询就会 panic,报错类似:Scan error on column index 1, name "created_at": unsupported Scan, storing driver.Value type []uint8。
DSN 字符串中要显式带上:parseTime=true 和 loc=Local(或 loc=Asia/Shanghai),否则时间字段读出来是字节切片,不是 time.Time。
常见写法示例:
dsn := "root:123456@tcp(127.0.0.1:3306)/testdb?charset=utf8mb4&parseTime=true&loc=Local"
-
parseTime=true是硬性要求,漏掉必报错 -
loc=Local避免时区偏移导致时间值错位(比如存进去是 10:00,查出来变 02:00) - 如果用 Docker 启动 MySQL,确认容器时区和宿主机一致,否则
loc=Local可能失效
AutoMigrate 不会修改已有字段类型或删除字段
AutoMigrate 的行为是“只增不减、只宽不窄”:它只会新增缺失的表或字段,不会删字段,也不会把 string 改成 int,更不会把 size:50 改成 size:100(除非你删库重来)。
比如你原来有:
type User struct {
ID uint
Name string `gorm:"size:50"`
}
后来改成:
type User struct {
ID uint
Name string `gorm:"size:100"`
Age int
}
执行 db.AutoMigrate(&User{}) 后:
-
age字段会被新增 -
name字段长度仍为 50,不会自动扩到 100 - 如果手动在数据库里删了
name字段,它也不会被重建(因为结构体字段还在)
真正想改结构,得靠手动 SQL 或工具如 migrate 库。别指望 AutoMigrate 替你做 DDL 变更。
预加载 Preload 是防 N+1 的唯一低成本方案
一对多关系下,直接遍历查子数据(比如查完 100 个用户再循环查每人文章)必然触发 N+1 查询,接口延迟飙升。这时候不用手写 JOIN,直接用 Preload 就行。
例如:
var users []User
db.Preload("Articles").Find(&users)
这会生成两条 SQL:一条查 users,一条用 IN 批量查所有关联的 articles(WHERE user_id IN (1,2,3,...))。
- 嵌套预加载也支持:
Preload("Articles.Comments") - 带条件的预加载:
Preload("Articles", "status = ?", "published") - 注意:不能对未定义
foreignKey的字段用Preload,GORM 会静默忽略,查不到关联数据也不报错
事务必须用传入的 tx 对象,不能混用全局 db
写事务时,如果在 db.Transaction(func(tx *gorm.DB) error { ... }) 回调里继续用外面的 db 而不是 tx,那这部分操作根本不在事务内,提交回滚都控制不到。
正确写法:
err := db.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(&user).Error; err != nil {
return err
}
if err := tx.Create(&order).Error; err != nil {
return err
}
return nil
})
- 回调函数里的所有数据库操作,必须用
tx -
tx是带事务上下文的独立实例,和db不共享状态 - 哪怕只是查一条数据做校验(比如检查余额),也得用
tx.First(),否则可能读到事务外的脏数据
最常被忽略的是 parseTime 参数和 Preload 的静默失败——前者让程序直接崩,后者让接口慢得莫名其妙却查不出原因。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











