@BatchSize使用聪明还是愚蠢?

时间:2016-01-27 08:48:15

标签: java hibernate postgresql jpa

首先,我将解释我如何理解和使用@BatchSize@BatchSize用于批量加载对象关系,对数据库的SQL请求较少。这对 LAZY @OneToMany关系特别有用。

然而,它甚至对 LAZY @OneToOne关系和@ManyToOne有用:如果从数据库加载实体列表并要求加载延迟{ {1}}实体,它将按批量加载实体,即使我只是使用加载列表的第一个实体的关系的测试。

注意是否有人想要测试:这只显示实体是否尚未加载:例如,如果你有一个带有管理员的用户列表并列出所有用户,当你将访问管理员时,不会触发任何请求因为它已经装好了。

我在该方法上看到的唯一缺点是,如果从数据库加载项目列表但仅使用其中的一部分。这是一个后过滤操作。

所以让我们谈谈要点。

让我们假设我做的一切都好,从不做类似后期过滤的操作,即使它让我做本机SQL查询或使用DTO对象进行多选标准查询等等。

  1. 在仔细考虑使用急切加载/加入并最终选择懒惰关系后,我是否可以认为我可以@*ToOne每个懒惰的关系?
  2. 我是否有兴趣为@BatchSize寻找足够的价值,或者我认为"越大越好" ?这意味着"" IN"中的数字是否有限制? SQL运算符可以让我的请求足够慢,不再值得了吗?我使用Postgres,但如果你有其他SGBD的答案,我也感兴趣。
  3. 可选问题:似乎在课堂上使用@BatchSize并不会产生大量结果。我仍然需要注释每一个懒惰的关系,我是否想念它或者它没用?
  4. 编辑:我的3点是我得到了不同的行为。

    让我说我正在加载一个类的实体列表" A"与B的LAZY OneToMany关系。现在我要打印B的所有creationDate。所以我做了一个经典的2 for循环。

    我现在用BatchSize注释了B:

    • @OneToMany未使用BatchSize进行批注:每个迭代都会独立加载每组B而不进行批处理。所以我对B类的注释似乎完全被忽略了。即使我将值设置为"两个"我在一组中有6个条目,我对该组有一个查询。
      • @OneToMany已注释:我具有已加载批次的特定查询。如果我将批量大小固定为2并且我总共有10个B accros我只得到5个请求:无论A的数量是多少。如果我将其设置为100:我有1个B对象查询。

    PS:我没有考虑任何可能通过fetch select / subselect触发加载B字段的B相关查询。

    编辑2:我刚刚发现这篇文章Why would I not use @BatchSize on every lazy loaded relationship?,但在我提出问题之前,我用Google搜索并搜索了SO,猜猜我没有使用正确的词......

    但是我添加了一些可能会导致不同答案的不同之处:当我想知道在每个关系中使用BatchSize时,选择我是否想要一个急切的加载,然后加入/ select fetch或者我想要延迟加载。

1 个答案:

答案 0 :(得分:5)

  1. 是的,@BatchSize旨在与懒惰关联一起使用。
  2. 无论如何,Hibernate将在大多数情况下执行多个语句,即使未初始化的代理/集合的数量小于指定的批量大小。有关详细信息,请参阅this answer。此外,与较小的查询相比,更轻的查询可能会对系统的整体吞吐量产生积极影响。
  3. 类级别上的
  4. @BatchSize表示实体的指定批量大小将应用于与该实体的所有@*ToOne惰性关联。请参阅documentationPerson实体的示例。
  5. 您提供的关联问题/答案更关注优化和延迟加载的需求。它们当然也适用于此,但它们与批量加载无关,这只是可能的方法之一。

    另一个重要的事情涉及链接答案中提到的热切加载,这表明如果始终使用属性,那么通过使用预先加载可以获得更好的性能。对于集合而言,这通常是不真实,在许多情况下,对于一个协会也是如此。

    例如,假设您使用bs时使用了csA 始终的实体。

    public class A {
      @OneToMany
      private Collection<B> bs;
    
      @OneToMany
      private Collection<C> cs;
    }
    

    如果你没有在单个查询中加入,那么急切地加载bscs显然会遇到N + 1选择问题。但是如果你在一个查询中加入它们,例如:

    select a from A
      left join fetch a.bs
      left join fetch a.cs
    

    然后在bscs之间创建完整笛卡尔积,并在每个count(a.bs) x count(a.cs)的结果集中返回a,逐一阅读并汇总到A的实体及其bscs的集合中。

    在这种情况下,批量提取会非常理想,因为您首先会先阅读A,然后是bs,然后是cs,这会导致更多查询,但总量会少得多从数据库传输的数据。此外,单独的查询比具有连接的大查询简单得多,并且数据库更容易执行和优化。