从Hibernate 5.x迁移到6.2后JPQL实例化查询报错求助
Hibernate 5.x迁移到6.2后的动态实例化报错问题
问题场景
将项目从Hibernate 5.x迁移到6.2后,原本正常运行的JPQL查询出现实例化错误,以下是相关代码与报错信息:
原始查询代码
public List<MarketCommodityInfo> getMarketCommodityInfoForCommodities(Double longitude, Double latitude, Integer page) { return getEntityManager().createQuery( "SELECT new org.kisanag.model.mandi.MarketCommodityInfo (mci, earth_distance(ll_to_earth(m.latitude, m.longitude), ll_to_earth(:latitude, :longitude)) / :conversion AS distance) FROM MarketCommodityInfo mci JOIN mci.id.market m JOIN mci.id.commodity c WHERE earth_distance(ll_to_earth(m.latitude, m.longitude),ll_to_earth(:latitude, :longitude)) < :within * :conversion ORDER BY distance, mci.id.arrivalDate, c.name", MarketCommodityInfo.class) .setParameter("latitude", latitude) .setParameter("longitude", longitude) .setParameter("within", 200) .setParameter("conversion", 1000) .setMaxResults(pageSize) .setFirstResult(page * pageSize) .getResultList(); }
第一次报错信息
Caused by: java.lang.IllegalStateException: Could not determine appropriate instantiation strategy - no matching constructor found and one or more arguments did not define alias for bean-injection at org.hibernate.sql.results.graph.instantiation.internal.DynamicInstantiationResultImpl.resolveAssembler(DynamicInstantiationResultImpl.java:197) at org.hibernate.sql.results.graph.instantiation.internal.DynamicInstantiationResultImpl.createResultAssembler(DynamicInstantiationResultImpl.java:106) at org.hibernate.sql.results.jdbc.internal.StandardJdbcValuesMapping.resolveAssemblers(StandardJdbcValuesMapping.java:53) at org.hibernate.sql.results.internal.ResultsHelper.createRowReader(ResultsHelper.java:78) at org.hibernate.sql.results.internal.ResultsHelper.createRowReader(ResultsHelper.java:64) at org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.doExecuteQuery(JdbcSelectExecutorStandardImpl.java:341) at org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.executeQuery(JdbcSelectExecutorStandardImpl.java:168) at org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.list(JdbcSelectExecutorStandardImpl.java:93) at org.hibernate.sql.exec.spi.JdbcSelectExecutor.list(JdbcSelectExecutor.java:31) at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.lambda$new$0(ConcreteSqmSelectQueryPlan.java:109) at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.withCacheableSqmInterpretation(ConcreteSqmSelectQueryPlan.java:302) at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.performList(ConcreteSqmSelectQueryPlan.java:243) at org.hibernate.query.sqm.internal.QuerySqmImpl.doList(QuerySqmImpl.java:521) at org.hibernate.query.spi.AbstractSelectionQuery.list(AbstractSelectionQuery.java:367) at org.hibernate.query.sqm.internal.QuerySqmImpl.list(QuerySqmImpl.java:1084) at org.hibernate.query.Query.getResultList(Query.java:119)
修改后的查询(添加别名)
SELECT new org.kisanag.model.mandi.MarketCommodityInfo (mci AS *commodityInfo*, earth_distance(ll_to_earth(m.latitude, m.longitude), ll_to_earth(:latitude, :longitude)) / :conversion AS distance) FROM MarketCommodityInfo mci JOIN mci.id.market m JOIN mci.id.commodity c WHERE earth_distance(ll_to_earth(m.latitude, m.longitude),ll_to_earth(:latitude, :longitude)) < :within * :conversion ORDER BY distance, mci.id.arrivalDate, c.name
第二次报错信息
Caused by: org.hibernate.query.sqm.sql.internal.InstantiationException: Unable to determine dynamic instantiation injection strategy for org.kisanag.model.mandi.MarketCommodityInfo#commodityInfo at org.hibernate.sql.results.graph.instantiation.internal.DynamicInstantiationAssemblerInjectionImpl.lambda$new$0(DynamicInstantiationAssemblerInjectionImpl.java:82) at org.hibernate.internal.util.beans.BeanInfoHelper.visitBeanInfo(BeanInfoHelper.java:55) at org.hibernate.internal.util.beans.BeanInfoHelper.visitBeanInfo(BeanInfoHelper.java:48) at org.hibernate.sql.results.graph.instantiation.internal.DynamicInstantiationAssemblerInjectionImpl.<init>(DynamicInstantiationAssemblerInjectionImpl.java:35) at org.hibernate.sql.results.graph.instantiation.internal.DynamicInstantiationResultImpl.resolveAssembler(DynamicInstantiationResultImpl.java:208) at org.hibernate.sql.results.graph.instantiation.internal.DynamicInstantiationResultImpl.createResultAssembler(DynamicInstantiationResultImpl.java:106) at org.hibernate.sql.results.jdbc.internal.StandardJdbcValuesMapping.resolveAssemblers(StandardJdbcValuesMapping.java:53) at org.hibernate.sql.results.internal.ResultsHelper.createRowReader(ResultsHelper.java:78) at org.hibernate.sql.results.internal.ResultsHelper.createRowReader(ResultsHelper.java:64) at org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.doExecuteQuery(JdbcSelectExecutorStandardImpl.java:341) at org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.executeQuery(JdbcSelectExecutorStandardImpl.java:168) at org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.list(JdbcSelectExecutorStandardImpl.java:93) at org.hibernate.sql.exec.spi.JdbcSelectExecutor.list(JdbcSelectExecutor.java:31) at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.lambda$new$0(ConcreteSqmSelectQueryPlan.java:109) at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.withCacheableSqmInterpretation(ConcreteSqmSelectQueryPlan.java:302) at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.performList(ConcreteSqmSelectQueryPlan.java:243) at org.hibernate.query.sqm.internal.QuerySqmImpl.doList(QuerySqmImpl.java:521) at org.hibernate.query.spi.AbstractSelectionQuery.list(AbstractSelectionQuery.java:367) at org.hibernate.query.sqm.internal.QuerySqmImpl.list(QuerySqmImpl.java:1084) at org.hibernate.query.Query.getResultList(Query.java:119)
实体类代码
import jakarta.persistence.EmbeddedId; import jakarta.persistence.Entity; import jakarta.persistence.Transient; @Entity public class MarketCommodityInfo { @EmbeddedId public MarketCommodityInfoKey id; public Double minPrice; public Double modalPrice; public Double maxPrice; public String unit; @Transient public Double distance; public MarketCommodityInfo() { } public MarketCommodityInfo(MarketCommodityInfo commodityInfo, Double distance) { this.id = commodityInfo.id; this.minPrice = commodityInfo.minPrice; this.modalPrice = commodityInfo.modalPrice; this.maxPrice = commodityInfo.maxPrice; this.unit = commodityInfo.unit; this.distance = distance; } }
嵌入式主键类代码
import java.io.Serializable; import java.util.Date; import jakarta.persistence.Embeddable; import jakarta.persistence.OneToOne; @Embeddable public class MarketCommodityInfoKey implements Serializable { public Date arrivalDate; @OneToOne public Commodity commodity; @OneToOne public Market market; public MarketCommodityInfoKey() { } public MarketCommodityInfoKey(Date arrivalDate, Commodity commodity, Market market) { this.arrivalDate = arrivalDate; this.commodity = commodity; this.market = market; } @Override public String toString() { return "MarketCommodityInfoKey [arrivalDate=" + arrivalDate + ", commodity=" + commodity + ", market=" + market + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((arrivalDate == null) ? 0 : arrivalDate.hashCode()); result = prime * result + ((commodity == null) ? 0 : commodity.hashCode()); result = prime * result + ((market == null) ? 0 : market.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; MarketCommodityInfoKey other = (MarketCommodityInfoKey) obj; if (arrivalDate == null) { if (other.arrivalDate != null) return false; } else if (!arrivalDate.equals(other.arrivalDate)) return false; if (commodity == null) { if (other.commodity != null) return false; } else if (!commodity.equals(other.commodity)) return false; if (market == null) { if (other.market != null) return false; } else if (!market.equals(other.market)) return false; return true; } }
解决方案
错误原因
Hibernate 6.x对JPQL动态实例化的语法检查更严格,核心问题出在构造函数参数的写法上:
- 原始查询中,在
new的参数里给distance加了AS distance,导致Hibernate无法匹配到实体类中MarketCommodityInfo(MarketCommodityInfo, Double)的构造函数,进而尝试使用bean注入策略,但不符合注入条件,触发第一次报错。 - 第二次修改时添加的
mci AS *commodityInfo*完全错误,实体类中不存在名为commodityInfo的属性,Hibernate尝试bean注入时找不到对应字段,触发第二次报错。
修正后的查询
去掉构造函数参数中的所有别名,同时为了在ORDER BY中使用distance,可选择以下两种方案:
方案1:重复distance计算表达式
public List<MarketCommodityInfo> getMarketCommodityInfoForCommodities(Double longitude, Double latitude, Integer page) { String jpql = "SELECT new org.kisanag.model.mandi.MarketCommodityInfo (mci, earth_distance(ll_to_earth(m.latitude, m.longitude), ll_to_earth(:latitude, :longitude)) / :conversion) " + "FROM MarketCommodityInfo mci " + "JOIN mci.id.market m " + "JOIN mci.id.commodity c " + "WHERE earth_distance(ll_to_earth(m.latitude, m.longitude),ll_to_earth(:latitude, :longitude)) < :within * :conversion " + "ORDER BY earth_distance(ll_to_earth(m.latitude, m.longitude), ll_to_earth(:latitude, :longitude)) / :conversion, mci.id.arrivalDate, c.name"; return getEntityManager().createQuery(jpql, MarketCommodityInfo.class) .setParameter("latitude", latitude) .setParameter("longitude", longitude) .setParameter("within", 200) .setParameter("conversion", 1000) .setMaxResults(pageSize) .setFirstResult(page * pageSize) .getResultList(); }
方案2:使用子查询封装distance
如果不想重复计算表达式,可用子查询先封装distance字段:
public List<MarketCommodityInfo> getMarketCommodityInfoForCommodities(Double longitude, Double latitude, Integer page) { String jpql = "SELECT new org.kisanag.model.mandi.MarketCommodityInfo (mc.mci, mc.distance) " + "FROM (SELECT mci, earth_distance(ll_to_earth(m.latitude, m.longitude), ll_to_earth(:latitude, :longitude)) / :conversion AS distance " + " FROM MarketCommodityInfo mci " + " JOIN mci.id.market m " + " JOIN mci.id.commodity c " + " WHERE earth_distance(ll_to_earth(m.latitude, m.longitude),ll_to_earth(:latitude, :longitude)) < :within * :conversion) mc " + "ORDER BY mc.distance, mc.mci.id.arrivalDate, mc.mci.id.commodity.name"; return getEntityManager().createQuery(jpql, MarketCommodityInfo.class) .setParameter("latitude", latitude) .setParameter("longitude", longitude) .setParameter("within", 200) .setParameter("conversion", 1000) .setMaxResults(pageSize) .setFirstResult(page * pageSize) .getResultList(); }
两种方案都能正确匹配实体类的构造函数,解决实例化报错问题。
内容的提问来源于stack exchange,提问作者Mahen
相关产品推荐
相关产品推荐

