首先我将解释我是如何理解和使用 @BatchSize
的:
@BatchSize
是为了批量加载对象关系,减少对数据库的SQL请求。这对于 LAZY @OneToMany
关系特别有用。
然而,它甚至对 LAZY @OneToOne
关系和 @ManyToOne
很有用:如果您从数据库加载实体列表并要求加载一个惰性 @*ToOne
实体,即使我只是使用加载列表第一个实体关系的测试,它也会按批处理加载实体。
请注意,如果有些人想要测试:这仅在实体尚未加载时显示:例如,如果您有一个带有管理器的用户列表并列出所有用户,当您访问管理器时,不会触发任何请求因为它已经加载了。
我在该方法中看到的唯一缺点是,如果您从数据库中加载项目列表但仅使用其中的一部分。这是一个后过滤操作。
那么让我们进入重点。
让我们假设我让一切都很好,从不做类似后过滤的操作,即使它让我做原生 SQL 查询或使用 DTO 对象进行多选条件查询等等。
- 在仔细考虑使用预先加载/加入并最终选择惰性关系之后,我认为我可以只
@BatchSize
每个惰性关系是否正确? - 我是否有兴趣为
@BatchSize
寻找足够的值,或者我是否可以认为“越大越好”?这意味着““IN”SQL 运算符中是否存在任何数量限制,可以使我的请求足够慢以至于不再有值(value)?我使用 Postgres,但如果您对其他 SGBD 有答案,我也很感兴趣。 - 可选问题:似乎在类上使用
@BatchSize
不会产生很多结果。我仍然需要注释每一个懒惰的关系,我是否遗漏了什么或者它没有用?
编辑:我的 3 点是我得到了不同的行为。
假设我正在加载类“A”的实体列表,它与 B 具有 LAZY OneToMany 关系。现在我想打印 B 的所有 creationDate。所以我正在做一个经典的 2 for 循环。
我现在用 BatchSize 注释了 B :
- @OneToMany 未使用 BatchSize 注释:每组 B 在每次迭代时独立加载,无需批处理。所以我对 B 类的注释似乎完全被忽略了。即使我将一个值设置为“两个”并且我在一组中有 6 个条目,我对该组有一个查询。
- @OneToMany 注释:我有加载的批处理的特定查询。如果我将批量大小固定为两个,并且我总共有 10 B accros,我只会收到 5 个请求:无论我有多少 A。如果我将它设置为 100:我有 1 个 B 对象查询。
PS:我没有考虑任何与 B 相关的查询,这些查询可能会触发以使用获取选择/子选择加载 B 字段。
编辑 2:我刚刚找到这篇文章 Why would I not use @BatchSize on every lazy loaded relationship?尽管我在发布我的问题之前用谷歌搜索了 SO,但我猜我没有使用正确的词...
但是我添加了一些不同的东西,这可能会导致不同的答案:当我想知道在每个关系上使用 BatchSize 时,它是在选择我是否想要一个急切加载,使用 join/select fetch 或者我想要惰性之后加载中。
最佳答案
- 是的,
@BatchSize
旨在与惰性关联一起使用。 - Hibernate 无论如何都会在大多数情况下执行多个语句,即使未初始化的代理/集合的数量小于指定的批处理大小。参见 this answer更多细节。此外,与不太大的查询相比,更轻的查询可能会对系统的整体吞吐量做出积极贡献。
@BatchSize
在类级别意味着实体的指定批量大小将应用于与该实体的所有@*ToOne
惰性关联。请参阅 documentation 中包含Person
实体的示例.
您提供的链接问题/答案通常更关注优化和延迟加载的需要。它们当然也适用于此,但它们不仅仅与批量加载相关,这只是其中一种可能的方法。
另一个重要的事情与链接答案中提到的预先加载有关,这表明如果始终使用某个属性,那么使用预先加载可能会获得更好的性能。这通常对于集合不正确并且在许多情况下对于一对一关联也是如此。
例如,假设您有以下实体,当 A
时,bs
和 cs
总是 使用被使用。
public class A {
@OneToMany
private Collection<B> bs;
@OneToMany
private Collection<C> cs;
}
急切加载 bs
和 cs
如果您不在单个查询中加入它们,显然会遇到 N+1 选择问题。但是,如果您将它们加入单个查询中,例如:
select a from A
left join fetch a.bs
left join fetch a.cs
然后您在 bs
和 cs
之间创建完整的笛卡尔积并返回 count(a.bs) x count(a. cs)
结果集中的行 for each a
逐一读取并组装成 A
的实体及其bs
和 cs
的集合。
在这种情况下,批量获取是最佳选择,因为您将首先读取 A
s,然后是 bs
,然后是 cs
,结果是更多查询,但从数据库传输的数据总量要少得多。此外,单独的查询比具有连接的大查询简单得多,并且数据库更容易执行和优化。
关于java - @BatchSize 是聪明还是愚蠢的使用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35032559/