JPQL查询ThingType关联属性报错:无法找到匹配构造方法
JPQL查询OneToMany关联时构造器参数不匹配问题
问题场景
定义了包含OneToMany关联的ThingType实体类:
@Entity @NoArgsConstructor @AllArgsConstructor @Getter @Setter public class ThingType { @Id @Column(name = "thing_type_id") private String thingTypeId; @Column(length = 50) private String description; @Embedded private Managed managed; @OneToMany(orphanRemoval = true,mappedBy = "thingType",cascade = CascadeType.ALL,fetch = FetchType.LAZY) private List<ThingTypeBasicProperties> basicProperties = new ArrayList<>(); @OneToMany(orphanRemoval = true,mappedBy = "thingType",cascade = CascadeType.ALL,fetch = FetchType.LAZY) private List<ThingTypeMeasurementProperties> measurementProperties = new ArrayList<>(); }
编写Repository接口通过JPQL查询ThingType及其关联集合:
@Repository public interface ThingTypeRepository extends JpaRepository<ThingType,String> { @Query(value = "select new io.badhusha.iotmonitor.model.ThingType(t.thingTypeId,t.description,bp,mp) from ThingType t LEFT JOIN t.basicProperties bp LEFT JOIN t.measurementProperties mp where t.thingTypeId = ?1") Optional<ThingType> findThingTypeById(@NonNull String thingTypeId); }
执行查询时抛出错误:
Unable to locate appropriate constructor on class [io.project.model.ThingType]. Expected arguments are: java.lang.String, java.lang.String, io.project.model.ThingTypeBasicProperties, io.project.model.ThingTypeMeasurementProperties
错误原因
你的JPQL写法存在两个核心问题:
- LEFT JOIN的行为特性:
LEFT JOIN t.basicProperties bp会将ThingType与每个关联的ThingTypeBasicProperties做笛卡尔积,返回多条记录,每条记录中的bp是单个ThingTypeBasicProperties实体,而非List集合;同理mp也是单个ThingTypeMeasurementProperties实体。 - 构造器参数不匹配:
ThingType的全参构造器要求传入List<ThingTypeBasicProperties>和List<ThingTypeMeasurementProperties>类型的参数,但JPQL查询返回的是单个实体类型的参数,所以JPA找不到匹配的构造器,抛出错误。
解决方案
推荐使用FETCH JOIN直接加载关联集合,JPA会自动将关联的实体组装成集合并注入到ThingType对象中,无需手动调用构造器:
方案1:使用FETCH JOIN + DISTINCT
@Repository public interface ThingTypeRepository extends JpaRepository<ThingType,String> { @Query("select distinct t from ThingType t LEFT JOIN FETCH t.basicProperties LEFT JOIN FETCH t.measurementProperties where t.thingTypeId = ?1") Optional<ThingType> findThingTypeById(@NonNull String thingTypeId); }
LEFT JOIN FETCH用于立即加载关联的集合,替代默认的懒加载;distinct用于消除笛卡尔积导致的重复ThingType对象(因为两个集合关联会产生多条重复的主实体记录)。
方案2:禁用DISTINCT传递(针对Hibernate)
如果不想用distinct,可以通过Hibernate的查询提示禁用SQL层面的DISTINCT,仅在JPA层面去重:
@Repository public interface ThingTypeRepository extends JpaRepository<ThingType,String> { @Query("select t from ThingType t LEFT JOIN FETCH t.basicProperties LEFT JOIN FETCH t.measurementProperties where t.thingTypeId = ?1") @QueryHints(value = @QueryHint(name = org.hibernate.annotations.QueryHints.PASS_DISTINCT_THROUGH, value = "false")) Optional<ThingType> findThingTypeById(@NonNull String thingTypeId); }
方案3:调整构造器(不推荐)
如果一定要使用构造器方式,需要自定义接受单个实体的构造器,再手动聚合集合,但这种方式需要额外处理分组逻辑,代码复杂度高,远不如FETCH JOIN简洁,因此不推荐。
内容的提问来源于stack exchange,提问作者Badhusha
相关产品推荐
相关产品推荐

