JPA报InaccessibleObjectException无法访问实体私有字段问题
你的判断完全准确,报错是Java模块系统(JPMS)的反射权限配置缺失导致的。
核心报错信息已经明确指出问题:
java.lang.reflect.InaccessibleObjectException: Unable to make field private int com.c57lee.possystemdemo.obj.Test.testId accessible: module com.c57lee.possystemdemo does not "opens com.c57lee.possystemdemo.obj" to unnamed module @191c1c44
Hibernate通过反射读取实体类私有字段完成ORM映射,Java 9+启用模块系统后,默认禁止外部模块反射访问未开放包下的类成员。你当前的module-info.java仅开放了根包给JavaFX FXML模块,既没开放实体类所在的com.c57lee.possystemdemo.obj包,也缺少Hibernate相关的依赖声明。
除此之外你还有几处配置问题:
persistence.xml使用的2.1版本是旧JPA(javax包)规范,和你当前引入的Jakarta Persistence 3.0、Hibernate 6不兼容- 没有在持久化配置中声明受管理的实体类,Hibernate无法扫描到Test实体
- JDBC连接URL缺少MySQL 8.x驱动必需的时区、SSL相关参数,后续连通数据库时也会触发报错
1. 修改module-info.java配置
补全Hibernate依赖声明,同时把实体类所在包开放给Hibernate反射访问权限,修改后内容如下:
module com.c57lee.possystemdemo { requires javafx.controls; requires javafx.fxml; requires java.sql; requires jakarta.persistence; // 引入Hibernate核心模块 requires org.hibernate.orm.core; opens com.c57lee.possystemdemo to javafx.fxml; // 开放实体类所在包给Hibernate、JPA模块,允许反射访问私有成员 opens com.c57lee.possystemdemo.obj to org.hibernate.orm.core, jakarta.persistence; exports com.c57lee.possystemdemo; }
后续新增其他存放实体类的包,都要按同样格式添加opens声明,否则Hibernate访问对应实体时还会报同类错误。
2. 修正persistence.xml配置
升级持久化规范版本到3.0适配Jakarta EE,添加实体类声明,补全JDBC连接参数,修改后内容如下:
<?xml version="1.0" encoding="UTF-8"?> <persistence version="3.0" xmlns="https://jakarta.ee/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd"> <persistence-unit name="c57leePersistence" transaction-type="RESOURCE_LOCAL"> <!-- 声明所有受JPA管理的实体类 --> <class>com.c57lee.possystemdemo.obj.Test</class> <properties> <property name="jakarta.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver" /> <property name="jakarta.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/POSSystemDemo?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true" /> <property name="jakarta.persistence.jdbc.user" value="root" /> <property name="jakarta.persistence.jdbc.password" value="" /> <!-- 开发阶段可选配置:自动建表、打印SQL方便调试 --> <property name="hibernate.hbm2ddl.auto" value="update" /> <property name="hibernate.show_sql" value="true" /> </properties> </persistence-unit> </persistence>
后续新增实体类,要在<persistence-unit>标签下添加对应的<class>标签声明。如果你的数据库设置了密码,把jakarta.persistence.jdbc.password的值改成对应密码即可。
3. 补充资源释放逻辑(建议优化)
你当前的代码用完EntityManager、EntityManagerFactory后没有关闭,长期运行会出现连接泄漏,建议调整JPA操作代码,异常时回滚事务,用完主动释放资源:
@Override public void start(Stage stage) throws IOException { EntityManagerFactory emf = null; EntityManager em = null; try { emf = Persistence.createEntityManagerFactory("c57leePersistence"); em = emf.createEntityManager(); em.getTransaction().begin(); Test test1 = new Test(); test1.setTestId(1); test1.setTestName("testname"); test1.setTestScore(70); System.out.println(test1); em.persist(test1); em.getTransaction().commit(); } catch (Exception e) { if (em != null && em.getTransaction().isActive()) { em.getTransaction().rollback(); } e.printStackTrace(); } finally { if (em != null) em.close(); if (emf != null) emf.close(); } // 原有JavaFX界面加载逻辑写在此处 }
改完以上配置重新编译运行,反射访问报错就会消失,JPA可以正常完成实体持久化操作。
内容的提问来源于stack exchange,提问作者lcm5506

