PROBLEM
You configure Hibernate using annotations and set orphanRemoval property in @OneToMany.
@Entity
@Table(name = "person")
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "personId")
private Long id;
@OneToMany(mappedBy = "person", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<car> cars = new HashSet<car>();
...
}
When you run the application, the application server throws the following exception:-
Caused by: java.lang.NoSuchMethodError: javax/persistence/OneToMany.orphanRemoval()Z
at org.hibernate.cfg.AnnotationBinder.processElementAnnotations(AnnotationBinder.java:1868)
at org.hibernate.cfg.AnnotationBinder.processIdPropertiesIfNotAlready(AnnotationBinder.java:767)
In my case, I’m getting this exception when I run on Websphere Application Server (WAS) 7.5.
SOLUTION
The orphanRemoval property in @OneToMany requires JPA 2.x to work. If you already have JPA 2.x in your classpath and you are still getting the error, then JPA 1.x must already been loaded either by your application or by the application server, causing your JPA 2.x to be ignored.
To fix this:-
- Add JPA 2.x in your classpath. For example, hibernate-jpa-2.0-api-1.0.1.Final.jar.
- Remove any trace of JPA 1.x from your classpath by scanning your dependency tree.
- If your application server uses JPA 1.x, you will need to remove or replace that jar with JPA 2.x.
- If you are not allowed to replace JPA 1.x jar on the application server (for example, you are not the admin of the server), you will need to invert the application class loader to PARENT_LAST to ensure the JPA 2.x from your application gets loaded first and JPA 1.x from the application server will get ignored.
Inverting class loader for application without replacing JPA 1.x from application server
Assuming you have JPA 2.x in your application classpath and your project structure looks like this:-
myproject
|- myproject-ear
| |- pom.xml
|- myproject-war
| |- src
| | |- main
| | |- java
| | |- resources
| | |- webapp
| |- pom.xml
|- pom.xml
… if you are deploying your application to Websphere Application Server (WAS), create deployment.xml under src/main/application in the EAR module and instruct WAS to load parent last:-
<!--?xml version="1.0" encoding="UTF-8"?-->
<appdeployment:deployment xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:appdeployment="http://www.ibm.com/websphere/appserver/schemas/5.0/appdeployment.xmi" xmi:id="Deployment_1">
<deployedobject xmi:type="appdeployment:ApplicationDeployment" xmi:id="ApplicationDeployment_1" startingweight="1" warclassloaderpolicy="SINGLE">
<classloader xmi:id="Classloader_1" mode="PARENT_LAST">
</classloader></deployedobject>
</appdeployment:deployment>
In the EAR pom.xml, extend the parent POM and configure the Maven EAR Plugin:-
<!--?xml version="1.0" encoding="UTF-8"?-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelversion>4.0.0</modelversion>
<!-- Extend the parent POM -->
<parent>
<groupid>myproject</groupid>
<artifactid>myproject</artifactid>
<version>1.0</version>
</parent>
<artifactid>myproject-ear</artifactid>
<packaging>ear</packaging>
<name>${project.artifactId}</name>
<build>
<plugins>
<!-- Configure Maven EAR Plugin -->
<plugin>
<artifactid>maven-ear-plugin</artifactid>
<version>2.4</version>
<configuration>
<displayname>${project.parent.artifactId}</displayname>
<modules>
<webmodule>
<groupid>myproject</groupid>
<artifactid>myproject-war</artifactid>
<bundlefilename>${project.parent.artifactId}.war</bundlefilename>
<contextroot>${context-root}</contextroot>
</webmodule>
</modules>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!--
Expose the WAR module to allow the WAR file to be packaged
in the EAR file by Maven EAR Plugin
-->
<dependency>
<groupid>myproject</groupid>
<artifactid>myproject-war</artifactid>
<version>1.0</version>
<type>war</type>
</dependency>
</dependencies>
</project>
Your current project structure should look like this now:-
myproject
|- myproject-ear
| |- src
| | |- main
| | |- application
| | |- deployment.xml
| |- pom.xml
|- myproject-war
| |- src
| | |- main
| | |- java
| | |- resources
| | |- webapp
| |- pom.xml
|- pom.xml
Configure the parent POM to be an aggregator too:-
<!--?xml version="1.0" encoding="UTF-8"?-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelversion>4.0.0</modelversion>
<groupid>myproject</groupid>
<artifactid>myproject</artifactid>
<packaging>pom</packaging>
<version>1.0</version>
<!-- Make this parent POM to be an aggregator for both EAR and WAR modules -->
<modules>
<module>myproject-ear</module>
<module>myproject-war</module>
</modules>
</project>
When you run mvn clean package on the parent POM, the EAR file will contain the WAR file and the deployment file that inverts the class loader for this application.
Leave a Reply