Eclipselink:批量读取创建大量查询
Eclipselink: batch reading creates a lot of queries
我使用 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();
在数据库中我有 3 篇文章,每篇文章都有两位作者。这些是 eclipse link 执行的查询:
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]
为什么查询这么多?我希望只有两个查询。我的错误是什么?
编辑
我又做了两个测试:
- 我只在文章 class 中对字段作者使用了注释
@BatchFetch(BatchFetchType.IN)
(没有添加查询提示)
我没有使用注释 @BatchFetch(BatchFetchType.IN)
但在查询中使用了两个提示:
字符串查询字符串="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();
table篇数据:
| id | title |
-----------------
| 1 | article1 |
| 2 | article2 |
| 3 | article3 |
table作者中的数据:
| 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]
根据 @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.
所以我猜是因为Cache中没有对象,所以产生了多个Select。您尝试的是 运行 与热缓存相同的查询。
另外根据你也可以看看这个SO Question
我们可以通过两种方式设置批量获取。
- 注释过多
@BatchFetch(BatchFetchType.IN)
- 过度查询提示
query.setHint(QueryHints.BATCH, column);
query.setHint(QueryHints.BATCH_TYPE, BatchFetchType.IN);
在您的情况下,我看到您在 Author
table 中添加了注释,但带有提示的查询是在 Article
table 中完成的。我不知道这背后的全部逻辑,但我建议:
@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 link 使用相同的:
OneToMany: LAZY
ManyToOne: EAGER
ManyToMany: LAZY
OneToOne: EAGER
@OneToMany 必须是 (fetch = FetchType.EAGER),@ManyToOne 必须是 (fetch = FetchType.LAZY)。
前 2 个查询符合预期,基于 1:M 上的 JPQL 和 batchFetch。第三个来自 @BatchFetch 注释并且看起来是正确的,但我不太明白为什么你会在本质上是 OneToOne 的东西上使用 batchfetch 而不是使用类似 @JoinFetch 的东西:我没有看到在两个查询中这样做有什么好处.
这看起来像是 EclipseLink 中的一个错误,当在循环关系中涉及的急切的 OneToOne 类型映射上使用 BatchFetch 时 - BatchFetch 仅适用于对集合类型的查询,并且可能强制读取数据库而不是使用缓存.
选项是:
- 让一侧变得懒惰,这样所有的 Article 实例都将完全
在 EclipseLink 需要构建 Author.article 之前在内存中构建
关系。
- 删除@BatchFetch(BatchFetchType.IN)
多对一关系。使用@FetchJoin 或查询提示来
在其他查询中需要时指定 BatchFetch:这两个选项都不是
此查询需要。
我使用 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();
在数据库中我有 3 篇文章,每篇文章都有两位作者。这些是 eclipse link 执行的查询:
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]
为什么查询这么多?我希望只有两个查询。我的错误是什么?
编辑
我又做了两个测试:
- 我只在文章 class 中对字段作者使用了注释
@BatchFetch(BatchFetchType.IN)
(没有添加查询提示) 我没有使用注释
@BatchFetch(BatchFetchType.IN)
但在查询中使用了两个提示:字符串查询字符串="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();
table篇数据:
| id | title |
-----------------
| 1 | article1 |
| 2 | article2 |
| 3 | article3 |
table作者中的数据:
| 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]
根据 @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.
所以我猜是因为Cache中没有对象,所以产生了多个Select。您尝试的是 运行 与热缓存相同的查询。
另外根据你也可以看看这个SO Question
我们可以通过两种方式设置批量获取。
- 注释过多
@BatchFetch(BatchFetchType.IN)
- 过度查询提示
query.setHint(QueryHints.BATCH, column);
query.setHint(QueryHints.BATCH_TYPE, BatchFetchType.IN);
在您的情况下,我看到您在 Author
table 中添加了注释,但带有提示的查询是在 Article
table 中完成的。我不知道这背后的全部逻辑,但我建议:
@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 link 使用相同的:
OneToMany: LAZY
ManyToOne: EAGER
ManyToMany: LAZY
OneToOne: EAGER
@OneToMany 必须是 (fetch = FetchType.EAGER),@ManyToOne 必须是 (fetch = FetchType.LAZY)。
前 2 个查询符合预期,基于 1:M 上的 JPQL 和 batchFetch。第三个来自 @BatchFetch 注释并且看起来是正确的,但我不太明白为什么你会在本质上是 OneToOne 的东西上使用 batchfetch 而不是使用类似 @JoinFetch 的东西:我没有看到在两个查询中这样做有什么好处.
这看起来像是 EclipseLink 中的一个错误,当在循环关系中涉及的急切的 OneToOne 类型映射上使用 BatchFetch 时 - BatchFetch 仅适用于对集合类型的查询,并且可能强制读取数据库而不是使用缓存. 选项是:
- 让一侧变得懒惰,这样所有的 Article 实例都将完全
在 EclipseLink 需要构建 Author.article 之前在内存中构建 关系。 - 删除@BatchFetch(BatchFetchType.IN) 多对一关系。使用@FetchJoin 或查询提示来 在其他查询中需要时指定 BatchFetch:这两个选项都不是 此查询需要。