JPA执行Select查询时如何映射实体未定义的额外返回字段
解决方案
JPA针对非实体全字段的查询场景,共有3种常用实现方式,可根据你的业务需求选择:
方案1:自定义DTO接收结果(最推荐,无侵入原实体)
和之前JDBC的思路一致,新建专门接收该查询结果的DTO类,包含所有返回字段的属性和对应全参构造方法即可:
// 自定义结果传输类 public class ResourceHierarchyDTO { private String businessUnit; private String regionName; private Integer resourceId; private String resourceName; private String timeZone; // 全参构造方法,注意参数顺序要和查询返回字段的顺序完全一致 public ResourceHierarchyDTO(String businessUnit, String regionName, Integer resourceId, String resourceName, String timeZone) { this.businessUnit = businessUnit; this.regionName = regionName; this.resourceId = resourceId; this.resourceName = resourceName; this.timeZone = timeZone; } // 自行补充getter、setter、toString方法 }
然后在ResourceHierarchy实体类上加结果映射注解,指定查询结果和DTO的对应关系:
@SqlResultSetMapping( name = "ResourceHierarchyDTOMapping", classes = @ConstructorResult( targetClass = ResourceHierarchyDTO.class, columns = { @ColumnResult(name = "businessUnit", type = String.class), @ColumnResult(name = "regionName", type = String.class), @ColumnResult(name = "resourceId", type = Integer.class), @ColumnResult(name = "resourceName", type = String.class), @ColumnResult(name = "timeZone", type = String.class) } ) ) @Entity @Table(name="resource_hierarchy") public class ResourceHierarchy { // 原有实体代码保持不变 }
最后在Repository层的查询方法上指定该映射即可:
@Query(nativeQuery = true, value = "你的原生SQL查询语句", resultSetMapping = "ResourceHierarchyDTOMapping") List<ResourceHierarchyDTO> findRAForClocks(@Param("businessUnitValue") String businessUnitValue, @Param("resourceTypeValueA") String resourceTypeValueA, @Param("reorgDate") Date reorgDate, @Param("resourceTypeValueB") String resourceTypeValueB);
方案2:原实体新增@Transient临时字段(适合不想新增类的场景)
如果不想单独新建DTO,可以在原有ResourceHierarchy实体中新增两个临时字段,用@Transient标注,表示该字段不需要和数据库表映射,仅用于接收查询结果:
// 添加到ResourceHierarchy实体类中 @Transient private String businessUnit; @Transient private String regionName; // 补充这两个字段的getter、setter方法
之后配置对应的实体结果映射即可:
@SqlResultSetMapping( name = "ResourceHierarchyWithExtraMapping", entities = @EntityResult( entityClass = ResourceHierarchy.class, fields = { @FieldResult(name = "resourceID", column = "resourceId"), @FieldResult(name = "resourceName", column = "resourceName"), @FieldResult(name = "timeZone", column = "timeZone"), @FieldResult(name = "businessUnit", column = "businessUnit"), @FieldResult(name = "regionName", column = "regionName") } ) )
查询方法返回类型可以继续用List<ResourceHierarchy>,注意该场景下实体中未在查询里返回的字段值会为null,使用时要注意判空。
方案3:Object[]接收后手动转换(适合临时快速开发,不推荐长期维护)
如果不想修改实体配置,可以直接用List<Object[]>接收原生查询的结果,然后手动遍历转换为你需要的模型:
// Repository层查询方法 @Query(nativeQuery = true, value = "你的原生SQL查询语句") List<Object[]> findRAForClocksRaw(@Param("businessUnitValue") String businessUnitValue, @Param("resourceTypeValueA") String resourceTypeValueA, @Param("reorgDate") Date reorgDate, @Param("resourceTypeValueB") String resourceTypeValueB);
手动转换示例:
List<Object[]> rawResult = resourceHierarchyRepository.findRAForClocksRaw(业务参数); List<ResourceHierarchyDTO> finalResult = new ArrayList<>(); for(Object[] row : rawResult) { ResourceHierarchyDTO dto = new ResourceHierarchyDTO(); dto.setBusinessUnit((String) row[0]); dto.setRegionName((String) row[1]); dto.setResourceId((Integer) row[2]); dto.setResourceName((String) row[3]); dto.setTimeZone((String) row[4]); finalResult.add(dto); }
内容的提问来源于stack exchange,提问作者Jeevan Gyawali
相关产品推荐
相关产品推荐

