【问题标题】:NHibernate criteria query questionNHibernate 标准查询问题
【发布时间】:2009-11-24 14:09:56
【问题描述】:

我有 3 个相关对象(Entry、GamePlay、Prize),我正在尝试找到使用 NHibernate 查询它们的最佳方法。当收到请求时,我需要查询 Entries 表以获取匹配的条目,如果找到,则获取 a) 最新的游戏以及第一个附有奖品的游戏。 Prize 是 GamePlay 的子对象,每个 Entry 对象都有一个 GamePlays 属性 (IList)。

目前,我正在研究一种方法,该方法可以拉出匹配的条目并急切地加载所有游戏和相关奖品,但加载所有游戏只是为了找到最新的游戏和任何包含奖品的游戏似乎很浪费。

现在,我的查询如下所示:

var entry = session.CreateCriteria<Entry>()
    .Add(Restrictions.Eq("Phone", phone))
    .AddOrder(Order.Desc("Created"))
    .SetFetchMode("GamePlays", FetchMode.Join)
    .SetMaxResults(1).UniqueResult<Entry>();

这有两个问题:

  1. 它会预先加载所有游戏。有了 365 天的数据,每个查询的数据量很容易膨胀到 30 万。
  2. 它不会急切地为每个游戏加载 Prize 子属性。因此,我在 GamePlays 列表中循环查找非空 Prize 的代码必须调用以加载我检查的每个 Prize 属性。

我不是休眠专家,但我知道必须有更好的方法来做到这一点。理想情况下,我想做以下(伪代码):

entry = findEntry(phoneNumber)
lastPlay = getLatestGamePlay(Entry)
firstWinningPlay = getFirstWinningGamePlay(Entry)

最终的结果当然是我有参赛详情,最新的游戏,以及第一个获胜的游戏。问题是我想在尽可能少的数据库调用中执行此操作,否则我只需执行 3 个单独的查询。

对象定义如下:

public class Entry 
{
    public Guid Id {get;set;}
    public string Phone {get;set;}
    public IList<GamePlay> GamePlays {get;set;}
    // ... other properties
}

public class GamePlay 
{
    public Guid Id {get;set;}
    public Entry Entry {get;set;}
    public Prize Prize {get;set;}
    // ... other properties
}

public class Prize
{
    public Guid Id {get;set;}
    // ... other properties
}

正确的 NHibernate 映射已经到位,所以我只需要帮助弄清楚如何设置条件查询(不是寻找 HQL,不要使用它)。

【问题讨论】:

  • HQl 比 Criteria 更强大。 Criteria 适合动态查询,HQL 适合复杂查询。 HQL 还有其他好处,例如您可以将它们存储在预编译的映射文件中。
  • 是的,HQL 更强大,但听起来用户想要坚持直接的 Criteria。我可以挖那个。
  • @Chris:尝试用 SQL 思考。为了以最佳方式获取数据,您会发出哪些查询?要么使用 3 个带有小结果集的查询,要么使用带有大结果集的查询。请记住,NHibernate 只能生成 SQL,它不能以任何其他方式从数据库中获取内容。
  • @Mike:是的,我想坚持使用强类型查询,因此我不喜欢 HQL。 @Mauricio,我意识到我将不得不以任何一种方式执行查询。有没有办法使用 MultiCriteria 进行相关查询(一个依赖于另一个)?我没有找到太多关于它的文档。
  • @Chris:再一次,用 SQL/RDBMS 术语思考。多条件只是发送到数据库的一批查询,数据库返回一批响应。他们不可能是相关的。

标签: nhibernate


【解决方案1】:

由于您在每个请求中都执行此操作,因此在您的实体中设置两个公式属性可能会更好。 第一个应该获取最新的 Gameplay-Id,另一个获取第一个具有非 Null 属性的 Gameplay-Id

Entry的xml映射文件中可能是这样的

<property name="LatestGameplay" formula="select top(1)gp.Id from Gameplay gp where gp.FK_EntryId = PK_EntryId order by gp.InsertDate desc" />

这会在 Entry 实体上留下 Gameplay Id,在您获取它之后,您需要再次往返 DB 以 GetById-fetch the game's

或者,您可以使用过滤器解决问题。 将集合设置回“惰性” 并创建这些漂亮的过滤器

Gameplay latest = NHibernateSession.CreateFilter(entry.GamePlays , "order by InsertDate desc").SetMaxResults(1).SetFirstResult(1).UniqueResult<Gameplay>();

Gameplay winner = NHibernateSession.CreateFilter(entry.GamePlays , "where FK_PrizeId is not null order by InsertDate asc ").SetMaxResults(1).SetFirstResult(1).UniqueResult<Gameplay>();

并且 IFilter 可以在多查询中使用,因此有 2 个 db 命中:一个用于原始 Entry,一个用于多查询。

最后但同样重要的是,您可以在 Entry 实体中定义 2 个包,一个 IList&lt;GamePlay&gt; Latest 和一个 IList&lt;Gameplay&gt; Winner,在 Entry 映射文件中将使用适当的查询进行过滤(尽管我没有现在记住你是否可以在过滤器中定义 TOP 子句)并将它们设置为非惰性。然后通过一次往返,您可以使用以下(丑陋的)语法获得所需的所有数据

Entry entry = findEntry(phoneNumber);
Gameplay winner = entry.Winner[0]; //check this if null first
Gameplay Latest = entry.Latest[0]; //ditto

请注意,在所有解决方案中,第 3 个解决方案提供了一种生成附加查询的机制,因为该包可用于 Criteria/HQL 查询

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多