Как можно сопоставить индексированный набор «многие ко многим» в NHibernate? - PullRequest
2 голосов
/ 11 ноября 2009

Рассмотрим сущность 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).

Ответы [ 2 ]

1 голос
/ 04 декабря 2009

Я думаю, что вам придется ослабить свои ограничения, чтобы это заработало. Как вы сказали, даже если NHibernate обновит индекс вместо categoryId, «может возникнуть проблема, если, скажем, были также уникальные ограничения на EntryID и Index». Вот что я бы предложил:

-- 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);

Я имею в виду, в идеале вы должны иметь что-то вроде следующего - но было бы почти невозможно выполнить какие-либо запросы ОБНОВЛЕНИЯ на нем:

-- 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)
);

Всякий раз, когда я застреваю в такой ситуации, когда то, что я действительно хочу, является уникальным ключом, но по какой-то причине я вынужден обходиться без него, я вместо этого соглашусь на индекс. * <ч />

Если это полезно, у Айенде есть пример с очень похожим отображением - список «многие ко многим». Однако он не говорит о схеме базы данных.

0 голосов
/ 11 ноября 2009

Загляните в блог Орен Эйни о тегах <map> и <set>, в частности, блоге <map>. Я думаю, что это то, что вы после.

<map>
<set>

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...