Hibernate配置@OneToOne关联时抛orphanRemoval方法缺失的NoSuchMethodError
该异常属于运行时类加载优先级冲突,和实体关联的编写逻辑、编译期maven依赖冲突无直接关联,核心触发链路为:
- Spigot 1.8.8 核心jar内置JPA 1.0版本的
javax.persistence.OneToOne注解类,该类不存在orphanRemoval()方法(该方法为JPA 2.0规范新增的接口定义) - 引入的Hibernate 4.1.6.Final基于JPA 2.0规范开发,在绑定
@OneToOne关联元数据时,会强制调用注解的orphanRemoval()方法读取配置值 - Spigot的类加载机制会优先加载服务器核心路径下的类,导致Hibernate运行时获取的
OneToOne注解为核心内置的老版本实现,而非项目打包进插件的JPA 2.0版本,最终触发方法不存在的报错 - 无关联属性时程序运行正常的原因:Hibernate绑定普通列元数据时,不会触发读取
@OneToOne注解的逻辑,自然不会调用到不存在的接口方法。
方案一:低改动兼容(适配Spigot内置类版本)
将Hibernate版本替换为完全兼容JPA 1.0规范的3.6.10.Final版本,该版本Hibernate不会调用JPA 2.0新增的orphanRemoval方法,可直接匹配Spigot核心内置的JPA版本,无需修改任何实体类代码。
对应pom.xml中的依赖配置替换为:
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>3.6.10.Final</version> </dependency>
方案二:保留现有Hibernate版本(解决类加载冲突)
如果需要保留Hibernate 4.1.6的功能特性,通过maven-shade插件的类重定位机制,将项目依赖的JPA 2.0包路径迁移到插件的私有路径下,绕开Spigot核心的类加载优先级限制。
修改pom.xml中maven-shade-plugin的配置,新增类重定位规则:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.2.4</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <createDependencyReducedPom>false</createDependencyReducedPom> <!-- 新增类路径重定位规则 --> <relocations> <relocation> <pattern>javax.persistence</pattern> <shadedPattern>dk.simonwinther.shaded.javax.persistence</shadedPattern> </relocation> </relocations> </configuration> </execution> </executions> </plugin>
配置生效后,插件运行时会优先加载自身私有路径下的JPA 2.0类,不会读取Spigot核心内置的老版本注解类,可从根源上解决方法不存在的问题。
此前通过maven依赖树排查无法定位冲突的原因:触发异常的javax.persistence类并非来自项目编译期依赖,而是Spigot服务器核心jar内置的捆绑类,仅通过编译期依赖扫描无法发现运行时的类加载优先级问题。
注:仅在pom.xml中显式引入高版本JPA依赖无法解决问题,因为Spigot类加载器会优先加载服务器核心路径下的同名类,插件包内的新版本类不会被优先加载。
注:实体类中字段直接初始化new GangPermissions(this.gangId)的写法,不会触发本次异常,无需为排错修改该部分代码。
内容的提问来源于stack exchange,提问作者Techniko

