JPA场景下如何通过@EmbeddableId在ArrayList中查找关联实体对象
问题根因
- 第一:
Department类中employees集合的@OneToMany注解默认使用懒加载策略,你没有显式配置主动加载的话,调用deleteEmployee方法时集合大概率是未初始化的空代理对象,自然找不到目标关联记录 - 第二:异常类型不符合场景,找不到员工关联记录却抛出部门不存在的异常,会误导后续问题排查
- 第三:存在潜在的多记录匹配问题,如果同一个员工在该部门有多条历史任职记录,仅用部门编号和员工编号匹配会命中多条,通常我们需要额外过滤当前在职(
toDate为null)的记录 - 第四:
DepartmentEmployeeId实现了Serializable接口但没有显式声明serialVersionUID,JPA序列化/反序列化复合主键时可能出现异常,导致equals匹配失败
修复方案
方案1:调整内存过滤逻辑(适合集合数据量小的场景)
首先补全DepartmentEmployeeId的序列化ID:
@Embeddable public class DepartmentEmployeeId implements Serializable { // 新增序列化ID private static final long serialVersionUID = 1L; // 原有代码保持不变 }
然后修改Department类的employees属性注解,开启主动加载:
@JsonIgnore @OneToMany(cascade = CascadeType.ALL, mappedBy = "department", orphanRemoval = true, fetch = FetchType.EAGER) private List<DepartmentEmployee> employees = new ArrayList<>();
最后修改deleteEmployee方法:
public void deleteEmployee(Long empNo, LocalDate toDate) { DepartmentEmployeeId targetId = new DepartmentEmployeeId(this.deptNo, empNo); this.employees.stream() // 额外过滤当前在职的记录,避免匹配到历史任职记录 .filter(item -> targetId.equals(item.getId()) && item.getToDate() == null) .findAny() // 替换为符合场景的异常,也可以自定义员工关联不存在的异常 .orElseThrow(() -> new IllegalArgumentException("该部门下未找到对应在职员工:" + empNo)) .setToDate(toDate); }
方案2:直接通过JPA查询修改(更推荐,适合大数据量场景)
不需要加载整个employees集合,直接通过JPQL语句修改目标记录,性能更高,也不需要处理懒加载问题,只需在DepartmentRepository中新增自定义修改方法即可:
@Modifying @Query("update DepartmentEmployee de set de.toDate = :toDate where de.id.deptNo = :deptNo and de.id.empNo = :empNo and de.toDate is null") int updateEmployeeQuitDate(String deptNo, Long empNo, LocalDate toDate);
业务层直接调用该方法执行更新即可。
内容的提问来源于stack exchange,提问作者Taulant Fazliu
相关产品推荐
相关产品推荐

