db.raw() 是执行原生 sql 的首选,不解析、不拦截,适用于复杂查询等场景;需注意占位符传参规则、表名列名须拼接并防注入、事务中必须用同一 tx 实例、returning 与 last_insert_id() 因数据库而异。

用 DB.Raw() 执行原生 SQL 最直接
想绕过 GORM 的 ORM 层直接发 SQL,DB.Raw() 是第一选择。它不解析、不拦截,把字符串原样交给数据库驱动,适合复杂查询、批量更新、存储过程调用等场景。
常见错误是传参方式不对:用 ? 占位符时,参数必须按顺序传入;用命名参数(如 :name)则需配合 sql.Named() 或 map,否则报 sql: expected 1 arguments, got 0。
- MySQL/PostgreSQL 都支持
?,但 SQLite 在某些驱动版本里对命名参数支持不稳定,优先用? - 参数不能是结构体字段名或表名——这些属于 SQL 结构,必须拼接(但要严格校验,防注入)
-
DB.Raw("SELECT * FROM users WHERE id = ?", 123).Scan(&u)可以;DB.Raw("SELECT * FROM ? WHERE id = ?", "users", 123)会报错
DB.Exec() 和 DB.Find() 别混用
DB.Exec() 用于不返回行的语句(INSERT/UPDATE/DELETE/DROP),返回 *sql.Result,能拿到影响行数;DB.Find() 或 Scan() 用于查数据,需要目标变量地址。
典型翻车点:用 Exec() 去查数据,结果没报错但 result.RowsAffected() 是 0,还误以为查到了空结果;反过来用 Find() 执行 UPDATE,GORM 会尝试映射字段,大概率 panic 或静默失败。
- INSERT 返回主键?用
Exec()后调result.LastInsertId()(MySQL)或result.LastInsertId()/result.RowsAffected()(PostgreSQL 需额外查RETURNING) - SELECT 必须配
Scan(&v)或Find(&vs),且变量类型要和 SELECT 字段顺序、类型严格匹配 - UPDATE/DELETE 建议加
WHERE,没条件就是全表操作——GORM 不做防护,执行完才告诉你影响了 10 万行
事务里执行原生 SQL 要用同一个 *gorm.DB 实例
在 DB.Transaction() 回调里,必须用传入的 tx 对象调 Raw() 或 Exec(),不能用外部的全局 db。否则原生 SQL 跑在默认连接上,不参与事务,导致部分成功、数据不一致。
Colly 是一个用于 Go 语言的快速开源爬取和爬虫框架。它适用于从简单的页面提取到异步爬虫处理大量页面集合,支持请求回调和结构化解析。
容易被忽略的是:事务内混合 ORM 操作和原生 SQL 时,GORM 的钩子(如 BeforeUpdate)不会触发,字段自动更新(UpdatedAt)、软删除标记等全失效。
- 正确写法:
tx.Raw("UPDATE orders SET status = ? WHERE id = ?", "shipped", 1001).Exec() - 错误写法:
db.Raw(...).Exec()—— 这个db是新连接,不在事务上下文里 - 如果 SQL 需要返回值(比如带
RETURNING的 UPDATE),记得用tx.Raw().Scan(),不是Exec()
PostgreSQL 的 RETURNING 和 MySQL 的 LAST_INSERT_ID() 处理方式不同
想在 INSERT/UPDATE 后立刻拿到生成值,PG 用 RETURNING 子句,MySQL 依赖 LAST_INSERT_ID() 函数,二者语法和 GORM 适配逻辑完全不同。
硬套会出问题:在 PG 里写 INSERT INTO t VALUES (...) ; SELECT LAST_INSERT_ID(),直接语法错误;在 MySQL 里用 RETURNING id,驱动不认识。
- PG 示例:
tx.Raw("INSERT INTO users(name) VALUES(?) RETURNING id", "alice").Scan(&id) - MySQL 示例:
tx.Exec("INSERT INTO users(name) VALUES(?)", "alice"); id, _ := result.LastInsertId() - 跨数据库项目别封装“通用插入函数”——SQL 差异太大,不如按方言分路径处理
GORM 的原生 SQL 支持很轻量,但它不帮你做 SQL 校验、不防注入、不保证事务一致性——这些都得你自己卡住边界。最常漏掉的是:事务中用了错的 DB 实例、表名列名拼接没过滤、以及 RETURNING 和 LAST_INSERT_ID 的混用。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










