我的代码导致数据库中出现罕见的双重或三重插入,我不知道为什么。重现起来非常困难,但我可以查看时间戳,看看当时创建的时间基本相同。我相信只有在找不到CardMeta
时才会发生。
我想我需要添加一个唯一的密钥或将其包装在一个事务中。
def get_or_create_meta(user, card) do
case Repo.all(from c in CardMeta, where: c.user_id == ^user.id,
where: c.card_id == ^card.id) do
[] ->
%CardMeta{}
metas ->
hd metas
end
end
def bury(user, card) do
get_or_create_meta(user, card)
|> Repo.preload([:card, :user])
|> CardMeta.changeset(%{last_seen: DateTime.utc_now(), user_id: user.id, card_id: card.id,
learning: false, known: false, prev_interval: 0})
|> Repo.insert_or_update
end
编辑:添加变更集来源
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [:last_seen, :difficulty, :prev_interval, :due, :known, :learning,
:user_id, :card_id])
|> assoc_constraint(:user)
|> assoc_constraint(:card)
end
从控制器召唤埋葬
def update(conn, %{"currentCardId" => card_id, "command" => command}) do
# perform some update on card
card = Repo.get!(Card,card_id)
user = Guardian.Plug.current_resource(conn)
case command do
"fail" ->
SpacedRepetition.fail(user, card)
"learn" ->
SpacedRepetition.learn(user, card)
_ ->
SpacedRepetition.bury(user, card)
end
sendNextCard(conn, user)
end
编辑:
我注意到重复行之间的last_seen字段是微秒不同,而create_at字段没有该分辨率。因此我怀疑insert_or_update调用是正常的,但控制器在数据库更新之前触发两次。这可能是客户方面的事情,我不想考虑。所以我只想添加一个独特的密钥。
答案 0 :(得分:1)
作为@ aliCna答案的替代方案,如果您不想更改CardMeta
上的主键,则可以通过迁移在数据库中放置唯一的索引约束:
defmodule YourApp.Repo.Migrations.AddCardMetaUniqueIndex do
use Ecto.Migration
def change do
create unique_index(
:card_meta,
[:card_id, :user_id],
name: :card_meta_unique_index)
end
end
然后,您可以在变更集中处理,以便在发生冲突时产生错误:
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [:last_seen, :difficulty, :prev_interval, :due, :known, :learning,
:user_id, :card_id])
|> assoc_constraint(:user)
|> assoc_constraint(:card)
|> unique_constraint(:user_id, name: :card_meta_unique_index)
end
答案 1 :(得分:0)
我相信您可以通过在user_id
和card_id
上添加复合主键来解决此问题
defmodule Anything.CardMeta do
use Anything.Web, :model
@primary_key false
schema "card_meta" do
field :user_id, :integer, primary_key: true
field :card_id, :integer, primary_key: true
. . .
timestamps()
end
end
如果这不能解决您的问题,请在此处添加您的数据模型!