db.Raw()执行原生SQL需用?占位符防注入,支持链式Scan()或Exec();Exec()是快捷写法,返回sql.Result,但不触发GORM钩子和关联逻辑。

用 db.Raw() 执行带参数的原生 SQL
直接执行自定义 SQL 最常用的方式是 db.Raw(),它返回一个 *gorm.DB 实例,可链式调用 Scan() 或 Exec()。关键点在于参数必须用 ? 占位(无论数据库驱动是 MySQL、PostgreSQL 还是 SQLite),GORM 会自动做转义防注入。
常见错误:写成 fmt.Sprintf 拼接 SQL —— 这会导致 SQL 注入,且在 PostgreSQL 中 $1 占位符会被 GORM 忽略,直接报错“pq: syntax error”。
- 正确写法:
db.Raw("SELECT * FROM users WHERE age > ? AND status = ?", 18, "active").Scan(&users) - 更新/删除操作用
Exec():db.Raw("UPDATE users SET status = ? WHERE id = ?", "archived", 123).Exec() - 若需复用查询结构,可先定义 SQL 字符串变量,但占位符和参数顺序必须严格对应
用 db.Exec() 执行无返回结果的语句
db.Exec() 是 db.Raw().Exec() 的快捷写法,适合 INSERT / UPDATE / DELETE / DDL 等不需扫描结果的场景。它返回 sql.Result,可通过 RowsAffected() 获取影响行数。
注意:GORM v2 默认开启事务,但 Exec() 不参与 GORM 的钩子(如 BeforeUpdate),也不会触发关联更新或软删除逻辑。如果你依赖这些行为,应改用 GORM 的模型方法而非原生 SQL。
立即学习“go语言免费学习笔记(深入)”;
- 获取影响行数:
res := db.Exec("DELETE FROM logs WHERE created_at - DDL 语句(如
CREATE INDEX)在某些驱动中可能不支持参数化,此时需确保输入已校验,避免拼接用户数据 - MySQL 中执行
LOAD DATA INFILE等非标准语句时,Exec()可用,但需确认底层*sql.DB支持
查询结果映射到结构体或 map 时的类型对齐问题
用 Raw().Scan() 查询时,字段名默认按数据库列名(非驼峰)匹配结构体字段。若列名含下划线(如 user_name),而结构体字段是 UserName,默认不会自动映射 —— GORM 不做蛇形转驼峰的隐式转换。
解决方式有两种:显式指定别名,或用 map[string]interface{} 接收再手动赋值。前者更可控,后者适合动态列场景。
- 用别名对齐字段:
db.Raw("SELECT id, user_name AS user_name, created_at AS created_at FROM users").Scan(&users),对应结构体字段名保持一致 - 结构体标签无效:
json:"user_name"或gorm:"column:user_name"在Raw().Scan()中不生效 - 查单行进 map:
var m map[string]interface{}; db.Raw("SELECT count(*) as total FROM users").Scan(&m),注意 key 是字符串,value 是interface{},需类型断言
事务内执行自定义 SQL 的注意事项
在 db.Transaction() 中调用 Raw() 或 Exec() 是安全的,它们共享同一底层 *sql.Tx。但容易被忽略的是:事务上下文必须传入 Raw() 链式调用的起始 db 实例 —— 如果你从全局 gorm.DB 开始,没用事务对象,SQL 就会跑在自动提交模式下。
典型陷阱:在事务函数里误用了未传参的全局 db 变量,导致部分操作回滚失败。
- 正确方式:
err := db.Transaction(func(tx *gorm.DB) error { return tx.Raw("INSERT INTO ...").Exec().Error }) - 不要写:
db.Transaction(func(tx *gorm.DB) error { return db.Raw("...").Exec().Error })—— 这里的db是外部变量,不绑定事务 - 事务中混合使用 GORM 方法与
Raw()没问题,只要都基于同一个tx实例
最易被绕过的细节是字段映射规则和事务上下文传递 —— 它们不报错,但行为和预期不符,调试成本远高于语法错误。


















