登录
首页 >  Golang >  Go教程

Go语言GORM使用原生SQL教程【避坑】

时间:2026-05-16 16:44:17 330浏览 收藏

本文深入剖析了Go语言中GORM使用原生SQL(Raw())的四大核心陷阱与最佳实践:参数绑定失效往往源于?占位符未正确传参或nil值处理不当;Scan映射失败多因字段名大小写不匹配、结构体非导出或缺失db标签;事务中必须使用tx.Raw()而非db.Raw()才能真正纳入事务上下文;性能瓶颈则常来自滥用Scan大结构体,推荐改用Rows()手动扫描或Row().Scan()精准获取单值——帮你避开生产环境高频踩坑点,写出更健壮、高效、可维护的原生SQL代码。

Go语言GORM如何用原生SQL_Go语言GORM Raw SQL教程【避坑】

gorm.Raw() 执行原生 SQL 时参数绑定失效?

不是 SQL 写错了,大概率是用了 ? 却没传参,或者传了 []interface{} 但里面混了 nil —— GORM 会静默跳过 nil,导致占位符数量对不上。

  • MySQL/PostgreSQL 都用 ? 占位,别写 $1:name,GORM 的 Raw() 不识别这些
  • 参数必须显式传进 Raw() 第二个及之后的参数,不能塞进切片再展开(Raw(sql, args...) 可以,但 Raw(sql, args) 不行)
  • nil 值要显式转成 sql.NullString 等类型,或改用 map[string]interface{} + NamedExec()
  • 示例:db.Raw("SELECT * FROM users WHERE id > ?", 100).Scan(&users) ✅;db.Raw("...", []interface{}{100})

Scan() 无法把 raw query 结果映射到结构体?

常见于字段名大小写不匹配、缺少 db 标签、或结构体字段非导出(小写开头)—— GORM 只扫导出字段,且默认按蛇形(user_name)映射到驼峰(UserName),但原生 SQL 不走这个逻辑。

  • 原生查询返回的列名必须和结构体字段名**完全一致**(区分大小写),除非加 AS 别名
  • 结构体字段必须首字母大写(导出),且建议加 gorm:"column:xxx" 显式指定来源列
  • 如果只查部分字段,结构体里没对应的字段,GORM 不报错也不赋值,容易漏数据
  • 示例:db.Raw("SELECT id, name AS Name FROM users").Scan(&u),对应结构体字段为 Name string `gorm:"column:Name"`

事务中执行 Raw() 后 Commit 失败或数据丢失?

因为 Raw() 默认不参与当前事务上下文——它拿到的是底层 *sql.DB,不是事务内的 *sql.Tx。除非你手动把事务对象传进去。

  • 正确做法:用 tx.Raw(...).Scan(),其中 tx 是从 db.Begin() 拿到的事务对象
  • 错误写法:db.Raw(...)tx 里调用,它仍走全局连接池,跟事务无关
  • PostgreSQL 对事务内 prepared statement 更敏感,裸 SQL 若含 CREATE TEMP TABLE 等,可能被事务隔离机制拦截
  • 验证是否在事务中:打印 tx.Statement.ConnPool == tx.ConnPool 应为 true

Raw() 查询性能比 GORM Model 方法慢很多?

通常不是 SQL 本身慢,而是没关掉 GORM 的反射开销和中间层处理——比如你只想要一行 ID,却用 Scan(&[]User{}) 让 GORM 做完整结构体初始化。

  • 能用 Rows() 就别用 Scan():返回 *sql.Rows,自己 rows.Next() + rows.Scan(),绕过 GORM 全流程
  • 避免 Scan 到大结构体:查单字段就用 var id int64; db.Raw("SELECT id FROM ...").Row().Scan(&id)
  • 注意驱动兼容性:SQLite 的 Raw() 不支持多语句,MySQL 默认也不开,multiStatements=true 要手动加在 DSN 里
  • 高频 raw 查询建议预编译:用 db.Session(&gorm.Session{PrepareStmt: true}).Raw(...)

最常被忽略的一点:Raw() 返回的 *gorm.DB 实例不再具备 Model 方法链能力,比如不能接 Where()Limit() —— 它就是个执行器,后续操作得靠 SQL 本身写清楚。

以上就是本文的全部内容了,是否有顺利帮助你解决问题?若是能给你带来学习上的帮助,请大家多多支持golang学习网!更多关于Golang的相关知识,也可关注golang学习网公众号。

资料下载
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>