如何最好地检索和更新NHibernate中的这些对象?

如何最好地检索和更新NHibernate中的这些对象?,nhibernate,hibernate,orm,Nhibernate,Hibernate,Orm,我之前询问过关于用户、项目和用户评级情况建模的问题。在我的示例中,用户评级与一个用户和一个项目相关。Nathan Fisher给出了一个很好的答案,我在下面介绍了他建议的模型 但我现在有一个关于检索这些对象的问题。 该模型通过保存对实体的引用来链接实体。我的问题是,如何最好地检索要更新的特定用户等级?在这种情况下,我将拥有用户ID(来自asp.net auth会话)、和项目ID(来自URL)。此外,每个用户或项目可能有1000个评级 回到老派,这就像一个更新查询“其中x=userID和y=ite

我之前询问过关于用户、项目和用户评级情况建模的问题。在我的示例中,用户评级与一个用户和一个项目相关。Nathan Fisher给出了一个很好的答案,我在下面介绍了他建议的模型

但我现在有一个关于检索这些对象的问题。

该模型通过保存对实体的引用来链接实体。我的问题是,如何最好地检索要更新的特定用户等级?在这种情况下,我将拥有用户ID(来自asp.net auth会话)、和项目ID(来自URL)。此外,每个用户或项目可能有1000个评级

回到老派,这就像一个更新查询“其中x=userID和y=itemID”一样简单。容易的。然而,在NHibernate中使用适当的对象模型来实现这一点的最佳方法还不太清楚

A)我知道我可以创建一个存储库方法GetRatingByUserAndItem,并向其传递一个User和Item对象,它将对其执行HQL/criteria查询以检索评级对象。但是要执行此操作,我假设首先需要从ORM中检索用户和项目,然后再在查询中将其传递回ORM。然后,我将获取UserRating对象,更新它,然后让ORM保存更改。与传统的教学方法相比,我觉得这种方法效率低得可笑

B)也许我可以新建UserRating对象,然后执行createorupdate类型调用ORM(不确定确切的语法)。这会更好,但我可能仍然需要首先检索用户和项目,这仍然是非常低效的

C)也许我应该从ORM中检索用户(或项目),并从其UserRatings集合中找到正确的UserRating。但是,如果我这样做,如何确保我没有检索到与该用户(或项目)相关的所有UserRatings,但仅仅是与特定项目和特定用户相关的项目

D)我突然想到,我可以在模型中从UserRating中删除对User和Item的完整引用,而是使用原始引用(UserID和ItemID)。这将允许我做一些像旧方法一样简单的事情。非常诱人,但这对我来说似乎不太合适——不太面向对象(这当然是我们首先使用ORM的主要原因!)

那么,有谁能提供一些明智的建议吗?我的上述选择是否正确?还是我没有考虑过更好的方法?

谢谢你的帮助!:)

更新:

我刚刚为此发布了一份赏金,并对此有了更好的理解,我还想知道,使用类似的方法,如何最好地执行以下查询

  • 检索用户未评分的所有项目。
  • 检索用户评分最低的项目和项目评分。

模型如下:

public class User 
{
    public virtual int UserId { get; set; }
    public virtual string UserName { get; set; }
    public virtual IList<UserRating> Ratings { get; set; }
}

public class Item 
{
    public virtual int ItemId { get; set; }
    public virtual string ItemName { get; set; }
    public virtual IList<UserRating> Ratings { get; set; }
}
public class UserRating 
{
    public virtual User User { get; set; }
    public virtual Item Item { get; set; }
    public virtual Int32 Rating { get; set; }
}

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Test" namespace="Test" >
    <class name="User">
        <id name="UserId" >
                <generator class="native" />
        </id>
        <property name="UserName" />
        <bag name="Ratings" generic="true" inverse="true" table="UserRating">
                <key column="UserId" />
                <one-to-many class="UserRating"/>
        </bag>
    </class>
    <class name="Item" >
        <id name="ItemId" >
                <generator class="native" />
        </id>
        <property name="ItemName" />
        <bag name="Ratings" generic="true" inverse="true" table="UserRating">
                <key column="ItemId" />
                <one-to-many class="UserRating"/>
        </bag>
    </class>
    <class name="UserRating" >
        <composite-id>
                <key-many-to-one class="User" column="UserId" name="User" />
                <key-many-to-one class="Item" column="ItemId" name="Item" />
        </composite-id>
        <property name="Rating" />
    </class>
</hibernate-mapping>
公共类用户
{
公共虚拟int用户标识{get;set;}
公共虚拟字符串用户名{get;set;}
公共虚拟IList评级{get;set;}
}
公共类项目
{
公共虚拟int ItemId{get;set;}
公共虚拟字符串ItemName{get;set;}
公共虚拟IList评级{get;set;}
}
公共类用户评级
{
公共虚拟用户用户{get;set;}
公共虚拟项项{get;set;}
公共虚拟Int32评级{get;set;}
}

通常,当您使用ORM时,您希望实现面向对象的业务逻辑(例如:更改数据)。这需要从数据库加载对象。NH允许您加载它们一次,并在不引用任何数据库相关内容的情况下对其进行更改,而只是更改属性值

尽管如此,这并不总是那么容易。有时由于性能原因,需要使用其他方法更新数据

您可以使用SQL更新,甚至可以使用SQL更新

另一种更经典的方法是只加载
UserRatings
。这需要使它成为一个独立的实体(它需要一个id,无论如何都要避免复合id,用多对一引用来替换它)。然后按用户和项目过滤
UserRatings
,在数据库中加载要更改的项目,并使用面向对象编程进行更改

它总是在性能和面向对象编程之间进行权衡。您应该尽可能使其成为OO,并且仅在需要时进行优化。可维护性很重要


我会避免将外键移动到域模型。

通常,当您使用ORM时,您希望实现面向对象的业务逻辑(例如:更改数据)。这需要从数据库加载对象。NH允许您加载它们一次,并在不引用任何数据库相关内容的情况下对其进行更改,而只是更改属性值

尽管如此,这并不总是那么容易。有时由于性能原因,需要使用其他方法更新数据

您可以使用SQL更新,甚至可以使用SQL更新

另一种更经典的方法是只加载
UserRatings
。这需要使它成为一个独立的实体(它需要一个id,无论如何都要避免复合id,用多对一引用来替换它)。然后过滤Select From UserRating Where ItemId=: @ItemId and UserId=: @UserId
Session.CreateCriteria(typeof(ClassLibrary1.UserRating))
                    .Add(Expression.Sql(String.Format("ItemId={0}",UserId)))
                    .Add(Expression.Sql(String.Format("UserId={0}",ItemId)))
                    .List<ClassLibrary1.UserRating>();
public void UpdateRating( int userId, int itemId, int newRating, ISession session )
{
  using( var tx = session.BeginTransaction())
  {
    var ratingCriteria = session.CreateCriteria<UserRating>()
      .CreateAlias( "Item" "item" )
      .CreateAlias( "User" "user" )
      .Add( Restrictions.Eq( "item.ItemId", itemId ) )
      .Add( Restrictions.Eq( "user.UserId", userId ) );
    var userRating = ratingCriteria.UniqueResult<UserRating>();
    userRating.Rating = newRating;
    tx.Commit();
  }
}
var ratedItemsCriteria = DetachedCriteria.For<UserRating>()
    .CreateAlias( "Item" "item" )
    .SetProjection( Projections.Property( "item.ItemId" ) )
      .CreateCriteria( "User" )
        .Add( Restrictions.Eq( "UserId", userId ) );
var unratedItemsCriteria = session.CreateCriteria<Item>()
  .Add( Subqueries.PropertyNotIn( "ItemId", ratedItemsCriteria ) );
var unratedItems - unratedItemsCriteria.List<Item>();
using (ITransaction transaction = session.BeginTransaction())
{
    UserRating userRating = userRatingRepository.GetUserRating(userId, itemId);
    userRating.Rating = 5;
    transaction.Commit();
}
public IList<UserRating> GetUserRating(int userId, int itemId)
{
    session.CreateCriteria(typeof (UserRating))
        .Add(Expression.Eq("UserId", userId))
        .Add(Expression.Eq("ItemId", itemId))
        .List<UserRating>();
}
public class User 
{
    public virtual int UserId { get; set; }
    public virtual string UserName { get; set; }
}
public class Item 
{
    public virtual int ItemId { get; set; }
    public virtual string ItemName { get; set; }
    public virtual IList<UserRating> Ratings { get; set; }
}
public class UserRating 
{
    public virtual int UserId { get; set; }
    public virtual int ItemId { get; set; }
    public virtual Int32 Rating { get; set; }
}