【问题标题】:What exactly is a Cartesian product in NHibernate?NHibernate 中的笛卡尔积到底是什么?
【发布时间】:2013-05-15 18:38:34
【问题描述】:

我经常从我的同事那里听到“笛卡尔积”这个词,因为有一个领域模型在一个集合中包含一个集合并且渴望获取相同的集合。假设我有以下模型。

public class Order{
  List<Product> Products
}
public class Product{
  List<Units> Units
}

现在他们说,如果我尝试用产品和单位获取订单,我应该得到一个笛卡尔产品(又名重复..)。 但是当我通过启用 ShowSql 对其进行测试时,我似乎没有得到任何重复。 这是我的存储库类

    public List<Order> GetOrdersWithProductsAndUnits()
    {
        return GetSession().Query<Order>()
                           .FetchMany(order => order.Products)
                           .ThenFetch(product => product.Units)
                           .ToList();
    } 

这是启用 ShowSql 的 sql 输出。这似乎很好,并且在必要的表上加入了预期。

select order0_.Id as Id8_0_, 
childstock1_.Id as Id23_1_,
childstock2_.Id as Id24_2_, 
order0_.CustomerName as Customer2_8_0_,  
order0_.City as City8_0_,
childstock1_.Size as Size23_1_,
childstock2_.Box as Box24_2_,
from Orders order0_  
left outer join Products childstock1_ on order0_.Id=childstock1_.OrderId  
left outer join Units childstock2_ on childstock1_.Id=childstock2_.ProductId

我的数据库中有 125 个订单,当我急切地获取带有产品和单位的订单时,我仍然得到 125 条记录。

List<Order> orders = repository.GetOrdersWithProductsAndUnits();
   Assert.That(orders.Count,Is.EqualTo(125));

进一步,当我通过 Visual Studio 调试器检查时,我在产品或单元级别的订单集合中也没有发现任何重复项。

由于笛卡尔积导致重复,我希望数据库中的订单数与单位数一样多(接近 1000 个)。我是否遗漏了一些琐碎的事情,我对笛卡尔积的理解是否有偏差?或者我正在使用的查询 API 是否会自动解决笛卡尔积问题。

仅供参考:我也确定数据设置。我已经通过 Visual Studio 调试器进行了调试,我注意到具有多个产品的订单和具有多个单元的产品。

感谢任何帮助/参考材料。

谢谢

【问题讨论】:

  • 您确定按照您所说的方式设置数据吗?订单是否有与之关联的产品?
  • 是的,我确定,我已经通过 Visual Studio 调试窗口进行了调试。每个订单有很多产品,每个产品有很多单位。
  • 好的——另一件事是 NHibernate 正在协调“额外”记录——您是否尝试过直接运行 SQL 并查看结果?另外,我不认为这会导致笛卡尔积。从技术上讲,这将是针对每个产品的每个订单针对每个单元。我认为如果没有交叉连接,这是不可能的,但我可能是错的。
  • 如果我直接运行生成的 SQL 查询,我会得到预期的 1000 条记录。没关系。我期待我的 POCO 对象本身存在重复项

标签: nhibernate fluent-nhibernate


【解决方案1】:

与其他查询类型(HQL、Criteria、QueryOver)相比,NHibernate 会自动丢弃 Linq 查询中的重复根实体。但正如您在上一条评论中所说,您仍然会从数据库中获得那么多行,因此您的查询可能会很慢。

【讨论】:

  • 是的,你是对的。事实证明,NHibernate 的 LINQ API 自动对结果集执行 DistinctRootEntityResultTransformer。因此..我只得到了不同的订单 ID,但是,在这些订单中我确实发现了重复的产品(与单位一样多的产品)。
【解决方案2】:

因此,您在此处遇到的笛卡尔积的问题是,您在 SQL 中返回的行数比您需要的多得多,因为它为每个子对象返回具有完全相同数据的订单列的副本。因此,它实际上不是获得了 125 次订单,而是获得了 1000 次订单,并在幕后“汇总”到您的订单对象中。每次重复的列是:order0_.Id, order0_.CustomerName, City8_0_

显然,您需要重复 Order Id,因为它是 FK 并且存在于远程表中。但是想象一下,如果您的 Order 对象有更多的列,并且您在具有数百万子行的数据集上执行此操作。

使用像 HibernatingRhinos 这样的东西会极大地帮助你。我使用了他们的 EF 工具,并避免了笛卡尔产品的许多问题,我不知道这些问题会在其他情况下发生。 ORM 创建笛卡尔积的罪魁祸首通常是对对象树的急切加载/深度读取。 基于视图的模型对象可以轻松避免这种情况,而不是尝试通过在客户端代码中将对象链接在一起来构建结果集。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-07
    • 2013-08-17
    • 2011-01-29
    相关资源
    最近更新 更多