0

0

如何使用Golang处理数据库操作错误_GolangSQL查询异常处理实践

P粉602998670

P粉602998670

发布时间:2025-12-31 19:21:08

|

597人浏览过

|

来源于php中文网

原创

Go 的 database/sql 错误不为 nil 不一定代表失败,需区分错误来源:Query/QueryRow 的 err 仅表示 SQL 提交是否成功,rows.Next() 返回 false 后须调用 rows.Err() 判断是否真出错,Scan 和 Close 也可能返回 error,事务中 Rollback 和 Commit 均需显式检查错误。

如何使用golang处理数据库操作错误_golangsql查询异常处理实践

Go 的 database/sql 错误不是 nil 就代表失败?

很多刚写 Go 数据库代码的人会误以为只要 err != nil 就一定出错了,其实不然。比如调用 rows.Next() 返回 false 可能只是查不到数据,也可能是底层驱动报错;而这个错误要等到你调用 rows.Err() 才能拿到。漏掉这一步,就会让 SQL 执行失败却“静默”通过。

正确做法是:每次遍历完 rows 后必须检查 rows.Err(),且不能只依赖 QueryRow().Scan() 的返回 err —— 它只捕获查询发起阶段的错误(如语法错、连接断),不包含扫描时的类型不匹配或空值处理异常。

  • Query()QueryRow() 返回的 err 仅表示“SQL 是否成功提交”,不代表结果能正常读取
  • rows.Scan() 失败不会自动终止循环,需在每次调用后检查其返回 err
  • rows.Close() 也可能返回 error(如网络中断导致清理失败),建议 defer 后仍做一次显式检查

如何区分 sql.ErrNoRows 和其他数据库错误?

sql.ErrNoRows 是唯一一个被标准库明确定义的“预期性错误”,它不是 bug,而是业务常态(例如查用户不存在)。直接用 if errors.Is(err, sql.ErrNoRows) 判断最稳妥,避免字符串匹配或 == 比较 —— 不同驱动返回的具体 error 类型可能不同(如 pgx 返回的是自定义类型)。

var user User
err := db.QueryRow("SELECT name, age FROM users WHERE id = $1", 123).Scan(&user.Name, &user.Age)
if err != nil {
    if errors.Is(err, sql.ErrNoRows) {
        // 用户不存在,业务逻辑继续
        return nil, nil
    }
    // 其他错误:字段类型不匹配、NULL 写入非指针等
    return nil, fmt.Errorf("query user: %w", err)
}
return &user, nil

注意:只有 QueryRow().Scan() 会返回 sql.ErrNoRowsQuery() + rows.Next() 不会,它只会让 Next() 返回 false,此时你要靠 rows.Err() 判断是否真出错。

立即学习go语言免费学习笔记(深入)”;

事务中遇到错误,tx.Rollback() 为什么还 panic?

常见写法是 defer tx.Rollback() 然后在中间 commit,但这样非常危险:如果 Rollback() 自己失败(比如连接已断),会触发 panic。更糟的是,panic 可能掩盖原本的业务错误。

正确模式是显式控制 rollback,并只在未 commit 时才调用:

新快购物系统
新快购物系统

新快购物系统是集合目前网络所有购物系统为参考而开发,不管从速度还是安全我们都努力做到最好,此版虽为免费版但是功能齐全,无任何错误,特点有:专业的、全面的电子商务解决方案,使您可以轻松实现网上销售;自助式开放性的数据平台,为您提供充满个性化的设计空间;功能全面、操作简单的远程管理系统,让您在家中也可实现正常销售管理;严谨实用的全新商品数据库,便于查询搜索您的商品。

下载
tx, err := db.Begin()
if err != nil {
    return err
}
defer func() {
    if r := recover(); r != nil {
        tx.Rollback()
        panic(r)
    }
}()

// 执行操作...
_, err = tx.Exec("INSERT INTO orders (...) VALUES (...)", ...)
if err != nil {
    tx.Rollback() // 显式回滚
    return fmt.Errorf("insert order: %w", err)
}

err = tx.Commit()
if err != nil {
    tx.Rollback() // commit 失败也要尝试回滚
    return fmt.Errorf("commit transaction: %w", err)
}

关键点:

  • 不要用 defer tx.Rollback() 无条件回滚,它无法区分“该回滚”和“已经 commit”
  • tx.Commit()tx.Rollback() 都可能返回 error,尤其是网络不稳定时
  • 如果使用 sqlxent 等 ORM,它们内部通常已封装了安全的事务控制,但底层逻辑仍遵循这一原则

database/sql 处理 NULL 值时最容易踩的坑

Go 的原生 Scan 不支持直接把数据库 NULL 映射到普通类型(如 intstring),强行这么做会导致 sql.ErrNoRows 以外的 panic 或静默截断。必须用指针或 sql.Null* 类型。

推荐优先使用 sql.NullStringsql.NullInt64 等,它们自带 Valid 字段,语义清晰:

var name sql.NullString
var age sql.NullInt64
err := row.Scan(&name, &age)
if err != nil {
    return err
}
if name.Valid {
    fmt.Println("Name:", name.String)
} else {
    fmt.Println("Name is NULL")
}

如果你用 struct + Scan,别忘了字段必须是导出的(首字母大写),且类型要严格匹配 —— sql.NullString 不能 Scan 进 *string,反之亦然。

另一个隐形陷阱:SELECT COALESCE(col, '') FROM t 看似绕过 NULL,但会丢失原始列的 NULL 语义,影响后续聚合或索引优化,不如在 Go 层明确处理。

真正难的不是写 if err != nil,而是想清楚这个 err 到底来自哪一层:驱动连接?SQL 解析?权限校验?类型转换?还是事务状态不一致?每种都需要不同的恢复策略。

相关专题

更多
golang如何定义变量
golang如何定义变量

golang定义变量的方法:1、声明变量并赋予初始值“var age int =值”;2、声明变量但不赋初始值“var age int”;3、使用短变量声明“age :=值”等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

174

2024.02.23

golang有哪些数据转换方法
golang有哪些数据转换方法

golang数据转换方法:1、类型转换操作符;2、类型断言;3、字符串和数字之间的转换;4、JSON序列化和反序列化;5、使用标准库进行数据转换;6、使用第三方库进行数据转换;7、自定义数据转换函数。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

224

2024.02.23

golang常用库有哪些
golang常用库有哪些

golang常用库有:1、标准库;2、字符串处理库;3、网络库;4、加密库;5、压缩库;6、xml和json解析库;7、日期和时间库;8、数据库操作库;9、文件操作库;10、图像处理库。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

335

2024.02.23

golang和python的区别是什么
golang和python的区别是什么

golang和python的区别是:1、golang是一种编译型语言,而python是一种解释型语言;2、golang天生支持并发编程,而python对并发与并行的支持相对较弱等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

206

2024.03.05

golang是免费的吗
golang是免费的吗

golang是免费的。golang是google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的开源编程语言,采用bsd开源协议。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

388

2024.05.21

golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

193

2025.06.09

golang相关判断方法
golang相关判断方法

本专题整合了golang相关判断方法,想了解更详细的相关内容,请阅读下面的文章。

188

2025.06.10

golang数组使用方法
golang数组使用方法

本专题整合了golang数组用法,想了解更多的相关内容,请阅读专题下面的文章。

191

2025.06.17

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

7

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

相关下载

更多

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Go 教程
Go 教程

共32课时 | 3.1万人学习

Go语言实战之 GraphQL
Go语言实战之 GraphQL

共10课时 | 0.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号