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 命令。

4

2 回答 2

2

来自 Hibernate.org

为什么当我只想添加或删除一个元素时,Hibernate 总是初始化一个集合?

不幸的是,集合 API 定义了只能通过访问数据库来计算的方法返回值。这有三个例外: Hibernate 可以添加到 a ,或者用 inverse="true" 声明而不初始化集合;返回值必须始终为真。

如果您想避免额外的数据库流量(即在性能关键代码中),请重构您的模型以仅使用多对一关联。这几乎总是可能的。然后使用查询代替集合访问。

此外,阅读这篇博文 NHibernate 和 Inverse=True|False Attribute肯定会有所帮助。

[已编辑]

好吧,想想多对一和另一个多对一。一个是一个并且相同的地方。这就是为什么他们说要重构模型。你需要介绍另一个实体,比如 UserFriend 什么的。现在,您将为 User-to-UserFriend、Friend-to-UserFriend 进行多对一操作。

因此,如您所见,这将使其成为多对多。我希望这能让重构变得清晰。你可能不想这样做,除非你真的遇到了糟糕的表现。正如 Darin 已经提到的,在其中一条评论中,不要进行过早的优化。此外,我想引用 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 是有区别的。如果在数据库中找不到用户,您将使用第二种方法获得 ObjectNotFoundException,而 UniqueResult 将返回 null。您可以根据需要使用任何一种方法。

于 2009-01-02T09:05:44.500 回答