结果集中的 NHibernate Fetch/FetchMany 重复,如何使用 ToFuture() 修复 [英] NHibernate Fetch/FetchMany duplication in resultset, how to fix with ToFuture()

查看:13
本文介绍了结果集中的 NHibernate Fetch/FetchMany 重复,如何使用 ToFuture() 修复的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对使用 NHibernate 比较陌生,并且遇到了一个我似乎无法解决的缺点.我有一个对象树,我希望在一次往返中从数据库中检索它,但最终得到的是笛卡尔积.

我尝试检索的对象称为 'AccountGroup''Concern''Advertiser''Product' 并且我只希望获得活动用户有权访问的那些对象.

我的初始查询如下所示:

using (var session = OpenSession()){返回 session.Query().FetchMany(a => a.Planners).Where(a => a.Planners.Any(p => p.Id == userId)).FetchMany(a => a.关注点).ThenFetchMany(c => c.Advertisers).ThenFetch(a => a.Products).ToList();}

这将不起作用,因为它将返回笛卡尔积,并且生成的实体将包含许多重复项.

但是,我不知道如何解决这个问题.我已经看到了 ToFuture() 方法,它允许我在同一次往返中执行多个查询,但我不知道如何配置我的 ToFuture()以正确填充所有子集合的方式进行查询.

谁能说明我如何使用 ToFuture 在单个查询中获取整个树而没有重复?

解决方案

我确实有这个主题的答案,我使用的解决方案.但它最后的意思是不要使用 Fetch"——用不同的方式来做.所以,请至少把它当作一个建议.

检查这个 Q &答:

如何在 NHibernate 中快速加载关联而不重复?

小引用:

<块引用>

获取集合是一项困难的操作.它有很多副作用(正如您所意识到的,当获取更多集合时).但即使获取一个集合,我们也会加载许多重复的行.

换句话说,Fetching 是一个脆弱的特性,应该在极少数情况下明智地使用,我会说.那么用什么?怎么解决?

从内置的 NHibernate 功能中获益:

19.1.5.使用批量提取

<块引用>

NHibernate 可以有效地利用批量抓取,即如果访问一个代理(或集合),NHibernate 可以加载多个未初始化的代理.批量抓取是对惰性选择抓取策略的优化.有两种方法可以调整批量获取:在类和集合级别.

类/实体的批量获取更容易理解.想象一下,您在运行时有以下情况:您在 ISession 中加载了 25 个 Cat 实例,每个 Cat 都引用了它的 Owner,一个 Person.Person 类使用代理来映射,lazy="true".如果您现在遍历所有猫并在每只猫上调用 cat.Owner,NHibernate 将默认执行 25 条 SELECT 语句,以检索代理所有者.您可以通过在 Person 的映射中指定批量大小来调整此行为:

...

<块引用>

NHibernate 现在只会执行三个查询,模式是 10、10、5.

您还可以启用批量提取集合.例如,如果每个 Person 都有一个 Cats 的惰性集合,并且当前在 ISSession 中加载了 10 个人,则遍历所有人将生成 10 个 SELECT,每次调用 person.Cats 一个.如果在 Person 的映射中为 Cats 集合启用批量抓取,NHibernate 可以预抓取集合:

<set name="Cats" batch-size="3">...</set>

根据我的经验,这种方法是无价的.对我们有用的设置是 batch-size="25".

如果您要求任何类型的实体 (通过 session.Get() 或 .QueryOver()...) - 直到会话打开,我们第一次接触相关的引用或集合 -它分几批加载...没有 1 + N SELECT 问题...

总结:用 batch-size="x" (x 可以是 25) 标记你的所有类和所有集合.这将支持对根实体的干净查询 - 在会话打开之前,所有相关内容都加载在几个 SELECTS 中.x 可以调整,有些可能会更高...

I'm relatively new to using NHibernate and I'm running into a shortcoming I can't seem to work myself around. I have an object tree that I wish to retrieve from the database in a single roundtrip but end up with a cartesian product.

The objects I'm trying to retrieve are called 'AccountGroup', 'Concern', 'Advertiser' and 'Product' and I only wish to get those objects where the active user has permissions for.

My initial query looked like this:

using (var session = OpenSession())
{
    return session.Query<AccountGroupEntity>()
        .FetchMany(a => a.Planners)
        .Where(a => a.Planners.Any(p => p.Id == userId))
        .FetchMany(a => a.Concerns)
        .ThenFetchMany(c => c.Advertisers)
        .ThenFetch(a => a.Products)
        .ToList();
}

This won't work as it will return a cartesian product and the resulting entities will contain many duplicates.

However, I have NO idea how to fix this. I've seen the ToFuture() method that will allow me to execute more than one query in the same roundtrip, but I have no clue how to configure my ToFuture() query in such a way that it populates all the child collections properly.

Could anyone shine some light on how I can use ToFuture to fetch the entire tree in a single query without duplicates?

解决方案

I do have an answer to this topic, solution which I do use. But it at the end means "do not use Fetch" - do it differently. So, please, take it at least as a suggestion.

Check this Q & A:

How to Eager Load Associations without duplication in NHibernate?

Small cite:

Fetching Collections is a difficult operation. It has many side effects (as you realized, when there are fetched more collections). But even with fetching one collection, we are loading many duplicated rows.

Other words, Fetching is a fragil feature, and should be used wisely in very few scenarios, I'd say. So what to use? How to solve that?

Profit from a built in NHibernate feature:

19.1.5. Using batch fetching

NHibernate can make efficient use of batch fetching, that is, NHibernate can load several uninitialized proxies if one proxy is accessed (or collections. Batch fetching is an optimization of the lazy select fetching strategy. There are two ways you can tune batch fetching: on the class and the collection level.

Batch fetching for classes/entities is easier to understand. Imagine you have the following situation at runtime: You have 25 Cat instances loaded in an ISession, each Cat has a reference to its Owner, a Person. The Person class is mapped with a proxy, lazy="true". If you now iterate through all cats and call cat.Owner on each, NHibernate will by default execute 25 SELECT statements, to retrieve the proxied owners. You can tune this behavior by specifying a batch-size in the mapping of Person:

<class name="Person" batch-size="10">...</class>

NHibernate will now execute only three queries, the pattern is 10, 10, 5.

You may also enable batch fetching of collections. For example, if each Person has a lazy collection of Cats, and 10 persons are currently loaded in the ISesssion, iterating through all persons will generate 10 SELECTs, one for every call to person.Cats. If you enable batch fetching for the Cats collection in the mapping of Person, NHibernate can pre-fetch collections:

<class name="Person">
    <set name="Cats" batch-size="3">
        ...
    </set>

My experience, this approach is pricless. The setting working for us is batch-size="25".

If you ask for any kind of Entity (via session.Get() or .QueryOver()...) - until session is open, the first time we touch related reference or collection - it is loaded in few batches... No 1 + N SELECT Issue...

Summary: Mark all your classes, and all collection with batch-size="x" (x could be 25). That will support clean queries over root Entities - until session is open, all related stuff is loaded in few SELECTS. The x could be adjusted, for some could be much more higher...

这篇关于结果集中的 NHibernate Fetch/FetchMany 重复,如何使用 ToFuture() 修复的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆