【问题标题】:@BatchSize but many round trips when fetching a @ManyToOne association@BatchSize 但在获取 @ManyToOne 关联时需要多次往返
【发布时间】:2015-02-25 14:35:57
【问题描述】:

我使用带有 hibernate spring-data-jpa 和 querydsl 的分页,我使用 @BatchSize(size=10) 只进行一次到数据库的往返。

@Entity
@Table(name = "appel_offre", catalog = "ao")
public class AppelOffre implements java.io.Serializable {

    ....
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "appelOffre")
    @BatchSize(size=10)
    public Set<AoActivite> getAoActivites() {
        return this.aoActivites;
    }

和:

@Entity
@Table(name = "ao_activite", catalog = "ao")
public class AoActivite implements java.io.Serializable {
    .....
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "ID_ACTIVITE", nullable = false)
    @BatchSize(size=10)
    public Activite getActivite() {
        return this.activite;
    }

我的查询

JPAQuery query = new JPAQuery(entityManager).from(ao) 

    .leftJoin( ao.acheteur, ach ).fetch()

    .leftJoin( ao.aoActivites , ao_ac )
    .leftJoin( ao_ac.activite , ac )
    .offset(...).limit(...).list(..);

但在日志中有很多往返数据库:

1 - round-trip

.....
Hibernate: select ... from ao.ao_activite aoactivite0_ where aoactivite0_.ID_APPEL_OFFRE in (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: select ... from ao.activite activite0_ where activite0_.ID_ACTIVITE=?
Hibernate: select ... from ao.activite activite0_ where activite0_.ID_ACTIVITE=?

2 - round-trip

.....
Hibernate: select ... from ao.activite activite0_ where activite0_.ID_ACTIVITE=?
Hibernate: select ... from ao.activite activite0_ where activite0_.ID_ACTIVITE=?
Hibernate: select ... from ao.activite activite0_ where activite0_.ID_ACTIVITE=?

3 - round-trip

.....
Hibernate: select ... from ao.activite activite0_ where activite0_.ID_ACTIVITE=?
Hibernate: select ... from ao.activite activite0_ where activite0_.ID_ACTIVITE=?

4 - round-trip

.....
Hibernate: select ... from ao.activite activite0_ where activite0_.ID_ACTIVITE=?

5 - round-trip

.....

6 - round-trip

.....
Hibernate: select ... from ao.activite activite0_ where activite0_.ID_ACTIVITE=?

7 - round-trip

......

8 - round-trip

.....
Hibernate: select ... from ao.activite activite0_ where activite0_.ID_ACTIVITE=?

9 - round-trip

.....

10 - round-trip

【问题讨论】:

    标签: java spring performance hibernate jpa


    【解决方案1】:
    1. @BatchSize 对 to-many associations (e.g. @OneToMany) 更有意义,而不是一对一关系。

    2. 使用批量提取,您有 (M/N + 1) 次数据库往返,其中 M 是未初始化的多对多关联中子实体的数量,N 是批量大小。

      李>
    3. 如果您想要一个且只有一个辅助数据库往返,您需要使用sub-select fetching。

    4. 对于一对一关联(例如@ManyToOne),使用JPQL or Criteria JOIN FETCH instead。

    【讨论】:

    • 4 - 如果我使用分页,我不能使用join fetch。 3 - sub-select fetching 我认为工作也仅适用于多对关联,例如仅用于收藏。
    • 但是 JPA 查询已经支持 .fetch(),对吧?至于 3。如果您也想获取它们,它是用于集合的。
    • 是的,它可以与 fetch 一起使用,但是如果我们 fetch 集合,则使用分页偏移 () 限制 () 存在内存问题。
    • 这取决于对多集合的大小。这就是为什么我说使用子选择获取,而不是加入。将集合连接提取与分页结合使用时,Hibernate 类似于提取整个集合以确保子关联不会被截断。
    • 我会试试看它是否对我有用。
    【解决方案2】:

    @BatchSize 对两者都有意义

    • One-To-Many 和
    • Many-to-One 也一样

    就Many-To-One而言,我们必须在@Entity级别上应用它(在我们的例子中是Activite类的映射)

    @Entity
    @BatchSize(size=25)
    @Table(name = "activite" ...
    public class Activite implements java.io.Serializable {
    ...
    

    在文档中检查它(下面附有小引用):

    20.1.5. Using batch fetching

    ...

    类/实体的批量获取更容易理解。考虑以下示例:在运行时,您在 Session 中加载了 25 个 Cat 实例,每个 Cat 都有对其所有者的引用,即一个 Person。 Person 类使用代理lazy="true" 进行映射。如果您现在遍历所有猫并在每个猫上调用getOwner(),默认情况下,Hibernate 将执行 25 个 SELECT 语句来检索代理所有者。您可以通过在 Person 的映射中指定批量大小来调整此行为:

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

    ...

    【讨论】:

    • 我会尽快尝试的。
    • 很高兴看到这一点,享受惊人的 Hibernate Youssef ;)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-11
    • 2021-02-12
    • 2015-04-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多