Using Jakarta Persistence API (JPA) 3.0 in Modern Java Apps

Date: January 25 2024


The release of Jakarta Persistence API (JPA) 3.0 marks a significant step forward for Java persistence, aligning the specification with the Jakarta EE 9+ namespace changes and introducing useful enhancements that modernize and simplify persistence in Java applications.

If you’re building new apps or migrating existing ones, understanding the changes in JPA 3.0 is crucial to leveraging the latest capabilities while ensuring compatibility and maintainability.


1. Jakarta Namespace Migration: The Most Obvious Change

The biggest visible change in JPA 3.0 is the move from javax.persistence to jakarta.persistence. This is part of the larger Eclipse Foundation-led transition from Java EE to Jakarta EE.

Migration Tip:

Update all your import statements:

// Old import
import javax.persistence.Entity;

// New import
import jakarta.persistence.Entity;

The change affects your persistence.xml, annotations, and APIs.


2. New Features in JPA 3.0

a) Support for Java Records as Entities

JPA 3.0 allows Java Records to be used as immutable entities. This modernizes domain modeling by enabling concise immutable data carriers that reduce boilerplate.

Example:

import jakarta.persistence.Entity;
import jakarta.persistence.Id;

@Entity
public record User(@Id Long id, String username, String email) {}

Records provide automatic getters, equals(), hashCode(), and toString(), making entities lightweight and immutable by default.


b) Support for Attribute Converters for Collections

JPA 3.0 enhances AttributeConverter support, enabling easier mapping of collection attributes to database columns using converters.

Example:

If you want to store a list of tags as a comma-separated string:

import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Converter
public class StringListConverter implements AttributeConverter<List<String>, String> {
    @Override
    public String convertToDatabaseColumn(List<String> attribute) {
        return attribute == null ? null : String.join(",", attribute);
    }

    @Override
    public List<String> convertToEntityAttribute(String dbData) {
        return dbData == null ? List.of() : Arrays.stream(dbData.split(",")).collect(Collectors.toList());
    }
}

Usage in entity:

import jakarta.persistence.Convert;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import java.util.List;

@Entity
public class Article {
    @Id
    private Long id;

    @Convert(converter = StringListConverter.class)
    private List<String> tags;

    // getters/setters
}

c) Improved Criteria API

JPA 3.0 adds enhancements to the Criteria API for building type-safe queries, including support for list-valued paths and subqueries improvements.

Example: Using list-valued paths

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Article> cq = cb.createQuery(Article.class);
Root<Article> root = cq.from(Article.class);

cq.where(cb.isMember("java", root.get("tags")));

List<Article> articles = entityManager.createQuery(cq).getResultList();

This queries for articles whose tags collection contains "java".


3. Jakarta Persistence XML Changes

persistence.xml remains essential for configuration, but the schema location and namespace must reflect Jakarta EE:

<persistence 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"
             version="3.0">

    <persistence-unit name="my-unit">
        <class>com.example.User</class>
        <properties>
            <!-- properties -->
        </properties>
    </persistence-unit>
</persistence>

4. JPA 3.0 and Hibernate 6 Compatibility

Hibernate 6 is designed to work with JPA 3.0, so upgrading to Hibernate 6 often means you’re also adopting the Jakarta Persistence API 3.0.

Ensure your Hibernate version and dependencies align with JPA 3.0 to avoid conflicts.


5. Migration Tips for Existing Apps

  • Change all javax.persistence imports to jakarta.persistence.
  • Update your persistence.xml schema location and namespace.
  • Verify third-party dependencies support Jakarta EE 9+ namespaces.
  • Test Java Records entities if you want to adopt them.
  • Refactor attribute converters if needed for new collection support.
  • Adjust your build tool configurations (Maven, Gradle) for Jakarta dependencies.

6. Practical Example: Migrating a Simple Entity

Hibernate 5.x + JPA 2.2

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Customer {
    @Id
    private Long id;
    private String name;

    // getters/setters
}

Hibernate 6.x + JPA 3.0

import jakarta.persistence.Entity;
import jakarta.persistence.Id;

@Entity
public class Customer {
    @Id
    private Long id;
    private String name;

    // getters/setters
}

That’s it! Just a namespace switch — but the implications are vast, so test thoroughly.


7. Conclusion

Jakarta Persistence API 3.0 modernizes Java persistence by:

  • Aligning with Jakarta EE 9+ namespace changes.
  • Adding support for modern Java features like Records.
  • Enhancing attribute converters and the Criteria API.

Whether you’re starting fresh or migrating, embracing JPA 3.0 ensures your app is future-proof, cloud-ready, and able to leverage the evolving Java ecosystem.


Want to dive deeper? Next time, we’ll explore Advanced Hibernate Caching Strategies to supercharge your data layer’s performance!


Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

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