使用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
相关产品推荐
相关产品推荐

