Eclipselink:批量阅读会产生大量查询

时间:2017-01-01 13:04:41

标签: java sql jpa eclipselink

我使用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”; 查询query = em.createQuery(queryString); query.setHint(“eclipselink.batch.type”,“IN”); query.setHint(“eclipselink.batch”,“e.authors”); query.setFirstResult(0); query.setMaxResults(10); 列表项= query.getResultList();

  3. 表格中的数据:

    | 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]
    

3 个答案:

答案 0 :(得分:2)

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

  1. 注释@BatchFetch(BatchFetchType.IN)
  2. 过度查询提示query.setHint(QueryHints.BATCH, column); query.setHint(QueryHints.BATCH_TYPE, BatchFetchType.IN);
  3. 在您的情况下,我看到您在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)。

答案 1 :(得分:2)

前2个查询是基于JPQL和1:M上的batchFetch预期的。第三个来自@BatchFetch注释,看起来是正确的,虽然我不太明白为什么你会在基本上是OneToOne上使用batchfetch而不是使用像@JoinFetch这样的东西:我没有看到这样做的好处两个问题。

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

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

答案 2 :(得分:0)

根据@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.

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

此外,您可以查看此SO Question