考虑一个实体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 constraint
和EntryId
保持不变,而CategoryId
值会更新,而不是离开Index
和{{ 1}}单独使用值并更新EntryId
值。但即使在这种情况下,如果对EntryID和Index也有一个UNIQUE约束(除Index
和CategoryId
上的主键外),也可能存在问题。
答案 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)