Go错误处理在数据库操作中的应用_Go SQL错误判断技巧

12次阅读

应使用 Errors.Is(err, sql.ErrNoRows) 显式判断查询无数据,而非 err == sql.ErrNoRows 或泛化错误处理;仅 QueryRow/QueryRowContext 返回该错误,Query 需用 rows.Next() 判断。

Go错误处理在数据库操作中的应用_Go SQL错误判断技巧

如何判断 sql.ErrNoRows 而不是泛化地用 errors.Is 捕获所有错误

数据库查询没找到数据时,db.QueryRow().Scan() 会返回 sql.ErrNoRows,这是预期行为,不是异常。直接用 if err != nil 判定会导致误报业务错误。

  • 必须用 errors.Is(err, sql.ErrNoRows) 显式识别,不能用 err == sql.ErrNoRows —— 因为底层可能包装过
  • 若想忽略“无数据”并返回零值,可直接写 if errors.Is(err, sql.ErrNoRows) { return 0, nil }
  • 注意:只有 QueryRowQueryRowContext 会返回 sql.ErrNoRowsQuery 返回空 *sql.Rows,需靠 rows.Next() 判断

driver.ErrBadConn 的真实用途与过时风险

这个错误曾用于提示连接失效,让调用方重试。但自 go 1.8 起,database/sql 已内置连接健康检查和自动重试逻辑,driver.ErrBadConn 不再被上层识别为重试信号。

  • 现代驱动(如 pgxmysql)已弃用该错误;若你还在代码里手动判断它,大概率是旧文档或遗留逻辑
  • 真正需要重试的场景(如网络闪断),应依赖 context.WithTimeout + 外层重试控制,而非解析底层驱动错误
  • 如果驱动仍返回 driver.ErrBadConn,建议升级驱动版本,或改用 errors.Is(err, driver.ErrBadConn) 后直接记录日志,不再做恢复动作

事务中遇到 sql.ErrTxDone 怎么办

调用 tx.Commit()tx.Rollback() 后,该事务对象即进入完成状态。后续再对它执行 tx.Querytx.Exec 就会返回 sql.ErrTxDone —— 这是明确的使用错误,不是并发问题。

  • 常见诱因:defer 中写了 tx.Rollback(),但前面已显式 Commit(),导致重复操作
  • 正确模式是用 if tx == nil 或标志位控制 rollback 是否执行,或者统一用 defer func() { if r := recover(); r == nil && !committed { _ = tx.Rollback() } }()
  • 不要试图恢复 sql.ErrTxDone,它表示你已经失去了对该事务的控制权,必须新建事务重试
func updateUser(tx *sql.Tx, id int, name string) error { 	_, err := tx.Exec("UPDATE users SET name = ? WHERE id = ?", name, id) 	if err != nil { 		return err 	} 	// 此处若误加 tx.Rollback(),下次再调用 tx.Exec 就会触发 sql.ErrTxDone 	return nil }

为什么 errors.As 在数据库错误中很少用得上

errors.As 用于提取底层错误类型(比如把包装后的错误转成 *pq.Error),但它只在你需要读取数据库特有字段(如 postgresqlCodeDetail)时才有意义。多数业务层只需分类处理,不需要深入驱动细节。

  • PostgreSQL 场景下,若要区分唯一约束冲突(23505)和外键失败(23503),才需 errors.As(err, &pqErr)
  • MySQL 驱动目前不导出错误结构体errors.As 无法提取具体 SQLSTATE,只能靠 err.Error() 字符串匹配(不推荐)
  • 跨数据库项目应避免依赖驱动特定错误,优先用 errors.Is 判断通用语义错误(如 sql.ErrNoRowssql.ErrTxDone

实际写业务逻辑时,最容易被忽略的是:**错误是否真的需要传播出去**。比如一个用户资料查询接口,查不到就该返回 404;但同一个错误若出现在订单创建的关联校验里,可能就得回滚整个事务。错误语义必须和上下文强绑定,而不是统一塞进 log.Fatal 或往上 panic。

text=ZqhQzanResources