Лучший способ моделировать отношения "Многие к одному" в NHibernate при работе с устаревшей базой данных?

StackOverflow https://stackoverflow.com/questions/10915

  •  08-06-2019
  •  | 
  •  

Вопрос

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

Это было полезно?

Решение

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

Вот два примера сопоставлений, которые могут подтолкнуть вас в правильном направлении, мне приходилось использовать имена таблиц adlib и т.д., Но, возможно, это могло бы помочь.Я бы, вероятно, также предложил сопоставить 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>

Другие советы

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

<?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>

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

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

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

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

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

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

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

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

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

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

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

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

Я думаю, проблема, с которой вы столкнулись здесь, заключается в том, что ваш объект AcceptedOffer содержит объект Plan, а затем ваш объект Plan, по-видимому, содержит коллекцию AcceptedOffers, которая содержит объекты AcceptedOffer.То же самое и с Клиентами.Я думаю, причина вашей проблемы в том, что объекты являются дочерними друг от друга.

Аналогично, что делает ваше принятое предложение сложным, так это то, что у него есть две обязанности:это указывает на предложения, включенные в план, это указывает на принятие клиентом.Это нарушает Принцип Единой ответственности.

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top