必须用?占位,严禁字符串拼接;命名参数非通用,仅postgresql例外;字段名与结构体严格匹配且注意大小写;事务中避免session()脱离上下文。

用 Raw() 执行原生 SQL 时,参数必须用问号占位
直接拼接字符串传入 Raw() 是高危操作,会导致 SQL 注入。GORM 的 Raw() 支持位置参数(?),但不支持命名参数(如 :name)——除非你用的是 PostgreSQL 驱动并显式启用命名参数支持,但那属于例外,不是通用解法。
正确写法是:
db.Raw("SELECT * FROM users WHERE age > ? AND status = ?", 18, "active").Scan(&users)
- 所有用户输入值必须作为后续参数传入,不能拼进 SQL 字符串
- MySQL/SQLite 驱动只认
?;PostgreSQL 默认也只认?,即使它原生支持$1,GORM 会自动转换 - 如果参数类型不匹配(比如把
string当int传给?),GORM 不报错但可能查不到结果或类型转换失败
执行 UPDATE/DELETE 前务必加 Where() 条件,避免全表误操作
GORM 的 Exec() 不做 WHERE 检查,一条没条件的 Raw("UPDATE users SET ...").Exec() 会直接更新整张表——连软删除字段 deleted_at 都不管。
安全做法是:哪怕只是执行原生 SQL,也优先走 GORM 的链式查询构造器补上约束:
db.Where("id = ?", userID).Exec("UPDATE users SET name = ? WHERE id = ?", newName, userID)
-
Where()生成的条件会合并进最终 SQL,双重保险 - 不要依赖原生 SQL 里的 WHERE —— 因为
Where()后调用Raw()会被忽略,必须用Exec()或Session()显式携带 - 对敏感操作,建议先用
Raw().Rows()查出影响行数预览,再决定是否Exec()
查询结果映射到结构体时,字段名必须和 SELECT 列完全一致(大小写敏感)
GORM 默认按字段名(非列别名)匹配结构体字段,而数据库对大小写的处理因驱动而异:MySQL 默认不区分,PostgreSQL 默认区分,SQLite 取决于编译选项。
例如这段 SQL:
db.Raw("SELECT id AS ID, name AS Name FROM users").Scan(&users)
在 PostgreSQL 下会失败,因为结构体字段 ID 和 Name 对应的列名是 "ID" 和 "Name"(带双引号),但 GORM 发送的查询不会自动加引号。
- 最稳方案:SELECT 列名全部小写,结构体字段也用小写(
type User struct { ID uint; Name string }) - 或者用
AS显式别名为小写:SELECT id AS id, name AS name - 避免依赖数据库默认行为,尤其跨环境部署时(开发用 SQLite、生产用 PostgreSQL)
事务中混用原生 SQL 和 GORM 方法,要注意 Session 是否共享
在 db.Transaction() 里,如果你用 db.Raw(),它默认使用当前事务的 *gorm.DB 实例,没问题;但一旦你中途调用了 db.Session(...) 或新建了 db.WithContext(),就可能脱离事务上下文。
典型错误:
db.Transaction(func(tx *gorm.DB) error {
tx.Raw("INSERT INTO logs (...) VALUES (?)", msg).Exec() // ✅ 在事务内
otherDB := tx.Session(&gorm.Session{NewDB: true}) // ❌ 新建 session,脱离事务
otherDB.Raw("UPDATE counters SET total = total + 1").Exec() // 这条不进事务!
return nil
})
- 只要没显式调用
Session()、WithContext()或Clone(),Raw()和First()等方法都共享同一事务 - 不确定时,打印
tx.Statement.ConnPool == db.Statement.ConnPool可验证是否同连接池 - 原生 SQL 不触发 GORM 的钩子(
BeforeUpdate等),事务内混合使用时逻辑一致性得自己兜底
GORM 执行原生 SQL 最容易被忽略的其实是字段映射大小写和事务隔离粒度——前者导致本地能跑线上查不到数据,后者让事务看似成功实则部分语句已提交。动手前先确认驱动行为,比写完再调试成本低得多。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











