You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

迁移至Hibernate 6后投影查询抛出InstantiationException异常

问题分析

迁移至Quarkus 3.14 + Hibernate 6后,自定义查询投影失效,核心原因是Hibernate 6对构造函数投影的类型匹配、构造函数识别规则比旧版本更严格,结合Kotlin编译特性,导致原本可被识别的辅助构造函数无法被Hibernate找到,具体表现为:

  • 查询返回的装箱类型(如Integer)与Kotlin构造函数的基本类型参数(如Int)不匹配
  • Panache的project()方法在Hibernate 6下的构造函数解析逻辑发生变化
  • 未显式指定构造函数时,Hibernate无法自动匹配到正确的辅助构造函数
解决方案

方案1:JPQL中显式使用构造函数表达式

直接在查询语句中用NEW关键字指定投影类的构造函数,让Hibernate明确调用目标:

fun getSender(senderId: Long): UnitDetails {
    return find(
        "SELECT NEW xx.domain.UnitDetails(ud.code, ud.type, route.sequence) " +
            "FROM RouteEntity route " +
            "JOIN UnitDetailsEntity ud ON ud.id = route.senderUnitDetailsId "
    ).list().first() // 根据业务需求调整结果获取逻辑
}

注意:需确保JPQL中的全类名正确,且构造函数参数的顺序、类型与查询返回字段完全匹配。

方案2:调整构造函数参数类型为包装类

将Kotlin辅助构造函数的Int参数改为Integer,匹配Hibernate返回的装箱类型:

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

修改后Hibernate能找到参数类型完全匹配的构造函数。

方案3:使用@ConstructorResult显式映射结果

通过@SqlResultSetMapping和@ConstructorResult定义结果与构造函数的映射关系:

  1. 在关联实体类(如RouteEntity)上定义映射:
@SqlResultSetMapping(
    name = "UnitDetailsMapping",
    classes = [
        ConstructorResult(
            targetClass = UnitDetails::class.java,
            columns = [
                ColumnResult(name = "code", type = String::class.java),
                ColumnResult(name = "type", type = BusinessUnitType::class.java),
                ColumnResult(name = "sequence", type = Integer::class.java)
            ]
        )
    ]
)
@Entity
class RouteEntity {
    // 实体字段定义
}
  1. 在查询中指定使用该映射:
fun getSender(senderId: Long): UnitDetails {
    return entityManager.createQuery(
        "SELECT ud.code as code, ud.type as type, route.sequence as sequence " +
            "FROM RouteEntity route " +
            "JOIN UnitDetailsEntity ud ON ud.id = route.senderUnitDetailsId ",
        UnitDetails::class.java
    ).setHint("org.hibernate.annotations.QueryHints.RESULT_SET_MAPPING", "UnitDetailsMapping")
        .singleResult
}

该方案适合复杂投影场景,可精确控制字段到构造函数参数的映射。

方案4:改用Panache接口投影

如果业务允许,用接口投影替代类投影,避免构造函数匹配问题:

interface UnitDetailsProjection {
    val code: String
    val type: BusinessUnitType
    val sequence: Int

    // 可选:定义默认方法聚合字段
    fun getBusinessUnit(): BusinessUnit {
        return BusinessUnit(code, type)
    }
}

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

接口投影无需构造函数,Panache会自动生成代理类实现接口,适合简单投影场景。

验证建议
  • 优先尝试方案1,显式构造函数表达式是Hibernate 6推荐的投影方式,兼容性最好
  • 使用Kotlin时,检查构造函数参数类型是否与JDBC返回类型一致(基本类型vs包装类)
  • 确保投影类构造函数为public访问权限(Kotlin默认是public,无需额外修改)

内容的提问来源于stack exchange,提问作者osumatu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 00:06:01