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

