如何从休眠/ JPA流式传输结果并在处理时释放资源?



我正在使用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监控了它,它把所有的记录都保存在内存中(数十Gigs的RAM(。

有没有办法让此代码在处理资源后释放资源,而不是将它们保留在内存中?

提前感谢!

溶液

由于评论中的@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());
});
}

可以使用 setFirstResult 和 setMaxResults 来迭代大型结果集。您可以在此相关问题中找到示例。

我想到的另一个问题是,默认情况下您可能设置了急切获取。这意味着您可能会获得所有相关实体,如果它们具有相关实体,则也会获取它们。您应该打开日志文件中的 sql 语句以检查是否发生这种情况。

---编辑以回答评论

如果未在 java 中引用对象,则可以使用 flush 和 clear(在实体管理器中(清除第一级缓存。这应该清除所有加载的对象。

最新更新