问题 如何在nhibernate查询中选择引用的实体


我有一个具有引用其他实体的属性的实体(示例中为ReferenceEntity)。

使用HQL我可以这样做:

select e.ReferenceEntity from Entity e where e.Id = :entityId

NHibernate会给我一个没有懒惰的ReferenceEntity实例。

查询即时尝试执行此操作:

Session.QueryOver<Entity>()
.Where(e => e.Id == entityId)
.Select(e => e.ReferenceEntity)
.SingleOrDefault<ReferenceEntity>()

使用QueryOver Nhibernate给了我ReferenceEntity但是懒惰。

我想使用查询来获得ReferenceEntity和eager加载,就像我使用hql一样。

谢谢


8429
2018-03-21 14:52


起源



答案:


建议#1

在执行查询以获取所需数据后,您可以执行一些LINQ操作。

var result = Session.QueryOver<Entity>()
    .Where(e => e.Id == entityId)        // Filter,
    .Fetch(e => e.ReferenceEntity).Eager // join the desired data into the query,
    .List()                              // execute database query,
    .Select(e => e.ReferenceEntity)      // then grab the desired data in-memory with LINQ.
    .SingleOrDefault();
Console.WriteLine("Name = " + result.Name);

它很简单,可以完成工作。

在我的测试中,它产生了一个查询。这是输出:

SELECT
    this_.Id as Id0_1_, this_.Name as Name0_1_, this_.ReferenceEntity_id as Referenc3_0_1_,
    q5379349_r2_.Id as Id1_0_, q5379349_r2_.Name as Name1_0_
FROM
    [Entity] this_
    left outer join [ReferenceEntity] q5379349_r2_
        on this_.ReferenceEntity_id=q5379349_r2_.Id
WHERE this_.Id = @p0;

建议#2

另一种方法是使用EXISTS子查询,它会稍微复杂一些,但会在第一次返回正确的结果而不需要进行数据库后操作:

ReferenceEntity alias = null;
var result = Session.QueryOver(() => alias)
    .WithSubquery.WhereExists(QueryOver.Of<Entity>()
        .Where(e => e.Id == entityId)                 // Filtered,
        .Where(e => e.ReferenceEntity.Id == alias.Id) // correlated,
        .Select(e => e.Id))                           // and projected (EXISTS requires a projection).
    .SingleOrDefault();
Console.WriteLine("Name = " + result.Name);

经过测试 - 单个查询结果:

SELECT this_.Id as Id1_0_, this_.Name as Name1_0_
FROM [ReferenceEntity] this_
WHERE exists (
    SELECT this_0_.Id as y0_
    FROM [Entity] this_0_
    WHERE this_0_.Id = @p0 and this_0_.ReferenceEntity_id = this_.Id);

10
2018-03-20 03:26



你的第一个建议导致 n + 1选择 和可能的性能问题。第二个可能只产生一个数据库查询,但在此内部您将拥有 WHERE EXISTS ( ... ),这可能会导致相同的不期望的复杂性(取决于RDBMS)。 - Jørn Schou-Rode
我希望有一个 QueryOver 表达式导致简单 SELECT 投射已经加入的所有字段 ReferenceEntity 表。 - Jørn Schou-Rode
编辑包括NHibernate执行的实际查询。这两种方法都只会导致执行一个查询。您可以发布导致选择N + 1的测试吗? - Daniel Schilling
您要定位哪个数据库服务器? SQL Server生成 几乎 两个查询的相同查询计划 - 它显示实际上使用INNER连接的EXISTS查询,因此在某些情况下它实际上可能比其他查询更快。 - Daniel Schilling
你显然是对的。我没有很好地阅读你的第一个查询,并错过了 Fetch().Eager 条款。对于那个很抱歉。非常感谢您使用生成的SQL和Sql Server洞察来记录查询。这是110代表 - 不要一次全部花掉;) - Jørn Schou-Rode


答案:


建议#1

在执行查询以获取所需数据后,您可以执行一些LINQ操作。

var result = Session.QueryOver<Entity>()
    .Where(e => e.Id == entityId)        // Filter,
    .Fetch(e => e.ReferenceEntity).Eager // join the desired data into the query,
    .List()                              // execute database query,
    .Select(e => e.ReferenceEntity)      // then grab the desired data in-memory with LINQ.
    .SingleOrDefault();
Console.WriteLine("Name = " + result.Name);

它很简单,可以完成工作。

在我的测试中,它产生了一个查询。这是输出:

SELECT
    this_.Id as Id0_1_, this_.Name as Name0_1_, this_.ReferenceEntity_id as Referenc3_0_1_,
    q5379349_r2_.Id as Id1_0_, q5379349_r2_.Name as Name1_0_
FROM
    [Entity] this_
    left outer join [ReferenceEntity] q5379349_r2_
        on this_.ReferenceEntity_id=q5379349_r2_.Id
WHERE this_.Id = @p0;

建议#2

另一种方法是使用EXISTS子查询,它会稍微复杂一些,但会在第一次返回正确的结果而不需要进行数据库后操作:

ReferenceEntity alias = null;
var result = Session.QueryOver(() => alias)
    .WithSubquery.WhereExists(QueryOver.Of<Entity>()
        .Where(e => e.Id == entityId)                 // Filtered,
        .Where(e => e.ReferenceEntity.Id == alias.Id) // correlated,
        .Select(e => e.Id))                           // and projected (EXISTS requires a projection).
    .SingleOrDefault();
Console.WriteLine("Name = " + result.Name);

经过测试 - 单个查询结果:

SELECT this_.Id as Id1_0_, this_.Name as Name1_0_
FROM [ReferenceEntity] this_
WHERE exists (
    SELECT this_0_.Id as y0_
    FROM [Entity] this_0_
    WHERE this_0_.Id = @p0 and this_0_.ReferenceEntity_id = this_.Id);

10
2018-03-20 03:26



你的第一个建议导致 n + 1选择 和可能的性能问题。第二个可能只产生一个数据库查询,但在此内部您将拥有 WHERE EXISTS ( ... ),这可能会导致相同的不期望的复杂性(取决于RDBMS)。 - Jørn Schou-Rode
我希望有一个 QueryOver 表达式导致简单 SELECT 投射已经加入的所有字段 ReferenceEntity 表。 - Jørn Schou-Rode
编辑包括NHibernate执行的实际查询。这两种方法都只会导致执行一个查询。您可以发布导致选择N + 1的测试吗? - Daniel Schilling
您要定位哪个数据库服务器? SQL Server生成 几乎 两个查询的相同查询计划 - 它显示实际上使用INNER连接的EXISTS查询,因此在某些情况下它实际上可能比其他查询更快。 - Daniel Schilling
你显然是对的。我没有很好地阅读你的第一个查询,并错过了 Fetch().Eager 条款。对于那个很抱歉。非常感谢您使用生成的SQL和Sql Server洞察来记录查询。这是110代表 - 不要一次全部花掉;) - Jørn Schou-Rode


如果我理解你,这就是你需要的:

Session.QueryOver<Entity>()
 .Where(e => e.Id == entityId)
 //!!!
 .Fetch(e=>e.ReferenceEntity).Eager
 .Select(e => e.ReferenceEntity)
 .SingleOrDefault<ReferenceEntity>()

0
2018-03-21 17:27



你好。我按你说的做了,但它不起作用。 QueryOver返回给我Castle.Proxies.GroupProxy。 NHibernate正在执行此查询:SELECT this_.group_id as y0_ FROM发布this_ WHERE this_.content_id =:p0;:p0 = 72.我想分组渴望。使用Hql我可以获得实体渴望。谢谢! - João Carlos Clementoni


尝试这个:

Session.QueryOver<Entity>()
 .Where(e => e.Id == entityId)
 .Fetch(e=>e.ReferenceEntity).Eager
 .Select(e => e.ReferenceEntity)
 .TransformUsing(Transformers.AliasToBean<ReferenceEntity>())
 .SingleOrDefault<ReferenceEntity>()

0
2018-03-15 14:19



似乎对我不起作用。生成的SQL仅投影引用实体的标识符: SELECT this_.ReferenceEntityId as y0_ FROM Entity this_ WHERE this_.Id = 'a67b894e-bc53-e011-bd88-22a08ed629e5' - Jørn Schou-Rode