【问题标题】:Eclipselink: batch reading creates a lot of queriesEclipselink:批量读取创建大量查询
【发布时间】:2017-05-15 21:39:36
【问题描述】:

我使用 eclipselink 2.6.4 并且我有以下实体

@Entity
@Table(name = "articles")
public class Article {

    @Id
    @Column(name = "id")
    private Integer id;

    @Column(name = "title")
    private String title;

    @OneToMany(fetch = FetchType.EAGER,mappedBy = "article")
    @BatchFetch(BatchFetchType.IN)
    private List<Author> authors

    //+ setters and getters
}

@Entity
@Table(name = "authors")
public class Author {

    @Id
    @Column(name = "id")
    private Integer id;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "articleId")
    private Article article;

    @Column(name = "surname")
    private String surname;

    //+setters and getters
}

这是我用来和作者一起阅读所有文章的代码:

String queryString="SELECT e FROM Article e";
Query query = em.createQuery(queryString);
query.setHint("eclipselink.batch.type", "IN");
query.setHint("eclipselink.batch", "e.authors");
query.setFirstResult(position);
query.setMaxResults(amount);
List<Article> items=query.getResultList();

在 DB 中,我有 3 篇文章,每篇文章都有两位作者。这些是 eclipse 链接执行的查询:

SELECT id AS a1, title AS a2 FROM articles LIMIT ? OFFSET ? bind => [2 parameters bound]
SELECT id, surname, articleId FROM authors WHERE (articleId IN (?,?,?)) bind => [3 parameters bound]
SELECT id, title FROM articles WHERE (id IN (?,?)) bind => [2 parameters bound]
SELECT id, surname, articleId FROM authors WHERE (articleId = ?) bind => [1 parameter bound]
SELECT id, surname, articleId FROM authors WHERE (articleId = ?) bind => [1 parameter bound]

为什么有这么多查询?我希望只有两个查询。我的错误是什么?

编辑
我又做了两个测试:

  1. 我只在字段作者的文章类中使用注释@BatchFetch(BatchFetchType.IN)(未添加查询提示)
  2. 我没有使用注解@BatchFetch(BatchFetchType.IN),而是在查询中使用了两个提示:

    String queryString="SELECT e FROM Article e"; 查询查询 = em.createQuery(queryString); query.setHint("eclipselink.batch.type", "IN"); query.setHint("eclipselink.batch", "e.authors"); query.setFirstResult(0); query.setMaxResults(10); 列表项=query.getResultList();

表格文章中的数据:

| id | title    |
-----------------
| 1  | article1 |
| 2  | article2 |
| 3  | article3 |

表作者中的数据:

| id | articleId |  surname  |
------------------------------
| 1  |  1        |  Author1  |
| 2  |  1        |  Author2  |
| 3  |  2        |  Author3  |
| 4  |  2        |  Author4  |
| 5  |  3        |  Author5  |
| 6  |  3        |  Author6  |

在每个测试中执行 6 个查询:

SELECT id AS a1, title AS a2 FROM articles LIMIT ? OFFSET ? bind => [2 parameters bound]
SELECT id, surname, articleId FROM authors WHERE (articleId IN (?,?,?)) bind => [3 parameters bound]
SELECT id, title FROM articles WHERE (id = ?) bind => [1 parameter bound]
SELECT id, surname, articleId FROM authors WHERE (articleId = ?) bind => [1 parameter bound]
SELECT id, title FROM articles WHERE (id = ?) bind => [1 parameter bound]
SELECT id, surname, articleId FROM authors WHERE (articleId = ?) bind => [1 parameter bound]

【问题讨论】:

    标签: java sql jpa eclipselink


    【解决方案1】:

    我们可以通过两种方式设置批量获取。

    1. 过注@BatchFetch(BatchFetchType.IN)
    2. 过度查询提示query.setHint(QueryHints.BATCH, column);query.setHint(QueryHints.BATCH_TYPE, BatchFetchType.IN);

    在您的情况下,我看到您在Author 表中添加了注释,但在Article 表上完成了带有提示的查询。我不知道这背后的整个逻辑,但我建议:

    @Entity
    @Table(name = "articles")
    public class Article {
    
        @Id
        @Column(name = "id")
        private Integer id;
    
        @Column(name = "title")
        private String title;
    
        @OneToMany(mappedBy = "article", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
        @BatchFetch(BatchFetchType.IN)
        private List<Author> authors
    
        //+ setters and getters
    } 
    
    @Entity
    @Table(name = "authors")
    public class Author {
    
        @Id
        @Column(name = "id")
        private Integer id;
    
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "articleId")
        private Article article;
    
        @Column(name = "surname")
        private String surname;
    
        //+setters and getters
    }
    

    不要使用该注解,只使用带有提示的查询:

    String queryString="SELECT e FROM Article e";
    Query query = em.createQuery(queryString);
    query.setHint("eclipselink.batch.type", "IN");
    query.setHint("eclipselink.batch", "e.authors");
    query.setFirstResult(position);
    query.setMaxResults(amount);
    List<Article> items=query.getResultList();
    

    还有一点: 从 JPA 2.0 规范来看,默认值如下:

    OneToMany: LAZY
    ManyToOne: EAGER
    ManyToMany: LAZY
    OneToOne: EAGER
    

    Eclipse 链接使用相同:

    OneToMany: LAZY
    ManyToOne: EAGER
    ManyToMany: LAZY
    OneToOne: EAGER
    

    @OneToMany 必须是 (fetch = FetchType.EAGER),@ManyToOne 必须是 (fetch = FetchType.LAZY)。

    【讨论】:

    • 更多关于@BatchFetch vs @JoinFetchjava-persistence-performance.blogspot.co.uk/2010/08/…和查询优化vard-lokkur.blogspot.co.uk/2011/05/…的信息
    • 我尝试了两个测试:1)我只对作者使用了注释@BatchFetch(BatchFetchType.IN) 2)我只使用了提示。结果是一样的 - 6 个查询。
    • 请您用最新的更改和结果更新您的问题。现在很难说发生了什么。
    • FetchType.EAGER 可以删除或更改为LAZYLAZY = 需要时获取 EAGER = 立即获取)
    • 问题解决了。 @OneToMany 必须是 (fetch = FetchType.EAGER)@ManyToOne 必须是 (fetch = FetchType.LAZY)。这种情况下我只有两个查询,尽管 Author 类中的文章不为空。把它作为答案,我会接受你给我的提示。
    【解决方案2】:

    前 2 个查询符合预期,基于 JPQL 和 1:M 上的 batchFetch。第三个来自@BatchFetch 注释并且看起来是正确的,尽管我不太明白为什么你会在本质上是 OneToOne 的东西上使用批处理而不是使用像 @JoinFetch 之类的东西:我认为在两个查询中这样做没有多大好处.

    这看起来像 EclipseLink 中的一个错误,当在循环关系中涉及的急切的 OneToOne 类型映射上使用 BatchFetch 时 - BatchFetch 仅用于对集合类型的查询,并且可能会强制读取数据库而不是使用缓存。 选项是:

    1. 使一侧变得懒惰,以便所有文章实例都将完全
      在 EclipseLink 需要构建之前在内存中构建 Author.article 关系。
    2. 删除@BatchFetch(BatchFetchType.IN) 多对一关系。使用@FetchJoin 或查询提示 在其他查询中需要时指定 BatchFetch:这两个选项都不是 此查询需要。

    【讨论】:

    • 嗨。由于分页,我使用批处理。看到我的问题stackoverflow.com/questions/41414874/…,你就会回答。
    • 我的回答是不要对集合使用 fetch 连接。联接适用于 OneToOne 和 ManyToOne 关系,因为它们不会更改返回的行数。批量获取在 1:1 上可能很有用,但在这里可能不是您的最佳选择。
    • 此外,无论如何,如果有两个选项 - join-fetch 和 batch,我希望能够同时使用它们。
    • 您误读了我的答案,因为我建议了您在接受的答案中制定的解决方案。问题是由于循环关系和你渴望获取一切;您的设置导致 EL 开始构建从 A1 开始的文章,然后是所有作者(B1、B2..)。在构建B2的过程中,需要构建尚未构建的A2,强制查询。将其中一个关系切换为惰性可以解决此问题,因为所有对象都将在需要时位于缓存中 - 这就是您所采用的
    • 另一个选项是在 1:1 关系上使用 fetch-join。这也是可行的,但会强制在查询中进行不必要的表连接以引入作者。这将导致 Authors 被构建,并且当它需要构建尚未构建的 Articles 时,该信息将已包含在该行中,因此不需要额外的查询。如前所述,这些数据已经存在,只是 1:1 批处理机制没有寻找它 - 应该作为错误提交。
    【解决方案3】:

    根据@BatchFetch 的文档

    When using BatchFetchType=IN, EclipseLink selects only objects not already in the cache. This method may work better with cursors or pagination, or in situations in which you cannot use a JOIN. On some databases, this may only work for singleton IDs.
    

    所以我猜是因为缓存中没有对象,所以会生成多个 Select。您尝试使用 Hot Cache 运行相同的查询。

    另外,你也可以看看这个SO Question

    【讨论】:

    • 没有逻辑,因为在第二次查询之后这些对象已经在缓存中了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-02
    • 2015-03-20
    • 1970-01-01
    • 2021-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多