Hibernate执行HQL更新查询时ClassCastException问题排查
Hibernate Kotlin应用中执行HQL更新查询时的ClassCastException问题
错误信息
java.lang.ClassCastException: class org.hibernate.hql.internal.ast.tree.SqlNode cannot be cast to class org.hibernate.hql.internal.ast.tree.FromReferenceNode (org.hibernate.hql.internal.ast.tree.SqlNode and org.hibernate.hql.internal.ast.tree.FromReferenceNode are in unnamed module of loader 'app') at org.hibernate.hql.internal.ast.HqlSqlWalker.generateSyntheticDotNodeForNonQualifiedPropertyRef(HqlSqlWalker.java:747) at org.hibernate.hql.internal.ast.HqlSqlWalker.lookupNonQualifiedProperty(HqlSqlWalker.java:740) at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.propertyRef(HqlSqlBaseWalker.java:1181) at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.assignment(HqlSqlBaseWalker.java:1066) at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.setClause(HqlSqlBaseWalker.java:782) at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.updateStatement(HqlSqlBaseWalker.java:398) at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:286) at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:276) at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:192) at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:144) at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:113) at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:73) at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:162) at org.hibernate.internal.AbstractSharedSessionContract.getQueryPlan(AbstractSharedSessionContract.java:622) at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:734) at org.hibernate.internal.AbstractSessionImpl.createQuery(AbstractSessionImpl.java:23) at jp.assasans.protanki.server.quests.ServerDailyQuest$updateProgress$2$1.invokeSuspend(ServerDailyQuest.kt:48) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106) at kotlinx.coroutines.internal.LimitedDispatcher.run(LimitedDispatcher.kt:42) at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:95) at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:749) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
问题场景
在基于Hibernate的Kotlin应用中,对抽象实体类ServerDailyQuest执行HQL更新查询时触发上述异常,相关代码如下:
实体类定义
package server.quests import jakarta.persistence.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import server.HibernateUtils import server.client.User import server.extensions.singleOrNullOrThrow import server.utils.LocalizedString @Entity @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @Table( name = "daily_quests", indexes = [ Index(name = "idx_daily_quests_user", columnList = "user_id") ] ) abstract class ServerDailyQuest( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) val id: Int, @ManyToOne val user: User, @Column(nullable = false, name = "questIndex") val index: Int, // INDEX是MariaDB的保留字 @Column(nullable = false) var current: Int, @Column(nullable = false) var required: Int, @Column(nullable = false) var new: Boolean, @Column(nullable = false) var completed: Boolean, @OneToMany(targetEntity = ServerDailyQuestReward::class, mappedBy = "id.quest") val rewards: MutableList<ServerDailyQuestReward> ) { @get:Transient abstract val description: LocalizedString suspend fun updateProgress() { HibernateUtils.createEntityManager().let { entityManager -> entityManager.transaction.begin() withContext(Dispatchers.IO) { val queryString = "UPDATE ServerDailyQuest SET current = :current, new = :new, completed = :completed WHERE id = :id" entityManager .createQuery(queryString) .setParameter("current", current) .setParameter("id", id) .setParameter("new", new) .setParameter("completed", completed) .executeUpdate() } entityManager.transaction.commit() entityManager.close() } } }
触发异常的核心代码
val queryString = "UPDATE ServerDailyQuest SET current = :current, new = :new, completed = :completed WHERE id = :id"
entityManager.createQuery(queryString)
问题原因
HQL中的new是保留关键字,实体类中定义的属性名new与该关键字冲突,导致Hibernate在解析HQL语句时无法正确识别这是一个实体属性,进而触发类型转换异常。
解决方案
有两种可行的解决方式:
方式一:转义HQL中的属性名
在HQL里用反引号(`)包裹冲突的属性名,明确告诉Hibernate这是一个实体属性而非关键字:
val queryString = "UPDATE ServerDailyQuest SET current = :current, `new` = :new, completed = :completed WHERE id = :id"
方式二:修改实体类属性名
将实体类中的new属性名改为非关键字名称,比如isNew,如果需要保持数据库列名不变,可以通过@Column注解指定:
@Column(nullable = false, name = "new") var isNew: Boolean,
对应的HQL更新语句和参数设置也需要同步修改:
val queryString = "UPDATE ServerDailyQuest SET current = :current, isNew = :isNew, completed = :completed WHERE id = :id" // 参数设置部分 .setParameter("isNew", isNew)
内容的提问来源于stack exchange,提问作者Misteriumbr
相关产品推荐
相关产品推荐

