Как сохранить подмножество объекта вместо всего объекта?

Я борюсь с проблемой, связанной с NHibernate, где я мог бы использовать некоторый ввод.

Введение:

У меня есть устаревшая база данных, где реляционные понятия на самом деле не применяются.

В базе данных есть таблица OrderLine, которая содержит данные для строк заказа.

Кроме того, таблица также содержит все столбцы с Order конкретной информацией. Это может быть, например, номер заказа клиента.

E.x. Если у меня есть 10 строк порядка, то у меня есть 10 строк в моей таблице OrderLines, и каждая строка имеет все специфические данные Order, например. номер заказа или информация о клиенте.

Я не хотел иметь вышеуказанную структуру в своем коде, поэтому для Orders было создано представление, чтобы я мог сопоставить мой Order в NHibernate, который затем имеет набор/пакет OrderLines, который делает намного больше смысл.

Отображение: (упрощено)

<class name="Order" table="[view_Orders]">
    <bag name="OrderLines">
</class>

<class name="OrderLine" table="OrderLines" />

Проблема:

Сложность представления делает невозможным сохранение в представлении. При попытке NHibernates выбрасывает это исключение:

NHibernate.Exceptions.GenericADOException: не удалось вставить: XXX --- > System.Data.SqlClient.SqlException: представление или функция 'view_Orders' не обновляется, поскольку модификация затрагивает несколько базовых таблиц.

Мое отображение NHibernate построено как объект Order, который имеет "набор или сумку" объектов OrderLine. В идеале я бы хотел, чтобы NHibernate только сохранял набор объектов OrderLine вместо всего объекта.

Есть ли способ достичь этого? Я попытался заблокировать объект, используя разные режимы блокировки, но мне это не помогло.

Ответ 1

Вы можете использовать mutable="false", чтобы избежать обновления и удаления, поскольку в этой статье говорится:

Неизменяемые классы, mutable = "false" , не могут быть обновлены или удалены приложением. Это позволяет NHibernate сделать некоторые незначительные оптимизации производительности.

Чтобы избежать вставки, вы можете использовать следующий оператор (используется proyection вместо вставить, не забывайте использовать check="none"):

<sql-insert check="none">SELECT 1</sql-insert>

Вот пример:

<class name="Order" table="[view_Orders]" mutable="false">
  <id name="OrderId" type="System.Guid">
    <generator class="guid.comb"/> <!-- Change as you need -->
  </id>

  <!-- Other properties -->
  <!-- <property name="GrandTotal"/> -->

  <set name="OrderLines" lazy="true" inverse="true" cascade="all-delete-orphan">
    <key column="OrderId"/>
    <one-to-many class="OrderLine"/>
  </set>

  <sql-insert check="none">SELECT 1</sql-insert>
</class>

<class name="OrderLine" table="OrderLine">
  <id name="OrderLineId" type="System.Guid">
    <generator class="guid.comb"/> <!-- Change as you need -->
  </id>

  <!-- Other properties -->
  <!-- <property name="OrderId"/>
  <property name="GrandTotal"/>/> --> 

</class>

Ответ 2

В случае, если я понимаю вашу проблему, решение на удивление просто. Мы просто отметили бы корневой объект dynamic-update="true"

<class name="Order" table="[view_Orders]" dynamic-update="true">
    ...
</class>

И затем примените update="false" к каждому свойству или ссылке, которые мы имеем в этом классе Order, отображаемом для просмотра:

...
<property name="Code"       update="false"/>
...
<many-to-one name="Country" update="false />

Но нашей коллекции понадобится стандартное, даже каскадное отображение:

<class name="Order" table="[view_Orders]" dynamic-update="true">
    <bag name="OrderLines" 
         lazy="true" 
         inverse="true" 
         batch-size="25" 
         cascade="all-delete-orphan" >
      ...
     </bag>
     ... // other stuff is update="false"
</class>

И теперь такой код будет иметь управление OrderLines, но не выполняет никаких обновлений для корневого объекта Order

var session = ... // get ISession 
// load root
var root = session.Get<Order>(123);

// if needed change existing line (pretend there is one)
root.OrderLines[0].Amount = 100;

// add new
var newOrder = ... // new order
root.OrderLines.Add(newOrder);

session.Save(root);
session.Flush();

И это все. Каскад на корневом объекте делает то, что нам нужно, а update = "false" не обновляет его...

ПРИМЕЧАНИЕ. Просто интересно отметить - есть класс и коллекция установка mutable="false", но здесь это не сработает... как решение было упомянуто выше (грустно, потому что это будет больше элегантный, но не работает, как ожидалось...). См:

19.2.2. Стратегия: только чтение

Если вашему приложению необходимо читать, но никогда не изменять экземпляры постоянного класса, может использоваться кеш-доступ только для чтения. Это самая простая и эффективная стратегия. Его даже совершенно безопасно для использования в кластере.

<class name="Eg.Immutable" mutable="false">