Лучший способ смоделировать отношения "многие-к-одному" в NHibernate при работе с устаревшей БД?

Предупреждение - я новичок в NHibernate. Я знаю, что этот вопрос кажется простым - и я уверен, что есть простой ответ, но я уже некоторое время крутил колеса над этим. Я имею дело с устаревшей базой данных, которую действительно нельзя изменить структурно. У меня есть таблица с подробностями, в которой перечислены планы платежей, которые были приняты клиентом. Каждый план платежей имеет идентификатор, который ведет к справочной таблице, чтобы получить условия плана и т. Д. В моей объектной модели у меня есть класс AcceptedPlan и класс Plan. Первоначально я использовал отношение «многие к одному» из таблицы сведений обратно в таблицу ссылок для моделирования этой взаимосвязи в NHibernate. Я также создал отношение «один ко многим», идущее в противоположном направлении от класса Plan к классу AcceptedPlan. Это было нормально, пока я просто читал данные. Я мог перейти к своему объекту Plan, который был свойством моего класса AcceptedPlan, чтобы прочитать детали плана. Моя проблема возникла, когда мне пришлось начать вставлять новые строки в таблицу деталей. Насколько я знаю, кажется, что единственный способ создать новый дочерний объект - это добавить его к родительскому объекту, а затем сохранить сеанс. Но я не хочу создавать новый родительский объект Plan каждый раз, когда хочу создать новую подробную запись. Это похоже на ненужные накладные расходы. Кто-нибудь знает, поступаю ли я неправильно? Я не хочу создавать новый родительский объект Plan каждый раз, когда я хочу создать новую подробную запись. Это похоже на ненужные накладные расходы. Кто-нибудь знает, поступаю ли я неправильно? Я не хочу создавать новый родительский объект Plan каждый раз, когда я хочу создать новую подробную запись. Это похоже на ненужные накладные расходы. Кто-нибудь знает, поступаю ли я неправильно?

Ответов (6)

Решение

Я бы воздержался от наличия дочернего объекта, содержащего своего логического родителя, когда вы это сделаете, он может очень быстро стать очень беспорядочным и очень рекурсивным. Я бы посмотрел, как вы собираетесь использовать модель предметной области, прежде чем делать что-то подобное. Вы можете легко сохранить ссылки на идентификаторы в таблицах и просто оставить их несопоставленными.

Вот два примера сопоставления, которые могут подтолкнуть вас в правильном направлении. Мне пришлось изменить имена таблиц и т. Д., Но это может помочь. Я бы, вероятно, также предложил сопоставить StatusId с перечислением.

Обратите внимание на то, как сумка эффективно отображает таблицу деталей в коллекции.

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping default-cascade="save-update" xmlns="urn:nhibernate-mapping-2.2">
    <class lazy="false" name="Namespace.Customer, Namespace" table="Customer">
        <id name="Id" type="Int32" unsaved-value="0">
            <column name="CustomerAccountId" length="4" sql-type="int" not-null="true" unique="true" index="CustomerPK"/>
            <generator class="native" />
        </id>

        <bag name="AcceptedOffers" inverse="false" lazy="false" cascade="all-delete-orphan" table="details">
          <key column="CustomerAccountId" foreign-key="AcceptedOfferFK"/>
          <many-to-many
            class="Namespace.AcceptedOffer, Namespace"
            column="AcceptedOfferFK"
            foreign-key="AcceptedOfferID"
            lazy="false"
           />
        </bag>

  </class>
</hibernate-mapping>


<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping default-cascade="save-update" xmlns="urn:nhibernate-mapping-2.2">
    <class lazy="false" name="Namespace.AcceptedOffer, Namespace" table="AcceptedOffer">
        <id name="Id" type="Int32" unsaved-value="0">
            <column name="AcceptedOfferId" length="4" sql-type="int" not-null="true" unique="true" index="AcceptedOfferPK"/>
            <generator class="native" />
        </id>

        <many-to-one 
          name="Plan"
          class="Namespace.Plan, Namespace"
          lazy="false"
          cascade="save-update"
        >
        <column name="PlanFK" length="4" sql-type="int" not-null="false"/>
        </many-to-one>

        <property name="StatusId" type="Int32">
            <column name="StatusId" length="4" sql-type="int" not-null="true"/>
        </property>

  </class>
</hibernate-mapping>

Подход, который я бы использовал для моделирования этого, выглядит следующим образом:

Объект клиента содержит ICollection <PaymentPlan> PaymentPlans, который представляет планы, принятые клиентом.

План PaymentPlan для клиента будет сопоставлен с использованием пакета, который использует таблицу сведений, чтобы установить, какой идентификатор клиента сопоставлен с какими PaymentPlans. При использовании каскада all-delete-orphan, если клиент был удален, будут удалены как записи из подробностей, так и платежные планы, принадлежащие клиенту.

Объект PaymentPlan содержит объект PlanTerms, который представляет условия плана платежей.

PlanTerms будет сопоставлен с PaymentPlan с использованием каскадного сопоставления «многие-к-одному» с сохранением-обновлением, которое просто вставит ссылку на соответствующий объект PlanTerms в PaymentPlan.

Используя эту модель, вы можете создать PlanTerms независимо, а затем, когда вы добавляете новый PaymentPlan клиенту, вы должны создать новый объект PaymentPlan, передав соответствующий объект PlanTerms, а затем добавить его в коллекцию соответствующего клиента. Наконец, вы должны сохранить клиента и позволить nhibernate каскадировать операцию сохранения.

В итоге вы получите объект Customer, объект PaymentPlan и объект PlanTerms, в котором клиент (таблица клиентов) владеет экземплярами PaymentPlans (таблица деталей), которые все соответствуют определенным PlanTerms (таблица плана).

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

Я не знаю, возможно ли это из-за того, что мой опыт NHibernate ограничен, но не могли бы вы создать класс BaseDetail, который имеет только свойства для Details, поскольку они сопоставляются непосредственно с таблицей Detail.

Затем создайте второй класс, который наследуется от класса BaseDetail, который имеет дополнительный объект родительского плана, чтобы вы могли создать класс BaseDetail, когда вы хотите просто создать строку деталей и назначить ей PlanId, но если вам нужно заполнить полную деталь запись с объектом родительского плана можно использовать унаследованный класс Detail.

Я не знаю, имеет ли это большой смысл, но дайте мне знать, и я проясню дальше.

Совет, который может (а может и не быть) полезен в NHibernate: вы можете сопоставить свои объекты с представлениями, как если бы представление было таблицей. Просто укажите имя представления как имя таблицы; пока все поля NOT NULL включены в представление и отображение будет работать нормально.

I think the problem you have here is that your AcceptedOffer object contains a Plan object, and then your Plan object appears to contain an AcceptedOffers collection that contains AcceptedOffer objects. Same thing with Customers. The fact that the objects are a child of each other is what causes your problem, I think.

Likewise, what makes your AcceptedOffer complex is it has a two responsibilities: it indicates offers included in a plan, it indicates acceptance by a customer. That violates the Single Responsibility Principle.

Возможно, вам придется различать Предложение, входящее в план, и Предложение, которое принимается клиентами. Итак, вот что я собираюсь сделать:

  1. Создайте отдельный объект Offer, который не имеет состояния, например, у него нет клиента и у него нет статуса - в качестве атрибутов у него есть только OfferId и план, которому он принадлежит.
  2. Измените свой объект Plan, чтобы он имел коллекцию предложений (не обязательно принимать предложение в своем контексте).
  3. Наконец, измените объект AcceptedOffer, чтобы он содержал предложение, клиента и статус. Заказчик остался прежним.

Я думаю, что это в достаточной мере распутает ваши сопоставления NHibernate и проблемы с сохранением объектов. :)

Не видел диаграммы вашей базы данных, пока писал.

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping default-cascade="save-update" xmlns="urn:nhibernate-mapping-2.2">
    <class lazy="false" name="Namespace.Customer, Namespace" table="Customer">
        <id name="Id" type="Int32" unsaved-value="0">
            <column name="customer_id" length="4" sql-type="int" not-null="true" unique="true" index="CustomerPK"/>
            <generator class="native" />
        </id>

        <bag name="AcceptedOffers" inverse="false" lazy="false" cascade="all-delete-orphan">
            <key column="accepted_offer_id"/>
            <one-to-many class="Namespace.AcceptedOffer, Namespace"/>
        </bag>

  </class>
</hibernate-mapping>


<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping default-cascade="save-update" xmlns="urn:nhibernate-mapping-2.2">
    <class lazy="false" name="Namespace.AcceptedOffer, Namespace" table="Accepted_Offer">
        <id name="Id" type="Int32" unsaved-value="0">
            <column name="accepted_offer_id" length="4" sql-type="int" not-null="true" unique="true" />
            <generator class="native" />
        </id>

        <many-to-one name="Plan" class="Namespace.Plan, Namespace" lazy="false" cascade="save-update">
            <column name="plan_id" length="4" sql-type="int" not-null="false"/>
        </many-to-one>

  </class>
</hibernate-mapping>

Вероятно, это должно помочь (я сделал только примеры сопоставлений для коллекций, вам нужно будет добавить другие свойства).