无法在 Rails 3.2 中使用复合唯一索引将记录添加到连接表

Posted

技术标签:

【中文标题】无法在 Rails 3.2 中使用复合唯一索引将记录添加到连接表【英文标题】:Can't add record to join table with composite unique index in Rails 3.2 【发布时间】:2013-03-02 15:09:46 【问题描述】:

重要 - 阅读下面的编辑以获取有关问题的更新

当我尝试将新记录添加到 SQLite3 上具有唯一复合键索引的连接表时,我得到了我认为的虚假错误。请注意,对于我所做的所有(手动)测试,数据库已通过 db:drop 后跟 db:migrate 完全重建。

错误:

ActiveRecord::RecordNotUnique
  SQLite3::ConstraintException: columns adventurer_id, item_id are not unique: 
  INSERT INTO "adventurers_items" ("adventurer_id", "item_id") VALUES (1, 68)

产生错误的代码:

class Adventurer < ActiveRecord::Base

  after_create :set_starting_skills
  after_create :set_starting_items

  has_and_belongs_to_many :items
  has_and_belongs_to_many :skills

  # automatically add starting skills on creation
  def set_starting_skills
    self.skills = self.profession.starting_skills
  end

  # automatically add starting items on creation
  def set_starting_items
    self.items = self.profession.items
  end

创建连接表 Adventurers_skills 的迁移:

class AdventurersItems < ActiveRecord::Migration
  def change
    create_table :adventurers_items do |t|
      t.integer :item_id, :null => false
      t.integer :adventurer_id, :null => false
   end

add_index :adventurers_items, :item_id
add_index :adventurers_items, :adventurer_id
add_index :adventurers_items, [:adventurer_id, :item_id], :unique => true

该表存在并且完全为空。为什么我的应用程序由于唯一性约束而无法插入此记录?我对等效表“adventurers_skills”也有同样的错误——我在架构上做错了吗?

编辑

系统正在尝试添加相同的物品/技能两次。当我将私有方法更改为此:

def set_starting_skills
  skills = profession.starting_skills
end

它不会尝试在连接表中创建任何内容。但是将第一行恢复为 self.skills 如下尝试创建相同的技能 TWICE

def set_starting_skills
  self.skills = profession.starting_skills
end

返回

(0.4ms)  INSERT INTO "adventurers_skills" ("adventurer_id", "skill_id") VALUES (4, 54)
(4.9ms)  INSERT INTO "adventurers_skills" ("adventurer_id", "skill_id") VALUES (4, 54)
SQLite3::ConstraintException: columns adventurer_id, skill_id are not unique: 
INSERT INTO "adventurers_skills" ("adventurer_id", "skill_id") VALUES (4, 54)
(3.2ms)  rollback transaction

profession.starting_skills只返回一项技能:

1.9.3-p194 :022 > Profession.find(7).starting_skills.each |x| puts x.id
54

所以真正的问题变成了:为什么 Rails 会尝试两次添加这个 HABTM 记录?

【问题讨论】:

请向我们展示导致该错误的代码。模型和迁移实际上并没有插入数据。不过,还有别的东西。所以请向我们展示“其他东西” 嗯,有一个错误在某处调用了两次回调方法,因此它试图插入一个项目两次。 那么,您解决了这个问题吗?因为我得到的完全一样。 【参考方案1】:

您需要将回调声明 (after_create :set_starting_skills) 放在 关系声明 (has_and_belongs_to_many :skills) 之后。

即模型中线的顺序很重要,否则会出现此错误。

这太疯狂了,还有a GitHub issue for it。

【讨论】:

以上是关于无法在 Rails 3.2 中使用复合唯一索引将记录添加到连接表的主要内容,如果未能解决你的问题,请参考以下文章

MongoDB中唯一索引的优势

MongoDB - 唯一索引与复合索引

MongoDB——索引属性之唯一索引(Unique Indexes)

MongoDB——索引属性之唯一索引(Unique Indexes)

mysql 复合 唯一 索引

复合索引主键与唯一自增主键