2014-04-03 31 views
2

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

大气压我使用的是标准的API来创建搜索查询和选择的实体。我用EAGER注释了所有集合。当我检查日志文件时,它会创建5个查询来获取所有的孩子。这是我想要的方式。 问题在于我必须经过1000次匹配实体之后的选择和停止后才能在java中进行大量筛选。所以我想我指定了读取大小,并立即停止从数据库读取实体,只要我有我的1k结果。

如果我介绍FetchBatchSize设置,OpenJPA的每个实体加载儿童创造单查询。 (N + 1个问题)

我也试图直接在我的查询使用fetch联接语法,但没有成功。那么我做错了什么?

我尝试:

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 

感谢您的帮助!

+0

你使用JPA注释的实体吗?如果是这样,你可以添加相关实体吗? – thobens

+0

是的,我愿意。在一分钟之内发布它。 – mkuff

+0

我展示了一个关系的例子,当我玩弄批量大小时,它不再渴望加载。 – mkuff

回答

0

似乎有申请了一些错误适用于我的情况。我发现了一个很好的解决方法。

首先,我只选择ID(Criteria API可以选择标量值),然后在那里应用批处理。所以我没有n + 1问题,因为错误的提取策略了。

在此之后,我使用IN()语句以批量为1000的方式选择我的实体,而不限制获取批量大小或最大结果。所以我不会遇到这个bug,并且OpenJPA为每个关系生成一个查询。

所以我有大约6个查询实体与所有的依赖关系。

再次感谢thobens的帮助!

0

看一看how to deal with large result sets,你会看到,EAGER是你应该做的是相反的。

正如我在评论中所述,EAGER意味着JPA一次加载所有结果,因此不推荐用于大型结果集。设置fetchBatchSize会导致JPA延迟加载每个x(在您的情况下为1000)结果。因此,这将是几乎一样的,如果你会使用@OneToMany(fetch = FetchType.LAZY, ...)(也值得一试)

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

也可以尝试

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

好吧,这是有道理的。我的问题是我仍然需要孩子。我必须手动选择它们吗?敏感的滚动类型将如何帮助我?非常感谢! – mkuff

+0

不,JPA会处理这个问题(如果您正确地指定了查询,将其发布在此处会很有帮助)。我假设SCROLL_SENSITIVE允许你优化延迟重载实体,或者它可以被省略。你真的尝试过吗? – thobens

+0

已经失去工作。我用我的查询更新了这篇文章。我的问题是,此搜索功能必须获取所有的孩子。所以懒加载并不是真正的选择。序列化元素时,我会产生太多的查询。我也会用不同的结果集类型来测试抓取。也许你在我的查询中发现了一个影响抓取的错误。 – mkuff