Search code examples
hibernatedynamicormquarkusinstantiation

Migrated from Hibernate 5.x to 6.2 and many things which were working before are now failing


Need help with issue I am facing since I moved from Hibernate 5.x to 6.2. The code which has been working without any issue before is failing now. Here is an example for the query which is failing.

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();

    }

This was failing with error

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)

I modified the query and added commodityInfo

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

and now it failing with following error

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)

Here is my class MarketCommodityInfo

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;
    }
}

and the key

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;
    }

}

Please help.


Solution

  • UPDATE

    My previous answer was incorrect.

    The real reason is:

    1. Your first attempt (with constructor injection) does not work because Hibernate is not able to determine the return type of the earth_distance() function, since you have not registered it as a known SQL function. Hibernate 6 currently insists on knowing the types of all constructor arguments.
    2. Your second attempt (with field injection) does not work either, since you have no field named commodityInfo. But even if you fix that, it still won't work, because Hibernate still won't know the type of the earth_distance() function.

    So the solution is to use a FunctionContributor to register this SQL function and its return type.

    Now, I suspect that DynamicInstantiationAssemblerInjectionImpl is being a bit too fussy here, and that we could improve it to handle your case where not every argument type is known. So I will open an issue about that.