我试图从我使用Hibernate的MySQL表中获取所有未删除的对象,但由于表中有这么多行(不出所料),我的应用程序崩溃了。每个类别的查询返回大约280k项。
我可以做些什么来缓解这种情况? Hibernate中是否提供了某种能够应对这种情况的功能?或者您对我如何改变逻辑以避免这种情况有任何想法?
有问题的方法:
public void removeCategory(ItemCategory category)
{
User user = userAuthentication.getLoggedInUser();
Set<Category> deletedCategories = category.orphan();
sessionManager.commit();
for (Category cat : deletedCategories)
{
List<Item> itemsInCategory = itemDAO.getItemsInCategory(category);
reindexer.reindex(cat, ReindexerPriority.HIGH);
reindexer.reindex(itemsInCategory, ReindexerPriority.LOW);
}
}
itemDAO#getItemsInCategory(类别):
public List<Items> getItemsInCategory(final ItemCategory category)
{
// HQL here, not SQL
final Query query = session.createQuery("SELECT item" +
"FROM Item item, ItemCategory c" +
"WHERE asset in elements(c.items)" +
"AND c = :category" +
"AND item.dateDeleted IS null");
query.setEntity("category", category);
return query.list();
}
答案 0 :(得分:1)
您可以通过多种方式对大型结果集进行操作。
如Pace所述,第一种是分批操作。您可以通过在循环中执行相同的查询但指定偏移和限制条件来轻松完成此操作。这通常也被称为使用分页来获取较小的数据片段并在间隔内对较大的数据集进行操作。
所以首先要改变你的方法,让你让它给你一个页面切片而不是所有项目:
public List<Items> getItemsByPage(int page, int pageSize) {
return session.createQuery( "..." )
.setFirstResult( ( page - 1 ) * pageSize )
.setMaxResults( pageSize )
.getResultList();
}
接下来就是在循环中使用它并根据返回的子集列表调用reindexer
:
int page = 1;
for ( List<Items> items = getItemsByPage( page, 100 ); !items.isEmpty(); ++page ) {
reindexer.reindex( items, ReindexerPriority.LOW );
// make sure to clear the session to avoid out of memory with L1C
session.clear();
}
另一种方法是重新实现上述内容并使用ScrollableResults
对象,这将允许您执行单个查询而不是多个查询,而是逐行地将结果集流式传输。
List<Items> batch = new ArrayList<>();
final ScrollableResults results = session.createQuery( ... ).scroll();
while ( results.next() ) {
batch.add( results.get( 0 ) );
if ( ( batch.size() % 100 ) == 0 ) {
reindexer.reindex( batch, ReindexerPriority.LOW );
batch.clear();
session.clear();
}
}
// handle left-overs of < 100 on last batch to process.
if ( !batch.isEmpty() ) {
reindexer.reindex( batch, ReindexerPriority.LOW );
batch.clear();
session.clear();
}
在这两种情况下,重要的是要注意L1C(第一级缓存)。 Hibernate维护所有已加载和附加实体的内存缓存,因此当您从数据库加载数据时,尤其是批量加载数据时,您需要注意此缓存并定期从中删除/清除以避免进入{{1}例外;因此,为什么你会看到我在点上使用OutOfMemory
。