Hibernate: пройдитесь миллионы строк и не пропустите память - PullRequest
6 голосов
/ 18 июня 2010

Код ниже работает, но 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 ]

4 голосов
/ 18 июня 2010

Не используйте здесь сеанс с отслеживанием состояния, это просто НЕ правильный инструмент для обхода миллионов строк и построения отчета. Вместо этого используйте Интерфейс StatelessSession .

Если вы используете MySQL Connector / J даже этого недостаточно, вам также необходимо отключить внутреннюю буферизацию, выполняемую драйвером JDBC, с помощью this :

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();
0 голосов
/ 19 июня 2010

Я думаю, что одной из моих проблем было то, что

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

в сочетании с

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

привело к сбросу базы данных на один запуск слишком рано. Это стало причиной исключений в отношении коллекций. Коллекции не могут быть обработаны в StatelessSession, так что это не обсуждается. Я не знаю, почему session.evict(currentObject) не работает, когда Session.clear() работает, но сейчас мне придется с этим справиться. Я подброшу очки ответа тому, кто сможет это понять.

Итак, пока у нас есть ответ. Требуется окно ручной прокрутки, закрытие ScrollableResults не помогает, и мне нужно правильно запустить Session.clear ().

0 голосов
/ 18 июня 2010

Несколько вещей, которые я бы предложил:

Попробуйте позвонить setCacheMode(CacheMode.IGNORE) в Критерии, прежде чем открывать его.

В методе advanceScroll() добавьте if (sr != null) sr.close();, чтобы предыдущие 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...