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

调用persist()时Hibernate 6转JPA PersistenceException错误排查

问题描述

使用Ktor 2.2.1(Kotlin 1.8)+ Hibernate 6.1.6.Final开发Web应用,将实体保存至H2内存数据库(v2.1.214)时,调用session.persist()触发以下错误:

Exception in thread "main" jakarta.persistence.PersistenceException: Converting `org.hibernate.property.access.spi.PropertyAccessException` to JPA `PersistenceException` : Error accessing field [private java.lang.Long com.example.models.jpa.UserDto.id] by reflection for persistent property [com.example.models.jpa.UserDto#id] : com.example.models.jpa.UserDto@32ba5c65
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:165)
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:175)
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:182)
    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:743)
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:721)
    at com.example.models.H2DatabaseFactory.init(H2DatabaseFactory.kt:27)
    at com.example.ApplicationKt.module(Application.kt:11)
    // 省略中间栈帧
Caused by: org.hibernate.property.access.spi.PropertyAccessException: Error accessing field [private java.lang.Long com.example.models.jpa.UserDto.id] by reflection for persistent property [com.example.models.jpa.UserDto#id] : com.example.models.jpa.UserDto@32ba5c65
    at org.hibernate.property.access.spi.GetterFieldImpl.get(GetterFieldImpl.java:48)
    // 省略中间栈帧
Caused by: java.lang.IllegalArgumentException: Can not set java.lang.Long field com.example.models.jpa.UserDto.id to com.example.models.jpa.UserDto
    at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
    at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
    at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:58)
    at java.base/jdk.internal.reflect.UnsafeObjectFieldAccessorImpl.get(UnsafeObjectFieldAccessorImpl.java:36)
    at java.base/java.lang.reflect.Field.get(Field.java:418)
    at org.hibernate.property.access.spi.GetterFieldImpl.get(GetterFieldImpl.java:44)
    // 省略后续栈帧

相关业务代码:

import org.hibernate.cfg.Configuration

val sessionFactory = Configuration()
            .addAnnotatedClass(UserDto::class.java)
            .buildSessionFactory()

val session = sessionFactory.openSession()
session.beginTransaction()

var userDto = UserDto()
userDto.username = "admin"
session.persist(userDto)   // 错误触发点

session.transaction.commit()

UserDto实体类:

package com.example.models.jpa

import jakarta.persistence.*

@Entity
open class UserDto {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    open var id: Long? = null

    @Column(nullable = false)
    open var username: String = ""
}

查询功能(session.createQuery("select u from UserDto u", UserDto::class.java).list())可正常执行,但保存操作失败。

build.gradle.kts核心配置:

plugins {
    kotlin("jvm") version "1.8.0"
    id("io.ktor.plugin") version "2.2.1"
    id("org.jetbrains.kotlin.plugin.allopen") version "1.8.0"
    id("org.jetbrains.kotlin.plugin.noarg") version "1.8.0"
    id("org.jetbrains.kotlin.plugin.jpa") version "1.8.0"
}

allOpen {
    annotation("javax.persistence.Entity")
    annotation("javax.persistence.MappedSuperclass")
    annotation("javax.persistence.Embeddable")
    annotation("jakarta.persistence.Entity")
    annotation("jakarta.persistence.MappedSuperclass")
    annotation("jakarta.persistence.Embeddable")
}

dependencies {
    // Ktor相关依赖省略
    // 数据库依赖
    implementation("mysql:mysql-connector-java:8.0.30")
    implementation("org.hibernate:hibernate-core:6.1.6.Final")
    implementation("com.h2database:h2:$h2_version")
}

application.yml配置:

ktor:
    application:
        modules:
            - com.example.ApplicationKt.module
    development: true
    deployment:
        port: 8080
        watch:
            - classes
            - resources
jwt:
    domain: "https://jwt-provider-domain/"
    audience: "jwt-audience"
    realm: "ktor sample app"

问题原因

核心错误Can not set java.lang.Long field com.example.models.jpa.UserDto.id to com.example.models.jpa.UserDto表明:Hibernate通过反射访问id字段时出现类型匹配异常。根源在于Kotlin字段的open修饰符与Hibernate代理生成机制冲突,且属性访问策略未适配Kotlin语法特性。

解决方法

1. 移除实体类字段的open修饰符

Hibernate仅要求实体类本身为open(由allOpen插件自动处理),字段无需额外标记open,否则会干扰代理生成时的反射逻辑:

package com.example.models.jpa

import jakarta.persistence.*

@Entity
open class UserDto {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    var id: Long? = null // 移除open修饰符

    @Column(nullable = false)
    var username: String = "" // 移除open修饰符
}

2. 显式指定属性访问策略

在@Entity注解上添加@Access(AccessType.FIELD),强制Hibernate直接访问字段,避免Kotlin getter/setter带来的反射问题:

@Entity
@Access(AccessType.FIELD)
open class UserDto {
    // 字段代码不变
}

3. 配置noarg插件适配Jakarta注解

在build.gradle.kts中明确指定noarg插件处理Jakarta的@Entity注解,确保无参构造函数正确生成:

noarg {
    annotation("jakarta.persistence.Entity")
}

4. 完善Hibernate数据库配置

确保Configuration包含完整的H2数据库连接参数,避免因配置缺失导致的隐性错误:

val sessionFactory = Configuration()
    .addAnnotatedClass(UserDto::class.java)
    // H2数据库核心配置
    .setProperty("hibernate.connection.driver_class", "org.h2.Driver")
    .setProperty("hibernate.connection.url", "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1")
    .setProperty("hibernate.connection.username", "sa")
    .setProperty("hibernate.connection.password", "")
    .setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect")
    .setProperty("hibernate.hbm2ddl.auto", "update") // 自动创建/更新表结构
    .buildSessionFactory()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 05:56:05