2015-08-11 3 views
10

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

Введение:

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

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

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

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

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

Mapping: (упрощенный)

<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 вместо всего объекта.

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

+0

Кажется, что то, что вы пытаетесь сделать, должно работать. Исключение указывает, что NHibernate пытается выполнить UPDATE против вашего объекта Order, который сопоставляется с невосстанавливаемым представлением. NHibernate будет делать это только в том случае, если он считает, что объект грязный, вне коллекций OrderLines. Вы должны проверить свои другие отображаемые свойства. И, возможно, попробуйте использовать профайлер, например NHProf, чтобы узнать, что меняется. –

+0

Точно. Я бы хотел НЕ обновить свой заказ, потому что он сопоставлен с не обновляемым представлением. Вместо этого я хотел бы только обновить дочерний объект, т. Е.мешок OrderLines. Я использую NHProfiler, и исключение сверху - это в основном SQL-Исключение, которое передается через NHibernate. – JuniorCoder

ответ

1

В случае, если я понимаю вашу проблему, решение на удивление прост.Мы просто ознаменуем корневой объект с 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(); 

И что это. Каскад на корневой объект делает то, что нам нужно, в то время как обновление = «ложь» не обновляет его ...

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

19.2.2. Strategy: read only

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

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

+0

Hi Radim, Большое спасибо за помощь. Я уже опробовал установку mutable = "false" без каких-либо успехов. Я пробовал использовать динамическое обновление, но я все равно получаю те же ошибки. Ошибка, описанная выше, связана со вставками данных, а не с обновлениями, которые, как я полагаю, нацелены на ваш пример, если они верны? – JuniorCoder

+0

Да, как я уже сказал, 'mutable =" false "' не является решением здесь. Но маркировка всех свойств, связанных с представлением, как 'update =" false "' будет работать. Просто '' будет сохранен ... Потому что ... если вы скажете, что вы сопоставили представление, мы должны говорить ПРОСТО ОБ ОБНОВЛЕНИИ. Итак, как вы описали * (если я прочитал его правильно) * - нам нужно сохранить только коллекцию - во время обновления. В этом случае даже элементы этой коллекции могут быть ЗАВЕРШЕНЫ. Но вставка новой записи в родительский вид ... не имеет смысла в зависимости от вашего сценария. Это то, что я проверил и работает .. или? –

+0

Я имею в виду часть вашего вопроса * «... В идеале я бы хотел, чтобы NHibernate сохранял только объекты OrderLine, а не весь объект ...» *, поэтому, если у нас есть существующая запись (та, которая представлена view, отображаемый как родительский объект), мы вставим только OrderLines ... и это решение будет поддержано приведенным выше ответом. Заказ уже существует (не обновляется NHibernate), все элементы в OrderLines полностью управляемы ... –

3

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

непреложным классы, изменяемые = «ложь», не может быть изменен или удален приложение. Это позволяет 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>