正确处理sql.ErrNoRows的方式是将其视为正常业务状态,使用errors.Is(err, sql.ErrNoRows)识别并根据场景返回nil、自定义错误或空集合,避免与数据库错误混淆。

在Golang中处理
sql.ErrNoRows
errors.Is
nil
很多人在刚接触Go的数据库操作时,会习惯性地将
row.Scan()
sql.ErrNoRows
if err != nil { return err }sql.ErrNoRows
正确的处理方式是,在获取到
row.Scan()
errors.Is(err, sql.ErrNoRows)
如果
errors.Is
true
立即学习“go语言免费学习笔记(深入)”;
nil
nil
nil
var ErrUserNotFound = errors.New("用户未找到")nil, ErrUserNotFound
sql.ErrNoRows
Query()
sql.ErrNoRows
rows.Next()
false
如果
err
nil
sql.ErrNoRows
这里有一个示例代码来展示这种处理方式:
动态WEB网站中的PHP和MySQL详细反映实际程序的需求,仔细地探讨外部数据的验证(例如信用卡卡号的格式)、用户登录以及如何使用模板建立网页的标准外观。动态WEB网站中的PHP和MySQL的内容不仅仅是这些。书中还提到如何串联JavaScript与PHP让用户操作时更快、更方便。还有正确处理用户输入错误的方法,让网站看起来更专业。另外还引入大量来自PEAR外挂函数库的强大功能,对常用的、强大的包
508
package main
import (
"database/sql"
"errors"
"fmt"
"log"
_ "github.com/go-sql-driver/mysql" // 假设使用MySQL驱动
)
// User 结构体用于映射数据库中的用户表
type User struct {
ID int
Name string
Email string
}
// GetUserByID 从数据库根据ID获取单个用户
func GetUserByID(db *sql.DB, id int) (*User, error) {
user := &User{}
// 假设我们有一个名为 'users' 的表
row := db.QueryRow("SELECT id, name, email FROM users WHERE id = ?", id)
err := row.Scan(&user.ID, &user.Name, &user.Email)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
// 这是正常的“未找到”情况,不是真正的错误。
// 我们可以选择返回 nil 和 nil 错误,或者一个自定义的业务错误。
// 这里我们返回 nil 和 nil 错误,让调用方判断 user 是否为 nil。
return nil, nil
}
// 其他真正的数据库错误,需要包装并向上层抛出。
return nil, fmt.Errorf("查询用户ID %d 失败: %w", err)
}
return user, nil
}
func main() {
// 实际应用中应配置数据库连接池
db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/database")
if err != nil {
log.Fatalf("无法连接到数据库: %v", err)
}
defer db.Close()
// 尝试获取一个存在的用户
user1, err := GetUserByID(db, 1)
if err != nil {
log.Fatalf("获取用户1时发生错误: %v", err)
}
if user1 == nil {
fmt.Println("用户1未找到。")
} else {
fmt.Printf("找到用户1: %+v\n", user1)
}
// 尝试获取一个不存在的用户
user2, err := GetUserByID(db, 999)
if err != nil {
log.Fatalf("获取用户999时发生错误: %v", err)
}
if user2 == nil {
fmt.Println("用户999未找到。") // 这会是预期的输出
} else {
fmt.Printf("找到用户999: %+v\n", user2)
}
// 模拟一个数据库错误(例如,表名错误)
// db.QueryRow("SELECT id FROM non_existent_table WHERE id = ?", 1).Scan(&user1.ID)
// 这种情况下,err 就不会是 sql.ErrNoRows,而是一个真正的数据库错误。
}将
sql.ErrNoRows
sql.ErrNoRows
nil
ErrNoRows
ErrNoRows
ERROR
errors.Is(err, sql.ErrNoRows)
sql.ErrNoRows
sql
我见过太多项目,因为不区分
sql.ErrNoRows
尽管我们强调
sql.ErrNoRows
sql.ErrNoRows
ErrNoRows
ErrUserNotFound
ErrNoRows
ErrNoRows
ErrNoRows
ErrNoRows
举个我遇到的例子:在一个订单处理系统中,当处理支付回调时,我们会根据订单ID去查询订单状态。如果此时数据库返回
sql.ErrNoRows
sql.ErrNoRows
为了让代码更整洁、更具可维护性,同时又能恰当地处理
sql.ErrNoRows
核心思想是:在数据库访问层(Repository/DAO)内部处理
sql.ErrNoRows
nil
sql.ErrNoRows
定义自定义错误: 首先,在你的
repository
dao
// repository/errors.go
package repository
import "errors"
var ErrNotFound = errors.New("记录未找到")封装数据库操作(Repository模式示例): 将数据库操作封装到一个结构体的方法中。在这些方法内部,处理
sql.ErrNoRows
repository.ErrNotFound
// repository/user_repository.go
package repository
import (
"database/sql"
"fmt"
"errors" // 导入errors包
)
// User 结构体(假设在 common/models 或此包内定义)
type User struct {
ID int
Name string
Email string
}
// UserRepository 定义了用户数据访问的接口
type UserRepository struct {
db *sql.DB
}
// NewUserRepository 创建一个新的 UserRepository 实例
func NewUserRepository(db *sql.DB) *UserRepository {
return &UserRepository{db: db}
}
// GetByID 根据ID从数据库获取单个用户
func (r *UserRepository) GetByID(id int) (*User, error) {
user := &User{}
row := r.db.QueryRow("SELECT id, name, email FROM users WHERE id = ?", id)
err := row.Scan(&user.ID, &user.Name, &user.Email)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
// 将底层的 sql.ErrNoRows 转换为我们自定义的 ErrNotFound
return nil, ErrNotFound
}
// 其他数据库错误,进行包装
return nil, fmt.Errorf("从数据库获取用户ID %d 失败: %w", id, err)
}
return user, nil
}
// GetAllUsers 获取所有用户(示例,通常不会返回 ErrNoRows)
func (r *UserRepository) GetAllUsers() ([]*User, error) {
rows, err := r.db.Query("SELECT id, name, email FROM users")
if err != nil {
return nil, fmt.Errorf("查询所有用户失败: %w", err)
}
defer rows.Close()
var users []*User
for rows.Next() {
user := &User{}
if err := rows.Scan(&user.ID, &user.Name, &user.Email); err != nil {
return nil, fmt.Errorf("扫描用户数据失败: %w", err)
}
users = append(users, user)
}
if err = rows.Err(); err != nil {
return nil, fmt.Errorf("遍历用户结果集失败: %w", err)
}
return users, nil // 如果没有结果,返回空切片,而不是 ErrNoRows
}业务逻辑层调用: 在业务逻辑层(Service层),你现在可以更简洁、更语义化地处理“未找到”的情况,而无需关心
sql.ErrNoRows
// service/user_service.go
package service
import (
"fmt"
"log"
"errors" // 导入errors包
"your_project/repository" // 假设你的repository包路径
)
type UserService struct {
userRepo *repository.UserRepository
}
func NewUserService(repo *repository.UserRepository) *UserService {
return &UserService{userRepo: repo}
}
func (s *UserService) GetUserDetails(userID int) (*repository.User, error) {
user, err := s.userRepo.GetByID(userID)
if err != nil {
if errors.Is(err, repository.ErrNotFound) {
// 业务逻辑层明确知道是“未找到”
log.Printf("用户ID %d 未找到。", userID)
// 可以选择返回 nil, nil,或者一个更高级别的业务错误
return nil, nil
}
// 其他真正的错误,记录并向上抛出
return nil, fmt.Errorf("获取用户详情失败: %w", err)
}
return user, nil
}
// main.go 中调用以上就是在Golang中处理数据库操作返回的sql.ErrNoRows的正确方式的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号