【问题标题】:Child collection and one to one儿童收藏和一对一
【发布时间】:2014-01-15 17:51:17
【问题描述】:

我的实体“TimeRecord”有一个集合“WayPoints”和两个一对一的属性“Location”和“WayData”。

每个属性都可以为空。

我需要为特定用户导出所有具有初始化属性的时间记录。

我实际上有一个可行的解决方案,但后来我开始使用NHibernateProiler,首先我注意到这段代码导致对数据库的查询数量荒谬。

var query = (from timeRecord in Session.Query<TimeRecord>()
                .Where(tr => tr.User.Id == userid)
            select timeRecord);

然后我将代码更改为:

var query = (from post in Session.Query<TimeRecord>()
                .Fetch(x => x.Location)
                .Fetch(x => x.WayData)
                .FetchMany(x => x.WayPoints)
                .Where(tr => tr.User.Id == userid)
            select post);

这导致我遇到笛卡尔积问题。

现在我正在试验这段代码:

 var sql1 = "from TimeRecord b left outer join fetch b.Location where b.User.Id=:User_id";
 var sql2 = "from TimeRecord b left outer join fetch b.WayData where b.User.Id=:User_id";
 var sql3 = "from TimeRecord b left inner join fetch b.WayPoints where b.User.Id=:User_id";


var result = Session.CreateMultiQuery()
             .Add(Session.CreateQuery(sql1))
             .Add(Session.CreateQuery(sql2))
             .Add(Session.CreateQuery(sql3))
             .SetParameter("User_id", userid)
             .List();

但我不能说这是否是正确的方法,或者这是否可以通过 nHibernate 实现。有人可以帮我吗?

【问题讨论】:

  • 笛卡尔积问题?查询是否产生重复的帖子,或者您的意思是它产生的 SQL 的结果集中有 #TimeRecords x #WayPoints?后者应该是正常的。尽管有FetchMany,但我认为NHibernate 会返回不同的结果。
  • 它的工作方式如下所述:nhforge.org/blogs/nhibernate/archive/2008/09/06/… -> 请注意,您有三个连接,一个内连接和两个外连接。 NHibernate 返回一组记录,其中包含所有三个连接的叉积。也就是说:如果一个博客有一个作者、10 个帖子和 100 个读者,那么结果集将有 1*10*100 = 1000 条记录。如果您有 100 个帖子和 1000 个读者,您将获得一组包含 100,000 条记录的集合!
  • 重要的是:将返回多少条记录。我同意Fetch(Many) 会炸毁 SQL 结果集,但 AFAIK(我更多地使用实体框架)只会返回带有加载引用和集合的唯一帖子。当然,您可以寻找使其在引擎盖下更高效的方法,例如与期货:stackoverflow.com/a/5225939/861716.
  • 我同意 EF 会更好。我总是选错一个 :( 上次我使用它时,我有很多 N:N 关系,当时 EF 并没有真正支持,所以我决定下次使用 nHibernate 作为持久层。
  • 不,EF 并不好。 NHibernate 批处理查询的能力非常独特。 EF 的 Include 语句同样会破坏 SQL 结果集。

标签: linq nhibernate fluent-nhibernate linq-to-nhibernate nhibernate-criteria


【解决方案1】:

1+N 问题在实体/集合映射和 ORM 工具中很常见。但是 NHibernate 有一个很好的解决方案来正确管理它。它被称为:

此设置将允许:

  • 继续查询根实体(在我们的例子中为TimeRecord
  • 查询内部没有获取 (Session.Query&lt;TimeRecord&gt;())。这意味着我们确实支持正确的分页。 Take()Skip() 将在 flat 根表上执行)
  • 所有集合都将加载它们自己的 SELECT 语句(可能看起来是缺点,但在下面)
  • 会有 much 更多 less SELECTs 然后 1+N。所有这些都将被批量处理。例如。 25 条记录
  • 所有本机映射(集合的延迟加载)仍将保留...

xml映射示例:

-- class level
<class name="Location" batch-size="25 ...

-- collection level
<batch name="Locations" batch-size="25" ...

我建议将其应用于您的所有集合/类。 使用 Fluent 映射也可以使用约定来完成

流畅的映射:

// class
public LocationMap()
{
    Id(x => x....
    ...
    BatchSize(25);

// collection
HasMany(x => x.Locations)
  ...
  .BatchSize(25);

【讨论】:

  • 我还在调查(学习)...但是正确的查询难道不比更改映射层更好吗?就我调查的问题而言,子选择应该为我完成这项工作……
  • 这种方法,让我们在这里说一下,完全适用于这些场景。事实上,它确实进行了子选择。确实如此。但开箱即用......只需设置BatchSize。我的经验是:到处都有它……不关心延迟加载……它会很有效;)在映射期间执行一次,在查询期间始终获取;)
  • 所以拥有它并没有什么坏处:P 非常感谢,但我想尝试使用子选择和 DetachedCriteria,因为我想学习如何进行如此复杂的加载...
  • 我不确定我是否正确地表达了所有这些。无论如何:在这里你可以得到一些关于如何做复杂子查询的灵感:stackoverflow.com/a/20537260/1679310。但是对于加载根实体及其集合...我总是使用 BatchSize()。无论如何,祝 NHibernate 好运 ;)
  • 我有一个 android 应用程序,它允许用户跟踪他的方式并将其附加到时间记录。可以有很多航点50公里的方式是〜1000航点。如果用户希望将他的数据保存在另一台设备上,则 android 应用程序会连接到 WCF 服务并需要特定用户的所有数据。所以我的要求很简单:我只希望我的对象图 TimeRecord -> WayPoints 被加载并发送到设备。这里开始我的加载策略问题。当我什么都不做,只是默认实现时,NHibernate 会为每个 WayPoint 集合执行延迟加载...
猜你喜欢
  • 1970-01-01
  • 2014-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多