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

如何使用Ecto.Multi delete并避免Repo.get_by返回nil时出错?

解决Ecto.Multi中Repo.get_by返回nil时的FunctionClauseError问题

问题背景

原本的业务逻辑是先批量删除指定图片,再根据num_id删除对应的产品(如果存在):

from(p in Mirror.Imports.Picture,
  where: p.picture_name in ^picture_name_list
)
|> Repo.delete_all()

case Repo.get_by(Mirror.Imports.Product, num_id: num_id) do
  nil -> :ok
  value -> Repo.delete(value) |> then(fn _ -> :ok end)
end

尝试用Ecto.Multi合并操作时写出了如下代码:

Ecto.Multi.new()
|> Ecto.Multi.delete_all(:delete_all, from(p in Mirror.Imports.Picture, where: p.picture_name in ^picture_name_list))
|> Ecto.Multi.delete(:delete, Repo.get_by(Mirror.Imports.Product, num_id: num_id))

但当Repo.get_by/3返回nil时触发了FunctionClauseError——因为Ecto.Multi.delete/4的函数子句仅接受Ecto.Changeset、结构体或1元函数,nil不符合任何子句要求。

解决方案

方案1:利用Ecto.Multi.delete的函数参数

Ecto.Multi.delete/4支持传入接收Repo的1元函数,在函数内部处理nil的情况:

Ecto.Multi.new()
|> Ecto.Multi.delete_all(:delete_all, from(p in Mirror.Imports.Picture, where: p.picture_name in ^picture_name_list))
|> Ecto.Multi.delete(:delete, fn repo ->
  case repo.get_by(Mirror.Imports.Product, num_id: num_id) do
    nil -> {:ok, nil}  # 返回ok表示操作成功跳过
    product -> repo.delete(product)
  end
end)

方案2:提前判断,动态添加操作

先获取产品实例,再根据是否为nil决定是否向Multi中添加删除操作:

# 初始化基础Multi操作
multi = 
  Ecto.Multi.new()
  |> Ecto.Multi.delete_all(:delete_all, from(p in Mirror.Imports.Picture, where: p.picture_name in ^picture_name_list))

# 根据产品是否存在动态扩展Multi
multi = 
  case Repo.get_by(Mirror.Imports.Product, num_id: num_id) do
    nil -> multi  # 产品不存在,不添加删除操作
    product -> Ecto.Multi.delete(multi, :delete, product)
  end

# 执行事务
Repo.transaction(multi)

方案3:用Ecto.Multi.run手动处理

Ecto.Multi.run/3是更通用的操作方式,适合处理复杂条件逻辑:

Ecto.Multi.new()
|> Ecto.Multi.delete_all(:delete_all, from(p in Mirror.Imports.Picture, where: p.picture_name in ^picture_name_list))
|> Ecto.Multi.run(:delete, fn repo, _changes ->
  case repo.get_by(Mirror.Imports.Product, num_id: num_id) do
    nil -> {:ok, nil}
    product -> repo.delete(product)
  end
end)

说明

  • 方案1和方案3的优势是将数据库查询延迟到事务执行阶段,避免在事务外提前查询导致的数据一致性问题
  • 方案2更直观,适合简单场景,但如果在获取产品和执行事务之间有其他操作修改数据,可能出现不一致

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 13:45:30