9

現在、データ アクセス レイヤーとして NHibernate を使用しており、Fluent NHibernate を使用してマッピング ファイルを作成しています。TripItem と TripItemAttributeValue の 2 つのクラスがあり、それらの間に多対多の関係があります。

マッピングは次のとおりです。

public class TripItemMap : ClassMap<TripItem2>
{
    public TripItemMap()
    {
        WithTable("TripItemsInt");
        NotLazyLoaded();

        Id(x => x.ID).GeneratedBy.Identity().WithUnsavedValue(0);
        Map(x => x.CreateDate, "CreatedOn").CanNotBeNull();
        Map(x => x.ModifyDate, "LastModified").CanNotBeNull();

        /* snip */

        HasManyToMany<TripItemAttributeValue>(x => x.Attributes).AsBag()
            .WithTableName("TripItems_TripItemAttributeValues_Link")
            .WithParentKeyColumn("TripItemId")
            .WithChildKeyColumn("TripItemAttributeValueId")
            .LazyLoad();
    }
}

public class TripItemAttributeValueMap : ClassMap<TripItemAttributeValue>
{
    public TripItemAttributeValueMap()
    {
        WithTable("TripItemAttributeValues");

        Id(x => x.Id).GeneratedBy.Identity();
        Map(x => x.Name).CanNotBeNull();

        HasManyToMany<TripItem2>(x => x.TripItems).AsBag()
            .WithTableName("TripItems_TripItemAttributeValues_Link")
            .WithParentKeyColumn("TripItemAttributeValueId")
            .WithChildKeyColumn("TripItemId")
            .LazyLoad();
    }
}

アプリケーションのある時点で、データベースから既存の属性をフェッチし、それらを tripItem.Attributes に追加してから、tripItem オブジェクトを保存します。最終的に、TripItems_TripItemAttributeValues_Link は新しいレコードを取得しないため、関係が保持されません。

役立つ場合、これらはこれらのクラスに対して Fluent NHibernate によって生成されたマッピング ファイルです。

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-lazy="true" assembly="ETP.Core" namespace="ETP.Core.Domain">
  <class name="TripItem2" table="TripItemsInt" xmlns="urn:nhibernate-mapping-2.2" lazy="false">
    <id name="ID" column="ID" type="Int32" unsaved-value="0">
      <generator class="identity" />
    </id>
    <property name="CreateDate" column="CreatedOn" type="DateTime" not-null="true">
      <column name="CreatedOn" />
    </property>
    <property name="ModifyDate" column="LastModified" type="DateTime" not-null="true">
      <column name="LastModified" />
    </property>
    <bag name="Attributes" lazy="true" table="TripItems_TripItemAttributeValues_Link">
      <key column="TripItemId" />
      <many-to-many column="TripItemAttributeValueId" class="ETP.Core.Domain.TripItemAttributeValue, ETP.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
    </bag>
  </class>
</hibernate-mapping>

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-lazy="true" assembly="ETP.Core" namespace="ETP.Core.Domain">
  <class name="TripItemAttributeValue" table="TripItemAttributeValues" xmlns="urn:nhibernate-mapping-2.2">
    <id name="Id" column="Id" type="Int32">
      <generator class="identity" />
    </id>
    <property name="Name" column="Name" length="100" type="String" not-null="true">
      <column name="Name" />
    </property>
    <bag name="TripItems" lazy="true" table="TripItems_TripItemAttributeValues_Link">
      <key column="TripItemAttributeValueId" />
      <many-to-many column="TripItemId" class="ETP.Core.Domain.TripItem2, ETP.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
    </bag>
  </class>
</hibernate-mapping>

ここで何が間違っていますか?

4

9 に答える 9

8

@efdee

私は同じ問題を抱えていて、これにほぼ2日を費やしました. 多対多の関係があり、リンク テーブルも更新されていませんでした。私は NHibernate を初めて使用しますが、それを学ぼうとしているだけです。

Fluent NHibernateでもマッピングでもないことが判明しましたが、NHibernateが多対多でどのように機能するかを理解していません。多対多の関係では、両方のエンティティのコレクションが設定されていない場合、NHibernate はデータをリンク テーブルに保存しません。

このエンティティが多対多の関係にあるとしましょう:


partial class Contact
{
   public string ContactName {get; set;}
   public IList Locations {get; set;}

}

partial class Location
{
   public string LocationName {get; set;}
   public string LocationAddress {get;set;}
   public IList Contacts {get;set;}
}

Contact.Locations に場所を追加するときは、連絡先が location.Contacts 内にも存在することを確認する必要があります。

場所を追加するには、Contact クラス内にこのメソッドを持っています。


public void AddLocation(Location location)
        {
            if (!location.Contacts.Contains(this))
            {
                location.Contacts.Add(this);
            }
            Locations.Add(location);
        }

これで私の問題は解決したようですが、私が言ったように、NHibernate を手に取って学習しているだけなので、もっと良い方法があるかもしれません。誰かがより良い解決策を持っている場合は、投稿してください。

これは、両方のコレクションを確認するように私に指示した投稿です: http://www.coderanch.com/t/217138/Object-Relational-Mapping/link-table-of-ManyToMany-annotation

于 2009-01-21T20:51:59.467 に答える
7

Session.Flush() を呼び出すか、トランザクションを使用します。

于 2009-10-17T17:49:27.203 に答える
2

Fluent NHibernateでどのように行うかはわかりませんが、バッグのカスケードオプションを設定する必要があります(TripItems)。いつものように、Ayendeにはカスケードオプションに関する役立つ投稿があります

簡単なGoogleから、次のことを試してみることをお勧めします。

HasManyToMany<TripItem2>(x => x.TripItems).AsBag()
        .WithTableName("TripItems_TripItemAttributeValues_Link")
        .WithParentKeyColumn("TripItemAttributeValueId")
        .WithChildKeyColumn("TripItemId")
        .LazyLoad()
/*-->*/ .Cascade.All(); /*<-- this is the bit that should make it work */
于 2009-01-02T13:55:37.947 に答える
1

私はまったく同じ問題を抱えていますが、NHibernate.JetDriverを使用しています。推奨される答えを使ってみましたが、うまくいきませんでした。NHibernate.JetDriverに多対多に関する制限があるかどうか誰かが知っていますか?

誰かがたまたまそれらをレビューすることに興味を持った場合に備えて、私のhbmファイルは次のとおりです。

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Ace.Docs.Core.Domain" assembly="Ace.Docs.Core">
<class name="Ace.Docs.Core.Domain.Address, Ace.Docs.Core" table="Addresses" lazy="true">
    <id name="Id" column="ID">
        <generator class="identity" />
    </id>
    <property name="Address1" column="Address1" />
    <property name="Address2" column="Address2" />
    <property name="City" column="City" />
    <property name="EmailAddress" column="EmailAddress" />
    <property name="Phone1" column="Phone1" />
    <property name="Phone2" column="Phone2" />
    <property name="PostalCode" column="PostalCode" />
    <property name="StateOrProvince" column="StateOrProvince" />
    <many-to-one name="AddressTypeMember" column="AddressTypeID" class="AddressType" />
    <bag name="HasPersonalInfo" table="Link_PersonalInfo_Addresses" lazy="true" cascade="save-update" inverse="true" >
        <key column="AddressID"></key>
        <many-to-many column="PersonalInfoID" class="PersonalInfo" />
    </bag>
</class>

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Ace.Docs.Core.Domain" assembly="Ace.Docs.Core">
<class name="Ace.Docs.Core.Domain.PersonalInfo, Ace.Docs.Core" table="PersonalInfo" lazy="true">
    <id name="Id" column="ID">
        <generator class="identity" />
    </id>
    <property name="Prefix" column="Prefix" />
    <property name="FirstName" column="FirstName" />
    <property name="MiddleName" column="MiddleName" />
    <property name="LastName" column="LastName" />
    <property name="SIN" column="SIN" />
    <property name="Birthdate" column="Birthdate" />
    <property name="Note" column="Notes" />
    <bag name="HasAddress" table="Link_PersonalInfo_Addresses" lazy="true" cascade="save-update" inverse="true" >
        <key column="PersonalInfoID"></key>
        <many-to-many column="AddressID" class="Address" />
    </bag>
</class>

于 2009-06-12T16:44:02.163 に答える
1

デビッド・ケンプはそれを正しく言っています:あなたはあなたのバッグにカスケードを追加したいと思っています.

私は常にマッピング ファイルを手作業で編集 (および手作業で作成) してきたので、そこに配置するのが私の自然な傾向です。次のように実行できます。

<bag name="TripItems" lazy="true" table="TripItems_TripItemAttributeValues_Link" cascade="all">
  <key column="TripItemAttributeValueId" />
  <many-to-many column="TripItemId" class="ETP.Core.Domain.TripItem2, ETP.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</bag>

クラスを「純粋」に保ち、すべてを .hbm.xml ファイルで nHibernate に関係させることで、ソリューションがよりクリーンに保たれることがわかりました。そうすれば、使いたい新しい ORM ソフトウェアがあれば、マッピング ファイルを置き換えるだけで、クラスを書き直す必要はありません。単体テストを使用してクラスをテストし、xml にテスト容易性を与えますが、私は Fluent NHibernate のメソッドが好きです。

于 2009-01-02T14:20:03.467 に答える
1

私はそれを手に入れました。これが他の誰かに役立つことを願っています。問題は、両方のバッグに inverse='true' があったことです。以下の抜粋を読むと、バッグの 1 つだけで inverse を true に設定する必要があることがわかります。

inverse="true" の使用に注意してください。もう一度、この設定は NHibernate に、カテゴリ コレクションに加えられた変更を無視し、関連付けのもう一方の端 (アイテム コレクション) をデータベースと同期する必要がある表現として使用するように指示します。

于 2009-06-12T17:14:27.517 に答える
0

私はCascade.All()が私の問題の実際の解決策ではないのではないかと心配しています-それは私が試したことの1つでした。問題は、コレクションに追加されたアイテムが保存されないことではありません。コレクションに追加された時点で、それらはすでにデータベースに存在しています。リンクテーブルのエントリが作成されていないだけです。さらに、Cascade.All()によって子アイテムも削除されると思いますが、これは私のシナリオでは望ましくない動作です。Cascade.SaveUpdate()を使用してみましたが、指摘したように、これは実際には私の問題ではない問題を解決します:-)

ただし、念のため、このソリューションを再試行して、結果をお知らせします。

クラスを純粋に保つことに関しては、これはFluent NHibernateの場合の100%です。作成するクラスマッピングは、.hbm.xmlファイルとほぼ同じように、エンティティクラスと一緒に使用されるC#コードファイルです。

于 2009-01-03T02:16:19.263 に答える