You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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. 优先用方案1:一次查询获取存在的实体,避免异常,但需接受字段加载的开销
    2. 或在方案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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 08:45:30