Как транслировать результаты из Hibernate / JPA и освобождать ресурсы по мере их обработки? - PullRequest
2 голосов
/ 09 октября 2019

Я использую Spring Data, JPA и Hibernate для выполнения функции над каждой записью, превышающей заданный ID.

Вот мой DAO:

public interface MyEntityDao extends JpaRepository<MyEntity, Long>, {

    @QueryHints(value = @QueryHint(name = org.hibernate.jpa.QueryHints.HINT_FETCH_SIZE, value = "1000"))
    Stream<MyEntity> findByIdGreaterThanOrderByIdAsc(Long id);
}

Метод используетсявот так, и это работает:

@Transactional(readOnly = true)
public void printRecordsGreaterThan(Long lastId) {
    myEntityDao.findByIdGreaterThanOrderByIdAsc(lastId).forEach((entity) -> {
        System.out.println("entity: " entity.getId());
    });
}

Проблема в том, когда этой операции нужно сканировать очень большой диапазон. Я контролировал его с помощью VisualVM, и он хранит все записи в памяти (ОЗУ в десятках гигабайт).

Есть ли способ заставить этот код освобождать ресурсы после их обработки, а не хранить их в памяти?

Заранее спасибо!

Решение

Благодаря @julodnik в комментариях вызывается clear() в менеджере сущностей, который так часто решаетсявопрос.

@PersistenceContext
private EntityManager em;

@Transactional(readOnly = true)
public void printRecordsGreaterThan(Long lastId) {
    AtomicLong counter = new AtomicLong();
    myEntityDao.findByIdGreaterThanOrderByIdAsc(lastId).forEach((entity) -> {
        long count = counter.getAndIncrement();
        if (count % 1000 == 0) {
            logger.info(String.format("Clearing %s session for result %d",  type.toString(), counter.get()));   
            em.clear();
        }
        System.out.println("entity: " entity.getId());
    });
}

1 Ответ

1 голос
/ 09 октября 2019

Вы можете использовать setFirstResult и setMaxResults для перебора большого набора результатов. Вы можете найти пример в этом связанном вопросе .

Еще одна проблема, которая приходит мне в голову, заключается в том, что у вас, возможно, по умолчанию установлена ​​функция eager fetch. Это означает, что вы можете получать все связанные сущности, и если у них есть связанные сущности, они также будут выбраны. Вы должны включить операторы sql в вашем файле журнала, чтобы проверить, происходит ли это.

--- РЕДАКТИРОВАТЬ, чтобы ответить на комментарий

Если на объекты не ссылаются в java, то выможет очистить кэш первого уровня с помощью flush и clear (в entitymanager). Это должно очистить все загруженные объекты.

...