Spring JPA最佳实践:findAllById与循环getReferenceById选哪个?
获取实体引用关联其他实体的最佳方案
两种方案的核心差异
先澄清你对两种方案的部分误解,再逐一分析:
方案1:findAllById
- 执行一次SQL查询:
SELECT * FROM theme WHERE id IN (...),返回所有存在的实体列表 - 行为:默认会加载实体的全部字段(除非通过
@EntityGraph或投影优化),返回的是实体实例或其代理,但无论如何都会触发数据库查询获取实体数据 - 优点:自动过滤不存在的ID,不会抛出异常
- 缺点:如果仅用于关联而不需要访问实体属性,加载全部字段属于冗余性能开销
方案2:循环调用getReferenceById
- 不会触发任何SQL查询:
getReferenceById仅返回实体的懒加载代理,只有当你访问代理的非ID属性时才会触发查询 - 误解纠正:这种方式不会执行N次查询,N次调用只是创建N个代理对象,没有数据库交互
- 优点:完全匹配“仅需关联无需访问属性”的需求,性能最优
- 缺点:若某个ID对应的实体不存在,会直接抛出
EntityNotFoundException
最佳方案选择
根据业务场景做判断:
- 若能确保所有传入的ID都存在:优先选方案2,无冗余查询,性能最好
- 若无法保证ID全部存在:
- 优先用方案1:一次查询获取存在的实体,避免异常,但需接受字段加载的开销
- 或在方案2基础上做异常处理:循环调用时捕获
EntityNotFoundException,过滤掉无效ID,兼顾性能与容错
其他实现方式
自定义Repository批量获取引用
在Repository中封装批量获取引用的方法,简化代码:
@Repository public interface ThemeRepository extends JpaRepository<Theme, Integer> { default List<Theme> getReferencesByIds(Collection<Integer> ids) { return ids.stream() .map(this::getReferenceById) .collect(Collectors.toList()); } }
调用示例:
List<Integer> themesIds = service.getThemesIds(); List<Theme> themesReference = themeRepository.getReferencesByIds(themesIds); anotherEntity.setThemes(themesReference);
直接使用EntityManager
Spring Data JPA的getReferenceById底层依赖EntityManager.getReference(),也可以直接调用:
@Autowired private EntityManager entityManager; // ... List<Theme> themesReference = themesIds.stream() .map(id -> entityManager.getReference(Theme.class, id)) .collect(Collectors.toList()); anotherEntity.setThemes(themesReference);
内容的提问来源于stack exchange,提问作者Rafael Furtado
相关产品推荐
相关产品推荐

