gorm.Open 必须传入对应驱动的 Dialector 实例,如 mysql.Open(dsn),不可传字符串;Model 字段需显式标注 primaryKey 或 column;Where 条件中零值默认被忽略;事务内需用 tx.WithContext(ctx) 传递上下文。

gorm.Open 传参顺序和驱动名必须严格匹配
Go 语言中 gorm.Open 不是自动识别数据库类型的,它依赖你传入的 gorm.Dialector 实例。直接传字符串(比如旧版写法 gorm.Open("mysql", ...))在 v2+ 已被移除,硬写会编译失败。
常见错误现象:报错 undefined: gorm.Open 或 cannot use "xxx" (type string) as type gorm.Dialector。
- MySQL 必须用
mysql.Open(dsn),来自gorm.io/driver/mysql - PostgreSQL 对应
postgres.Open(dsn),来自gorm.io/driver/postgres - SQLite 是
sqlite.Open(file),注意参数是文件路径,不是 DSN
别漏掉 import 对应驱动包,否则 Open 函数根本不存在。
定义 Model 时字段标签不能只靠 struct 名称推断
GORM 不会默认把 Go 字段名转成 snake_case 数据库列名,除非你显式配置或加标签。比如 UserName 默认映射到 user_name 是因为 GORM 内置了蛇形命名策略,但一旦你写了 json:"user_name" 却没写 gorm:"column:user_name",GORM 仍可能生成 user_name 列——但这是巧合,不是保证。
立即学习“go语言免费学习笔记(深入)”;
- 主键必须明确:加
gorm:"primaryKey",否则 GORM 可能选错字段甚至报错failed to find primary field - 想跳过某字段不映射:用
gorm:"-:all"或gorm:" 控制读写权限 - 时间字段推荐用
gorm:"autoCreateTime;autoUpdateTime",避免手动赋值time.Now()
没有标签时,GORM 的默认行为受全局 NamingStrategy 影响,不同版本策略有差异,别依赖“好像能用”。
Query 时用 Where 链式调用比拼接 SQL 更安全也更灵活
手写 SQL 拼接字符串(如 "WHERE name = '" + name + "'")不仅易 SQL 注入,还绕过了 GORM 的预处理和类型转换。GORM 的 Where 支持多种写法,底层都走参数化查询。
db.Where("age > ?", 18).Find(&users)
db.Where("name LIKE ?", "%john%").Find(&users)
db.Where("status = ? AND role = ?", "active", "admin").Find(&users)
db.Where(&User{Status: "active", Role: "admin"}).Find(&users) // 结构体条件
注意:Where 后跟 map 或结构体时,零值(如 0, "", nil)默认会被忽略;如果真要查 age = 0,得用 Where("age = ?", 0)。
另外,First() 和 Take() 行为不同:First() 按主键升序,Take() 不保证顺序,查单条时别混用。
事务里用 db.WithContext(ctx) 传递超时或取消信号
单纯用 db.Transaction(func(tx *gorm.DB) error { ... }) 创建的事务内部,无法响应 context.Context 的 cancel 或 timeout。如果事务卡住(比如锁等待),整个 goroutine 就挂死。
- 正确做法是在事务函数内,用传入的
tx显式带上 context:tx.WithContext(ctx).Where(...).Update(...) - 外部调用前先设置超时:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - 别在事务里用原始
db实例,否则 context 不生效
还有一个容易被忽略的点:事务内执行 Create 后,如果结构体带自增主键,GORM 会把生成的 ID 回填到原变量,但这个回填只发生在事务成功提交后——若事务 rollback,ID 字段不会还原,业务层需自行清理或重置。










