记一次gorm.ErrRecordNotFound踩坑记录

812 阅读2分钟

在某个项目中,有个数据验证的业务,即在数据库中查询数据是否存在,若数据已存在则返回错误并给前端提示。代码如下

Func01

func (t *ServiceInfo) Find(c *gin.Context, tx *gorm.DB, search *ServiceInfo) (*ServiceInfo, error) {
    out := &ServiceInfo{}
    err := tx.WithContext(c).Where(search).Find(out).Error
    if err != nil {
        return nil, err
    }
    return out, nil
}

gorm在之前的版本中,因为gorm的查询是链式的语句,所以中间出现的错误会存入到Error的参数集中处理。而且当没有查询到数据的时候也会得到错误ErrRecordNotFound。所以此代码就把错误同一处理,当controller中没有收到任何错误时,可以说明数据库中查询到了此数据,即校验重复了。

但是经过测试后发现无论如何err都是nil,且RowsAffected也明明为0。在网上也没有直接搜到这个坑的blog,于是我去翻了gorm最新的文档,发现了此段话!

image.png

即说明Find()方法不会再得到ErrRecordNotFound的错误

于是我采用了Find()和First()进行了对比的实验

Func02

func (t *ServiceInfo) Find(c *gin.Context, tx *gorm.DB, search *ServiceInfo) (*ServiceInfo, error) {
    out := &ServiceInfo{}
    resultFind := tx.WithContext(c).Where(search).Find(out)
    resultFirst := tx.WithContext(c).Where(search).First(out)
    log.Print("Find() Err: ", resultFind.Error, "\tFind Rows Affected: ", resultFind.RowsAffected)
    log.Print("First() Err: ", resultFirst.Error, "\tFind Rows Affected: ", resultFirst.RowsAffected)
    err := resultFind.Error
    if err != nil {
        return nil, err
    }
    return out, nil
}

得到输出

2022/07/25 10:00:35 Find() Err: <nil>   Find Rows Affected: 0
2022/07/25 10:00:35 First() Err: record not found       Find Rows Affected: 0

很明显,当查询不到结果的时候First()方法会返回ErrRecordNotFound,而Find()方法并不会

因此,若在不想改变代码原有的逻辑基础上,可以通过手动添加Error的方法来完成数据校验的工作

Func03

func (t *ServiceInfo) Find(c *gin.Context, tx *gorm.DB, search *ServiceInfo) (*ServiceInfo, error) {
    out := &ServiceInfo{}
    resultFind := tx.WithContext(c).Where(search).Find(out)
    if resultFind.RowsAffected < 1 {
        err := resultFind.AddError(gorm.ErrRecordNotFound)
        if err != nil {
            return nil, err
        }
    }
    err := resultFind.Error
    if err != nil {
        return nil, err
    }
    return out, nil
}

即通过 resultFind.RowsAffected < 1 来判断是否查询到数据,再通过resultFind.AddError(gorm.ErrRecordNotFound) 手动添加

ErrRecordNotFound错误,藉此来完成在旧版本中存在的功能。

PS:我个人并不是很理解为什么要取消Find()方法中的这个错误提示

此实现方式仅供参考,如有更漂亮的方法希望不吝赐教