Hibernate:走数百万行并且不会泄漏内存

时间:2010-06-18 16:14:47

标签: java performance hibernate orm batch-file

以下代码功能,但Hibernate永远不会放任何对象。调用session.clear()会导致有关获取连接类的异常,并且在检索下一个对象之前调用session.evict(currentObject)也无法释放内存。最终我耗尽了我的堆空间。

检查我的堆转储,StatefulPersistenceContext是指向我的对象的所有引用的垃圾收集器的根目录。

public class CriteriaReportSource implements JRDataSource {

    private ScrollableResults sr;
    private Object currentObject;
    private Criteria c;
    private static final int scrollSize = 10;
    private int offset = 1;

    public CriteriaReportSource(Criteria c) {
        this.c = c;
        advanceScroll();
    }

    private void advanceScroll() {
//        ((Session) Main.em.getDelegate()).clear();
        this.sr = c.setFirstResult(offset)
                   .setMaxResults(scrollSize)
                   .scroll(ScrollMode.FORWARD_ONLY);
        offset += scrollSize;
    }

    public boolean next() {
        if (sr.next()) {
            currentObject = sr.get(0);
            if (sr.isLast()) {
                advanceScroll();
            }
            return true;
        }

        return false;
    }

    public Object getFieldValue(JRField jrf) throws JRException {
        Object retVal = null;
        if(currentObject == null) { return null; }
        try {
            retVal = PropertyUtils.getProperty(currentObject, jrf.getName());
        } catch (Exception ex) {
            Logger.getLogger(CriteriaReportSource.class.getName()).log(Level.SEVERE, null, ex);
        }
        return retVal;
    }
}

3 个答案:

答案 0 :(得分:4)

不要在这里使用有状态会话,它不是行走数百万行并构建报告的正确工具。请改用The StatelessSession interface

如果使用MySQL Connector / J即使这还不够,还需要使用this来阻止JDBC驱动程序完成的内部缓冲:

Query query = session.createQuery(query);
query.setReadOnly(true);
// MIN_VALUE gives hint to JDBC driver to stream results
query.setFetchSize(Integer.MIN_VALUE);
ScrollableResults results = query.scroll(ScrollMode.FORWARD_ONLY);
// iterate over results
while (results.next()) {
    Object row = results.get();
    // process row then release reference
    // you may need to evict() as well
}
results.close();

答案 1 :(得分:0)

我会建议一些事情:

在打开之前尝试在条件上调用setCacheMode(CacheMode.IGNORE)

advanceScroll()方法中,添加if (sr != null) sr.close();,以便在重新分配到新的ScrollableResults之前关闭先前的ScrollableResults。

一个问题:调用setMaxSize(),然后跟踪偏移量然后重新打开可滚动结果的原因是什么,为什么不这样做呢?

public CriteriaReportSource(Criteria c) {
    this.c = c;
    this.sr = c.setCacheMode(CacheMode.IGNORE)
               .scroll(ScrollMode.FORWARD_ONLY);
}


public boolean next() {
    if (sr.next()) {
        currentObject = sr.get(0);
        return true;
    }
    return false;
}

答案 2 :(得分:0)

我认为我的一个问题是

if (sr.isLast()) {
    advanceScroll();
    //...

结合

((Session) Main.em.getDelegate()).clear();
//Also, "Main.em.clear()" should do...

过早地将数据库冲出一次。这是关于收集的例外的原因。无法在StatelessSession中处理集合,因此不在表中。我不知道为什么session.evict(currentObject)Session.clear()确实有效时无效,但这就是我现在必须处理它的方式。我会把答案点扔给谁能想出那个答案。

所以,现在,我们有答案。需要手动滚动窗口,关闭ScrollableResults没有帮助,我需要正确运行Session.clear()。