如何在NHibernate中映射索引的多对多集?

时间:2009-11-10 21:09:23

标签: nhibernate nhibernate-mapping many-to-many

考虑一个实体Entry,它包含另一个实体(多对多),Category的集合,这样Entry可以与给定的类别相关联不超过一次(暗示“集合”并受约束数据库)和集合中的类别实体的顺序是固定的,并由映射表上的额外字段定义(暗示“列表”)。对于这种情况,适当的映射是什么?

有关此问题试图解决的问题的其他详细信息/细节:

由于<set>不使用<index>,我们可以轻松地维护应用层中类别实体的唯一性(而不是使用NHibernate),使用{{1}似乎有意义,因为它将自动处理映射表上额外字段中的排序值的更新(这是我想要使用的最大“功能”)。不幸的是,在更新集合时,由于NHibernate执行更新的方式(即使新集合值最终不会导致约束违规),我们遇到约束违规,因为我们对相关对象之间的数据库有唯一约束在映射表中。

更具体地说,请考虑以下数据库架构:

<list>

以下映射:

CREATE TABLE Category (
  id int IDENTITY PRIMARY KEY,
  name varchar(50)
)
CREATE TABLE Entry (
  id int IDENTITY PRIMARY KEY,
  data varchar(50)
)
CREATE TABLE EntryCategory (
  categoryId int REFERENCES Category (id),
  entryId int REFERENCES Entry (id),
  index int,
  PRIMARY KEY (categoryid, entryId)
)

让我们考虑包含以下数据的初始数据库状态:

    EntryId  CategoryID  Index
    555      12          0
    555      13          1
    555      11          2
    555      2           3

在条目#555加载时,其<class name="Category"> <id name="Id"> <generator class="native"> </id> <property name="name"/> </class> <class name="Entry"> <id name="Id"> <generator class="native"> </id> <property name="data"/> <list name="Categories" table="EntryCategory"> <key column="entryID"/> <index column="index"/> <many-to-many class="Category" column="categoryID"/> </list> </class> 列表将包含以下元素:

    Index  Category ID
    0      12
    1      13
    2      11
    3      2

如果我们更新此列表以删除元素(索引1处的类别13),则如下所示:

    Index  CategoryID
    0      12
    1      11
    2      2

NHibernate将首先删除索引值最高的条目(因为大小已减少),执行以下操作:

Categories

在此更新之后,数据库中的数据如下所示:

    EntryId  CategoryID  Index
    555      12          0
    555      13          1
    555      11          2

接下来,它尝试使用正确的映射更新值,并以此updaet语句开头:

DELETE FROM EntryCategory WHERE entryId = @p0 AND index = @p1; 
@p0 = '555', @p1 = '3'

这与UPDATE EntryCategory SET CategoryID = @p0 WHERE EntryId = @p1 AND Index = @p2; @p0 = '11', @p1 = '555', @p2 = '1' 失败,因为它试图使数据看起来像这样:

    EntryId  CategoryID  Index
    555      12          0
    555      11          1
    555      11          2

似乎真正需要发生的事情是Violation of PRIMARY KEY constraintEntryId保持不变,而CategoryId值会更新,而不是离开Index和{{ 1}}单独使用值并更新EntryId值。但即使在这种情况下,如果对EntryID和Index也有一个UNIQUE约束(除IndexCategoryId上的主键外),也可能存在问题。

2 个答案:

答案 0 :(得分:1)

我认为你必须放松约束才能让它发挥作用。就像你说的那样,即使NHibernate更新了索引而不是categoryId,“如果对EntryID和Index也有一个UNIQUE约束,那么可能会出现问题”。这就是我的建议:

-- Looser constraints.  This should work.
CREATE TABLE EntryCategory (
  id int IDENTITY PRIMARY KEY,
  categoryId int REFERENCES Category (id),
  entryId int REFERENCES Entry (id),
  index int
);
CREATE INDEX IX_EntryCategory_category ON EntryCategory (entryId, categoryId);
CREATE INDEX IX_EntryCategory_index ON EntryCategory (entryId, index);

我的意思是,理想情况下你会得到类似下面的内容 - 但几乎不可能在其上运行任何UPDATE查询:

-- Nice tight constraints - but they are TOO tight.
CREATE TABLE EntryCategory (
  categoryId int REFERENCES Category (id),
  entryId int REFERENCES Entry (id),
  index int,
  PRIMARY KEY (entryId, categoryId),
  UNIQUE KEY (entryId, index)
);

每当我陷入这样的情况时,我真正想要的是一个独特的钥匙,但出于某种原因我不得不这样做,我宁愿选择索引。< / p>


如果它有用,Ayende有an example with a very similar mapping - 多对多列表。但他并没有谈论数据库架构。

答案 1 :(得分:0)

查看Oren Eini关于<map><set>标签的博客,尤其是<map>博客。我认为这就是你追求的目标。

<map>
<set>