Eu estou usando dados Spring, JPA e Hibernate para executar uma função em cada maior registro de um determinado ID.
Aqui é o meu 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);
}
O método é usado como este, e ele funciona:
@Transactional(readOnly = true)
public void printRecordsGreaterThan(Long lastId) {
myEntityDao.findByIdGreaterThanOrderByIdAsc(lastId).forEach((entity) -> {
System.out.println("entity: " entity.getId());
});
}
A questão é quando esta operação tem de digitalizar uma gama muito grande. I monitorado com VisualVM e está mantendo todos os registros na memória (Dezenas de pena GB de RAM).
Existe alguma maneira de ter este código liberar os recursos, uma vez que são processados em vez de mantê-los na memória?
Desde já, obrigado!
Solução
Graças a @julodnik nos comentários, invocando clear()
sobre o gerenciador de entidades de vez em quando resolveu o problema.
@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());
});
}
Você pode usar setFirstResult e setMaxResults para iterar sobre um grande conjunto de resultados. Você pode encontrar um exemplo nesta questão relacionada .
Outra questão que vem à minha mente é que você pode ter ansiosos fetch definida por padrão. Isto significa que você pode estar recebendo todas as entidades relacionadas e se eles têm entidades relacionadas serão buscados também. Você deve ligar as instruções SQL em seu arquivo de log para verificar se isso está acontecendo.
--- EDIT para responder a comentário
Se os objetos não estão sendo referenciado em java, então você pode limpar o primeiro cache de nível com flush e limpar (em entitymanager). Isso deve limpar todos os objetos carregados.