sqlx不是orm,仅增强database/sql;查单行必须用get(传结构体地址且字段带db tag),否则字段映射失败;returning需配get/select;命名参数仅postgresql原生支持。

sqlx 不是 ORM,它只是让 database/sql 更好用;直接用 QueryRow 会跳过结构体自动扫描,字段映射必然失败。
Get 和 Select 必须传结构体地址,且字段要带 db tag
常见错误是定义了 User 结构体但没加 db 标签,或标签值和 SQL 返回列名不一致。比如数据库列是 user_name,但结构体写成 db:"name",就会扫不到。
- 必须用地址:
db.Get(&user, query, args...),不是db.Get(user, ...) - 字段名默认按小写匹配,但强烈建议显式写
dbtag,避免大小写/下划线歧义 - 如果 SELECT 中用了别名(如
SELECT id AS user_id),tag 就得是db:"user_id" - 查不到数据时
Get返回sql.ErrNoRows,要用errors.Is(err, sql.ErrNoRows)单独判断,别和连接错误混在一起处理
Exec 只处理无结果集语句,RETURNING 必须配 Get 或 Select
PostgreSQL 的 INSERT ... RETURNING id 看似是 INSERT,实则是带结果集的查询语句。用 Exec 调用只会丢掉返回值,还不会报错——这是最隐蔽的坑。
-
Exec适用于纯增删改:没有结果集,只关心Result.RowsAffected()或Result.LastInsertId()(仅 MySQL/SQLite 支持) - PostgreSQL 想拿插入 ID,必须写
db.Get(&id, "INSERT ... RETURNING id", ...) - 如果 RETURNING 多列,就用结构体 +
Get;只取一个字段,可直接扫到基础类型变量地址,如var id int64; db.Get(&id, query, ...) - MySQL/SQLite 不支持 RETURNING,想拿自增 ID 就只能靠
LastInsertId(),别在 PostgreSQL 里硬套
命名参数在不同驱动下行为不一致,别裸写 :name
PostgreSQL 原生支持 :name,但 MySQL 和 SQLite 不支持。直接写 db.Query("SELECT * FROM u WHERE name = :name", map[string]interface{}{"name": "a"}) 在 MySQL 下会 panic。
- 跨数据库兼容写法:统一用位置参数(
?或$1),靠sqlx.Rebind()自动转换 - 真要用命名参数,必须走
NamedQuery或NamedExec:先 prepare 再 exec,底层会做参数重绑定 - 传参用 map 时键名必须和 SQL 中
:xxx完全一致,少个字符或大小写不对都会报driver: bad connection(其实是参数不匹配) - 事务中用
NamedExec报错,优先检查 map 键名、是否传了 nil 值、以及该字段是否允许 NULL
sqlx.Connect 和 sqlx.Open 的关键区别在 Ping 时机
sqlx.Open 和标准库 sql.Open 行为一致:只初始化驱动,不建真实连接;而 sqlx.Connect 会在返回前自动调一次 Ping,确保连接可用。
- 开发/测试环境建议用
Connect,早暴露配置错误(比如密码错、库不存在) - 生产环境若想延迟校验(比如启动时不强依赖 DB 可用),就用
Open,但后续首次操作前务必自己加db.Ping() - 无论哪种方式,都得手动调
db.SetMaxOpenConns和db.SetMaxIdleConns,否则默认不限制,容易打爆数据库连接数 -
Connect失败会直接返回 error;Open成功后Ping失败,也得当初始化失败处理——不能忽略
最容易被忽略的是:结构体字段没加 db tag 时,sqlx 会静默 fallback 到字段名小写匹配,一旦数据库列名含下划线或大小写不一致,就扫空字段且不报错。上线后才发现用户邮箱总为空,这种问题很难复现。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











