Как сохранить подмножество объекта вместо всего объекта?
Я борюсь с проблемой, связанной с 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">