kotlinhibernatequarkusquarkus-panache

Projection fails with InstantiationException after Hibernate 6 migration


After the migration to Quarkus 3.14 and Hibernate 6, custom query projections stopped working.

I have many more examples but all comes to this exception and combination of a custom query and an aggregate class projection.

The exception I get:

org.hibernate.InstantiationException: Cannot instantiate query result type  'xx.domain.UnitDetails' due to: xx.domain.BusinessUnitDetails.<init>(java.lang.String,xx.domain.BusinessUnitType,java.lang.Integer)

    at org.hibernate.sql.results.internal.RowTransformerConstructorImpl.<init>(RowTransformerConstructorImpl.java:42)
    at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.determineRowTransformer(ConcreteSqmSelectQueryPlan.java:315)
    at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.<init>(ConcreteSqmSelectQueryPlan.java:96)
    at org.hibernate.query.sqm.internal.AbstractSqmSelectionQuery.buildConcreteQueryPlan(AbstractSqmSelectionQuery.java:277)
    at org.hibernate.query.sqm.internal.AbstractSqmSelectionQuery.buildConcreteQueryPlan(AbstractSqmSelectionQuery.java:261)
    at org.hibernate.query.sqm.internal.AbstractSqmSelectionQuery.buildSelectQueryPlan(AbstractSqmSelectionQuery.java:247)
    at org.hibernate.query.internal.QueryInterpretationCacheStandardImpl.resolveSelectQueryPlan(QueryInterpretationCacheStandardImpl.java:83)
    at org.hibernate.query.sqm.internal.SqmSelectionQueryImpl.resolveQueryPlan(SqmSelectionQueryImpl.java:371)
    at org.hibernate.query.sqm.internal.SqmSelectionQueryImpl.doList(SqmSelectionQueryImpl.java:298)
    at org.hibernate.query.spi.AbstractSelectionQuery.list(AbstractSelectionQuery.java:136)
    at org.hibernate.query.SelectionQuery.getResultList(SelectionQuery.java:124)
    at io.quarkus.hibernate.orm.panache.common.runtime.CommonPanacheQueryImpl.list(CommonPanacheQueryImpl.java:303)
    at io.quarkus.hibernate.orm.panache.runtime.PanacheQueryImpl.list(PanacheQueryImpl.java:150)
    at xx.persistence.repository.route.RouteRepository.getConsigneesForConsignor(RouteRepository.kt:111)
    at xx.persistence.repository.route.RouteRepository_Subclass.getConsigneesForConsignor$$superforward(Unknown Source)
    at xx.persistence.repository.route.RouteRepository_Subclass$$function$$26.apply(Unknown Source)
    at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:73)
    at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:62)
    at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorBase.invokeInOurTx(TransactionalInterceptorBase.java:136)
    at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorBase.invokeInOurTx(TransactionalInterceptorBase.java:107)
    at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorRequired.doIntercept(TransactionalInterceptorRequired.java:38)
    at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorBase.intercept(TransactionalInterceptorBase.java:61)
    at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorRequired.intercept(TransactionalInterceptorRequired.java:32)
    at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorRequired_Bean.intercept(Unknown Source)
    at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:42)
    at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:30)
    at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:27)
    at xx.persistence.repository.route.RouteRepository_Subclass.getConsigneesForConsignor(Unknown Source)
    at xx.persistence.repository.route.RouteRepository_ClientProxy.getConsigneesForConsignor(Unknown Source)
    at xx.repository.route.RouteRepositoryIntegrationTest.should return a list of active consignees for the given consignor when one matching consignor(RouteRepositoryIntegrationTest.kt:182)
    at java.base/java.lang.reflect.Method.invoke(Method.java:578)
    at io.quarkus.test.junit.QuarkusTestExtension.runExtensionMethod(QuarkusTestExtension.java:973)
    at io.quarkus.test.junit.QuarkusTestExtension.interceptTestMethod(QuarkusTestExtension.java:823)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
Caused by: java.lang.NoSuchMethodException: xx.domain.BusinessUnitDetails.<init>(java.lang.String,xx.BusinessUnitType,java.lang.Integer)
    at java.base/java.lang.Class.getConstructor0(Class.java:3641)
    at java.base/java.lang.Class.getDeclaredConstructor(Class.java:2810)
    at org.hibernate.sql.results.internal.RowTransformerConstructorImpl.<init>(RowTransformerConstructorImpl.java:37)
    ... 34 more

Repository layer, script:

fun getSender(senderId: Long): UnitDetails {
    return find(
        "SELECT ud.code, ud.type, route.sequence " +
            "FROM RouteEntity route " +
            "JOIN UnitDetailsEntity ud ON ud.id = route.senderUnitDetailsId "
    ).project(UnitDetails::class.java).list()
}

The aggregate domain object I use to project with a custom constructor to satisfy hibernate:

class UnitDetails(
    val businessUnit: BusinessUnit,
    val sequence: Int
) {
    constructor(
        code: String,
        type: BusinessUnitType,
        sequence: Int
    ) : this(BusinessUnit(code, type), sequence)
}

Solution

  • The problem is your Kotlin class uses type Int, which in Java translates to int; but Hibernate ORM expects an Integer in this context.

    Hibernate ORM looks for a constructor with a signature using Integer, doesn't find it, and reports this (admittedly quite unclear) error.

    So, just change your constructor to use Int? (or whatever Kotlin maps to java.lang.Integer):

    class UnitDetails(
        val businessUnit: BusinessUnit,
        val sequence: Int?
    ) {
        constructor(
            code: String,
            type: BusinessUnitType,
            sequence: Int?
        ) : this(BusinessUnit(code, type), sequence)
    }
    

    See also https://github.com/quarkusio/quarkus/issues/43368, https://hibernate.atlassian.net/browse/HHH-18664