You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Diesel的on_conflict_do_nothing时遇Err.NotFound问题求助

解决Diesel中create_or_get函数冲突时返回NotFound的问题

你遇到的问题根源很明确:当使用on_conflict_do_nothing时,如果目标用户已存在,数据库不会执行任何插入操作,returning子句也就没有数据返回,Diesel的get_result自然会返回Err.NotFound——这是该方法的预期行为,因为插入操作没有产生任何行。

下面提供两种可行的解决方案:

方案一:利用冲突时的更新操作(推荐)

通过指定冲突列(这里是name,因为它是唯一约束),在冲突时执行一个符合业务逻辑的更新(比如更新updated_at为当前时间),这样returning子句会返回已存在的用户记录,且只需要一次数据库请求:

pub fn create_or_get(&self, user_name: &String) -> QueryResult<User> {
    use self::schema::users::dsl::*;
    let new_user = NewUser { name: user_name.clone() };
    let conn = &mut self.pool.get().unwrap();
    
    diesel::insert_into(users)
        .values(&new_user)
        .on_conflict(name) // 指定触发冲突的唯一约束列
        .do_update()
        .set(updated_at = diesel::dsl::now()) // 冲突时更新更新时间,也可以用`set(name = name)`这种无意义更新
        .returning(User::as_returning())
        .get_result(conn)
}

方案二:插入失败后查询已存在用户

如果不想修改任何字段,可以先尝试插入,当捕获到NotFound错误时,再执行一次查询获取已存在的用户:

pub fn create_or_get(&self, user_name: &String) -> QueryResult<User> {
    use self::schema::users::dsl::*;
    let new_user = NewUser { name: user_name.clone() };
    let conn = &mut self.pool.get().unwrap();
    
    match diesel::insert_into(users)
        .values(&new_user)
        .on_conflict(name)
        .do_nothing()
        .returning(User::as_returning())
        .get_result(conn)
    {
        Ok(user) => Ok(user),
        Err(diesel::result::Error::NotFound) => {
            users.filter(name.eq(user_name))
                .first(conn)
        },
        Err(e) => Err(e),
    }
}

两种方案对比:

  • 方案一效率更高,仅需一次数据库交互,还能维护updated_at字段的准确性;
  • 方案二更直观,但冲突场景下会触发两次数据库请求。

内容的提问来源于stack exchange,提问作者Manuelarte

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 08:17:33