迁移至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定义结果与构造函数的映射关系:
- 在关联实体类(如
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 { // 实体字段定义 }
- 在查询中指定使用该映射:
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
相关产品推荐
相关产品推荐

