4

これが私のクラスです:

public class User
{
  public int Id { get; set; }
  public string Name { get; set; }

  public ISet<User> Friends { get; set; }
}

これが私のマッピングです:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
  namespace="Test" assembly="test">

  <class name="User" table="Users">
      <id name="Id" column="id">
          <generator class="native"/>
      </id>
      <property name="Name" column="name"/>
      <set name="Friends" table="Friends">
          <key column="user_id"/>
          <many-to-many class="User" column="friend_id"/>
      </set>
  </class>
</hibernate-mapping>

ここに問題があります:

User user = session.Load<User>(1);

User friend = new User();
friend.Name = "new friend";

user.Friends.Add(friend);

最後の行[user.Friends.Add(friend)]で、新しい友達を追加する前に、Friendsコレクションが初期化されることに気付きました。

私の質問は:NHibernateでこの動作を回避する方法はありますか?パフォーマンス上の理由から、INSERTコマンドを1つだけ実行したいからです。

4

2 に答える 2

2

Hibernate.org から

要素を追加または削除したいだけなのに、Hibernate が常にコレクションを初期化するのはなぜですか?

残念ながら、コレクション API は、データベースにアクセスすることによってのみ計算できるメソッドの戻り値を定義します。これには 3 つの例外があります。戻り値は常に true でなければなりません。

余分なデータベース トラフィック (パフォーマンスが重要なコードなど) を避けたい場合は、多対 1 の関連付けのみを使用するようにモデルをリファクタリングします。これはほとんどの場合可能です。次に、コレクション アクセスの代わりにクエリを使用します。

さらに、このブログエントリNHibernate and Inverse=True|False Attributeを読むと、間違いなく役に立ちます。

【編集済】

さて、多対一と別の多対一を考えてみてください。ひとつは同じものです。それが彼らがモデルをリファクタリングすると言った理由です。UserFriend など、別のエンティティを導入する必要があります。ここで、User-to-UserFriend、Friend-to-UserFriend の両方に対して多対 1 を作成します。

したがって、ご覧のとおり、これにより多対多になります。これにより、リファクタリングが明確になることを願っています。実際にパフォーマンスの低下を経験していない限り、これを実行したくない場合があります。Darin が既に述べたように、コメントの 1 つで、時期尚早の最適化を行わないでください。さらに、Donald E. Knuth の悪名高い格言「時期尚早の最適化は諸悪の根源である」を引用したいと思います。

于 2009-01-02T09:05:11.443 に答える
0

Friends コレクションにアクセスしようとすると NHibernate が SELECT ステートメントを発行する理由は、ユーザー エンティティをロードした時点で、コレクションが初期化されていないためです。次のクエリを使用して、コレクションを強制的に読み込むことができます。

User user = session
    .CreateCriteria(typeof(User))
    .SetFetchMode("Friends", FetchMode.Eager)
    .Add(Expression.IdEq(1))
    .UniqueResult<User>();

または、マッピング ファイルに fetch="join" を追加します。

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
  namespace="Test" assembly="test">

  <class name="User" table="Users">
      <id name="Id" column="id">
          <generator class="native"/>
      </id>
      <property name="Name" column="name"/>
      <set name="Friends" table="Friends" fetch="join">
          <key column="user_id"/>
          <many-to-many class="User" column="friend_id"/>
      </set>
  </class>
</hibernate-mapping>

UniqueResult と session.Load には違いがあることに注意してください。データベースでユーザーが見つからない場合、2 番目の方法では ObjectNotFoundException が発生しますが、UniqueResult は null を返します。必要に応じて、どちらのアプローチも使用できます。

于 2009-01-02T09:05:44.500 に答える