能否通过Spring Data Projection实现跨两层@OneToMany关联的JPA查询映射至DTO?
跨两层@OneToMany关联的JPA查询结果映射至DTO(Spring Data Projection实现)
完全可行,结合Spring Data Projection与JPQL聚合查询即可实现需求,以下是具体方案:
1. 定义目标DTO(Projection接口)
使用Spring Data的接口式Projection,无需编写实现类,框架会自动生成动态代理:
public interface BirdDetailsDto { String getName(); String getSex(); Double getLatestAvgLength(); }
2. 编写Repository查询方法
通过JPQL实现关联查询、筛选最新HealthCheck、聚合计算平均值,直接映射到DTO:
@Repository public interface BirdRepository extends JpaRepository<Bird, Long> { @Query(""" SELECT b.name AS name, b.sex AS sex, AVG(lm.value) AS latestAvgLength FROM Bird b LEFT JOIN b.healthChecks hc LEFT JOIN hc.lengthMeasurements lm WHERE hc.checkDateTime = ( SELECT MAX(hc2.checkDateTime) FROM HealthCheck hc2 WHERE hc2.bird = b ) GROUP BY b.id, b.name, b.sex """) List<BirdDetailsDto> findAllBirdDetails(); }
关键说明:
- 子查询用于筛选每个Bird对应的最新HealthCheck(基于
checkDateTime最大值) - 通过
LEFT JOIN确保即使Bird无HealthCheck或HealthCheck无LengthMeasurements时仍能返回结果(此时latestAvgLength为null) - 因HealthCheck继承自Task,只要Task中定义的
lengthMeasurements关联映射正确,JPQL可直接通过hc.lengthMeasurements访问
3. 可选:类形式DTO
若需要更灵活的逻辑(比如默认值处理),可改用类形式DTO,需提供匹配的构造方法:
public record BirdDetailsDto(String name, String sex, Double latestAvgLength) { // 可选:添加带默认值的构造方法,处理null场景 public BirdDetailsDto(String name, String sex) { this(name, sex, 0.0); } }
此时JPQL需调整为调用构造方法:
@Query(""" SELECT new com.yourpackage.BirdDetailsDto(b.name, b.sex, AVG(lm.value)) FROM Bird b LEFT JOIN b.healthChecks hc LEFT JOIN hc.lengthMeasurements lm WHERE hc.checkDateTime = ( SELECT MAX(hc2.checkDateTime) FROM HealthCheck hc2 WHERE hc2.bird = b ) GROUP BY b.id, b.name, b.sex """) List<BirdDetailsDto> findAllBirdDetails();
内容的提问来源于stack exchange,提问作者Ray Bond
相关产品推荐
相关产品推荐

