Рассмотрим сущность Entry, которая содержит коллекцию Category (многие-ко-многим) другой сущности, такую, что Entry может быть связана с данной Категорией не более одного раза (что подразумевает «множество» и ограничено база данных), а порядок объектов категории в коллекции фиксирован и определяется дополнительным полем в таблице сопоставления (подразумевается «список»). Какое будет правильное отображение для такого случая?
Дополнительные сведения / сведения о проблеме, которые пытается решить этот вопрос:
Поскольку <set>
не использует <index>
, и мы можем легко поддерживать уникальность объектов Category на прикладном уровне (в отличие от использования NHibernate для этого), использование <list>
, кажется, имеет смысл, поскольку он автоматически обработает обновление значения порядка в дополнительном поле таблицы сопоставления (это самая большая «функция», которую я хотел бы использовать). К сожалению, при обновлении коллекции мы сталкиваемся с нарушениями ограничений из-за способа, которым NHibernate выполняет обновления (даже когда новые значения коллекции в конечном итоге не вызовут нарушения ограничений), поскольку у нас есть уникальное ограничение на базу данных между связанными объектами в таблице сопоставления.
В частности, рассмотрим следующую схему базы данных:
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)
)
И следующие сопоставления:
<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>
Рассмотрим исходное состояние базы данных, содержащее следующие данные:
EntryId CategoryID Index
555 12 0
555 13 1
555 11 2
555 2 3
При загрузке записи # 555 ее список Categories
будет содержать следующие элементы:
Index Category ID
0 12
1 13
2 11
3 2
Если мы обновим этот список, чтобы удалить элемент (категория 13 с индексом 1), чтобы он выглядел следующим образом:
Index CategoryID
0 12
1 11
2 2
NHibernate сначала удалит запись с наибольшим значением индекса (поскольку размер уменьшился), выполнив следующее:
DELETE FROM EntryCategory WHERE entryId = @p0 AND index = @p1;
@p0 = '555', @p1 = '3'
После этого обновления данные в базе выглядят так:
EntryId CategoryID Index
555 12 0
555 13 1
555 11 2
Затем он пытается обновить значения с правильными сопоставлениями и начинает с этого оператора updaet:
UPDATE EntryCategory SET CategoryID = @p0 WHERE EntryId = @p1 AND Index = @p2;
@p0 = '11', @p1 = '555', @p2 = '1'
Это не с Violation of PRIMARY KEY constraint
, так как он пытается сделать данные похожими на:
EntryId CategoryID Index
555 12 0
555 11 1
555 11 2
Похоже, что действительно должно произойти, это то, что EntryId
и CategoryId
оставлены в покое, и значения Index
обновляются вместо того, чтобы оставлять значения EntryId
и Index
в одиночку, и обновляются CategoryId
ценности. Но даже в этом случае может возникнуть проблема, если, скажем, существуют также уникальные ограничения на EntryID и Index (в дополнение к первичному ключу на EntryID
и CategoryID
).