【问题标题】:OpenJPA Eager FetchingOpenJPA 渴望获取
【发布时间】:2014-05-15 12:06:59
【问题描述】:

我使用与 WebSphere 8.5 捆绑在一起的 OpenJPA 2.3,我必须从表中读取大量数据。我还必须获取与根实体的很多关系。

Atm 我正在使用标准 API 创建搜索查询并选择实体。我用 EAGER 注释了所有集合。当我检查日志文件时,它会创建 5 个查询来获取所有子项。这就是我想要的方式。 问题是我必须在选择后在 java 中过滤很多,并在 1000 个匹配实体后停止。所以我想我指定了 fetch 大小并在我得到 1k 结果后停止从数据库中读取实体。

如果我引入 FetchBatchSize 设置,OpenJPA 会为每个实体创建单个查询来加载子实体。 (n+1 个问题)

我还尝试在查询中直接使用 fetch join 语法,但没有任何成功。那我做错了什么?

我试过了:

1)

    query.setHint("openjpa.FetchPlan.FetchBatchSize", 1000);
    query.setHint("openjpa.FetchPlan.ResultSetType", "SCROLL_INSENSITIVE"); 

2)

        OpenJPAQuery<?> kq = OpenJPAPersistence.cast(query);
        JDBCFetchPlan fetch = (JDBCFetchPlan) kq.getFetchPlan();
        fetch.setFetchBatchSize(1000);
        fetch.setResultSetType(ResultSetType.FORWARD_ONLY);
        fetch.setFetchDirection(FetchDirection.FORWARD);
        fetch.setLRSSizeAlgorithm(LRSSizeAlgorithm.UNKNOWN);

实体:

@Entity
@Table(name = "CONTRACT")
public class Contract {

// omitted the other properties. The other relationships are annotated the same way
    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "contract")
    private List<Vehicle> vehicles= new ArrayList<Vehicle>();

查询:

        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Contract> crit = cb.createQuery(Contract.class);
        crit.distinct(true);
        Root<Contract> r = crit.from(Contract.class);

        // omited the where clause. In worst case I have a full table scan without any where clause. (the reason I need the batch size)

        Fetch<Contract, Vehicle> fetchVehicles = r.fetch("vehicles", JoinType.LEFT); // I tried to work with a fetch join as well

                TypedQuery<Contract> query = em.createQuery(crit);

//      query.setHint("openjpa.FetchPlan.FetchBatchSize", FETCH_SIZE);
//      query.setHint("openjpa.FetchPlan.ResultSetType", "SCROLL_INSENSITIVE"); 

        OpenJPAQuery<?> kq = OpenJPAPersistence.cast(query);
        JDBCFetchPlan fetch = (JDBCFetchPlan) kq.getFetchPlan();
        fetch.setFetchBatchSize(FETCH_SIZE);
        fetch.setResultSetType(ResultSetType.FORWARD_ONLY);
        fetch.setFetchDirection(FetchDirection.FORWARD);
        fetch.setLRSSizeAlgorithm(LRSSizeAlgorithm.UNKNOWN);
        fetch.setEagerFetchMode(FetchMode.PARALLEL);

        List<TPV> queryResult = query.getResultList();

        // here begins the filtering and I stop as soon I have 1000 results

感谢您的帮助!

【问题讨论】:

  • 您使用 JPA 注释实体吗?如果有,能否请您添加相关实体?
  • 是的,我愿意。一分钟后将其发布到编辑中。
  • 我展示了一个关系示例,当我使用批量大小时,它不再急切加载。
  • 当然可以,因为EAGER 意味着它会一次加载所有结果。 (无论如何这对于巨大的结果集都是不利的)。设置 fetchBatchSize 会导致 JPA 延迟加载每个 x(在您的情况下为 1000)结果。所以它实际上与使用@OneToMany(fetch = FetchType.LAZY, ...) 相同

标签: java jpa-2.0 openjpa


【解决方案1】:

查看how to deal with large result sets,您会发现 EAGER 与您应该做的相反。

正如我在 cmets 中所说,EAGER 表示 JPA 一次加载所有结果,因此不建议用于大型结果集。设置 fetchBatchSize 会导致 JPA 延迟加载每个 x(在您的情况下为 1000)结果。所以它实际上与使用@OneToMany(fetch = FetchType.LAZY, ...) 相同(也值得一试)

fetchBatch 的大小设置为低得多的数字(例如 50)也会降低保存在内存中的对象。

也试试

query.setHint("openjpa.FetchPlan.ResultSetType", "SCROLL_SENSITIVE"); 

【讨论】:

  • 这是有道理的。我的问题是我仍然需要孩子。我必须手动选择它们吗?敏感滚动类型对我有什么帮助?非常感谢!
  • 不,JPA 会处理这个问题(如果您正确指定了查询,在此处发布它会很有帮助)。我假设 SCROLL_SENSITIVE 允许您优化延迟重新加载实体,或者可以省略它。你真的试过了吗?
  • 已经失业了。我用我的查询更新了帖子。我的问题是这个搜索功能必须获取所有孩子。所以延迟加载并不是一个真正的选择。序列化元素时会生成太多查询。我也会用不同的结果集类型测试获取。也许您在我的查询中发现了一个影响抓取的错误。
  • 也许我遇到了这个错误:issues.apache.org/jira/browse/OPENJPA-2299 我需要获取多个集合。
【解决方案2】:

似乎有一些错误适用于我的场景。我找到了一种可以很好地扩展的解决方法。

首先,我只选择 id(Criteria API 可以选择 skalar 值),然后在那里应用批处理。因此,由于错误的获取策略,我不再有 n+1 问题。

在此之后,我以 1000 个批次选择带有 IN() 语句的实体,而不限制获取批次大小或最大结果。所以我没有遇到这个错误,OpenJPA 为每个关系生成一个查询。

所以我对实体及其所有依赖项有大约 6 个查询。

再次感谢 thobens 的帮助!

【讨论】:

    猜你喜欢
    • 2011-04-28
    • 1970-01-01
    • 1970-01-01
    • 2015-03-10
    • 2023-03-09
    • 2020-01-20
    • 2019-02-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多