Migrating from JPA 2.x to 3.0

By Thorben Janssen


I recently got a lot of questions about JPA 3.0, and since EclipseLink and Hibernate now offer full support for it, it’s time to take a closer look at the latest update of the specification. As part of the transformation from Java EE to Jakarta EE, the Java Persistence API (JPA) was renamed to Jakarta Persistence API (JPA). The first step of that process happened with JPA 2.2 and was now completed with JPA 3.0. Let’s take a closer look at the changes introduced in JPA 3.0 and the required migration steps.

It’s different but it’s still the same

If you take a closer look at JPA 3.0, it might seem like nothing has changed. And that’s not entirely wrong. JPA 3.0 brings no new features. It only changes the prefix of all API classes’ package names, the prefix of some configuration properties, and the schema namespaces for all XML-based configuration files. The goal of these changes was to replace the word “java” to avoid legal issues.

The prefix of the package names and configuration parameter names changes from javax.persistence.* to jakarta.persistence.*, e.g., the import required for the @Entity annotation changes from javax.persistence.Entity to jakarta.persistence.Entity and the name of the javax.persistence.schema-generation.database.action property changes to jakarta.persistence.schema-generation.database.action. In addition to that, the XML namespaces changed from http://xmlns.jcp.org/xml/ns/persistence and
http://xmlns.jcp.org/xml/ns/persistence/orm to https://jakarta.ee/xml/ns/persistence and https://jakarta.ee/xml/ns/persistence/orm.

These changes complete the transition process from Java EE to Jakarta EE. Unfortunately, they also require you to migrate your code and configuration files.

Frameworks Implementing JPA 3.0

The 2 most popular implementations of the JPA specification support version 3.0. If you want to use EclipseLink, you need to use at least version 3.0.

<dependency>
	<groupId>org.eclipse.persistence</groupId>
	<artifactId>org.eclipse.persistence.jpa</artifactId>
	<version>3.0.1</version>
</dependency>

Hibernate started to support JPA 3.0 in version 5.5. All Hibernate artifacts that support the new JPA 3.0 APIs have the suffix “-jakarta” in their name. If you still want to use the old APIs, you can stick to the old artifact names.

<dependency>
	<groupId>org.hibernate</groupId>
	<artifactId>hibernate-core-jakarta</artifactId>
	<version>5.5.2</version>
</dependency>

Migrating an Existing Application

After you updated your dependencies to one of the ones listed above, you need to make a few changes to your import statements and configuration files. But, don’t worry, it’s not as bad as it might seem. You can do most of the work using the search and replace feature in your IDE or by executing a few basic commands on the command line.

Changing JPA-Related Imports

As mentioned earlier, JPA 3.0 didn’t add or change any functionality compared to JPA 2.2. It only changed the package from javax.persistence to jakarta.persistence. Due to that, you can simply replace all occurrences of “import javax.persistence” with “import jakarta.persistence”.

After you did that, the imports of your entity classes should look like the following code snippet, and all your mapping definitions should be unchanged.

import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Version;

@Entity
public class ChessGame {
    
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;

    private LocalDate date;

    private int round;

    @Version
    private int version;

    @ManyToOne
    private ChessTournament chessTournament;

    @ManyToOne(fetch = FetchType.LAZY)
    private ChessPlayer playerWhite;
    
    @ManyToOne(fetch = FetchType.LAZY)
    private ChessPlayer playerBlack;
	
    ...

}

Updating XML Namespaces

If you’re using an orm.xml or a persistence.xml file to define your mapping or configure your persistence unit, you should also update the namespace and schema definitions.

In your persistence.xml configuration, you need to replace http://xmlns.jcp.org/xml/ns/persistence with https://jakarta.ee/xml/ns/persistence.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persistence xmlns="https://jakarta.ee/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             version="3.0"
             xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd">
    <persistence-unit name="my-persistence-unit">
        ...
    </persistence-unit>
</persistence>

And in your orm.xml file, which contains your mapping definitions, you need to replace http://xmlns.jcp.org/xml/ns/persistence/orm with https://jakarta.ee/xml/ns/persistence/orm.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<entity-mappings version="3.0"
	xmlns="https://jakarta.ee/xml/ns/persistence/orm"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
	<entity class="com.thorben.janssen.model.ChessPlayer" name="ChessPlayer">
		...
	</entity>
</entity-mappings>

Migrating Configuration Parameters

The last thing you need to do to migrate your application to JPA 3.0 is to update the names of your configuration parameters. These are usually defined and used in your persistence.xml file, and I explained all of them in great detail in my Beginner’s Guide to JPA’s persistence.xml.

The name of some of these properties includes the prefix “javax.persistence”, e.g., “javax.persistence.jdbc.driver”. For all of these parameters, you need to replace “javax.persistence” with “jakarta.persistence”. After you did that, your configuration looks similar to the following code snippet.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persistence xmlns="https://jakarta.ee/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             version="3.0"
             xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd">
    <persistence-unit name="my-persistence-unit">
        <description>Hibernate example configuration - thorben-janssen.com</description>
        <exclude-unlisted-classes>false</exclude-unlisted-classes>

        <properties>
            <property name="hibernate.jdbc.time_zone" value="UTC"/>

            <property name="jakarta.persistence.jdbc.driver" value="org.postgresql.Driver"/>
            <property name="jakarta.persistence.jdbc.url" value="jdbc:postgresql://localhost:5432/test"/>
            <property name="jakarta.persistence.jdbc.user" value="postgres"/>
            <property name="jakarta.persistence.jdbc.password" value="postgres"/>

            <property name="jakarta.persistence.schema-generation.database.action" value="drop-and-create"/>
            <property name="jakarta.persistence.sql-load-script-source" value="data.sql"/>
        </properties>
    </persistence-unit>
</persistence>

Conclusion

JPA 3.0 changed the packages of all classes, the XML namespaces of the configuration files, and the names of some configuration parameters to complete the transition from the Java Persistence API to the Jakarta Persistence API. Especially the change of the package names might look like a huge change that causes a lot of work in existing projects. But it’s not as bad as it seems. You can easily change the import statements using your IDE’s search and replace feature or a basic shell script.


Tags


About the author

Thorben is an independent consultant, international speaker, and trainer specialized in solving Java persistence problems with JPA and Hibernate.
He is also the author of Amazon’s bestselling book Hibernate Tips - More than 70 solutions to common Hibernate problems.

Books and Courses

Coaching and Consulting

Leave a Reply

Your email address will not be published. Required fields are marked

This site uses Akismet to reduce spam. Learn how your comment data is processed.

{"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}