我正在使用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(在实体管理器中(清除第一级缓存。这应该清除所有加载的对象。