DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Code Security Remediation: What 50,000 Repositories Reveal About PR Scanning
  • Shrink a Bloated Git Repository and Optimize Pack Files
  • Fast Data Access Part 2: From Manual Hacks to Modern Stacks
  • Using ChartMuseum as a Helm Repository

Trending

  • OpenAPI From Code With Spring and Java: A Recipe for Your CI
  • Detecting Bugs and Vulnerabilities in Java With SonarQube
  • Run Gemma 4 on Your Laptop: A Hands-On Guide to Google's Latest Open Multimodal LLM
  • When Perfect Data Breaks: The Journey from Data Quality to Data Observability
  1. DZone
  2. Data Engineering
  3. Databases
  4. Jakarta Data in Jakarta EE 12 M2: From Repositories to a Unified Data Access Model

Jakarta Data in Jakarta EE 12 M2: From Repositories to a Unified Data Access Model

Jakarta Data in Jakarta EE 12 M2 extends the EE 11 repository model with stateful operations, unified querying, and SQL/NoSQL alignment for domain-centric data access.

By 
Otavio Santana user avatar
Otavio Santana
DZone Core CORE ·
Feb. 10, 26 · Analysis
Likes (1)
Comment
Save
Tweet
Share
1.1K Views

Join the DZone community and get the full member experience.

Join For Free

Enterprise Java persistence has been expanding its scope over the last few releases, slowly but deliberately moving away from the idea that persistence is synonymous with relational databases. With Jakarta EE 11, that shift became explicit through the introduction of Jakarta Data, a specification that standardizes application-level data access across both SQL and NoSQL databases. Jakarta EE 12 M2 builds on that foundation, not by changing direction, but by completing ideas that were intentionally deferred in the previous release.

Jakarta Data did not replace Jakarta Persistence. Instead, it introduced a new abstraction layer, focused on how applications use data rather than how data is stored. This distinction is subtle but fundamental. Jakarta Persistence remains an ORM specification, deeply rooted in relational concepts, SQL semantics, and persistence contexts. Jakarta Data, by contrast, targets a higher level: the repository, where domain logic meets data access.

The inspiration for this model is not accidental. The repository concept in Jakarta Data closely aligns with the Domain-Driven Design definition popularized by Eric Evans, which defines a repository as a collection-like abstraction that mediates between the domain and the data source. Rather than exposing tables, documents, or queries directly, repositories express intent in domain terms.

At a high level, Jakarta Data provides a familiar, Jakarta-based programming model that allows Java developers to interact with relational and NoSQL databases consistently, while preserving the specific strengths of each underlying store. The goal is not to flatten differences, but to consolidate common patterns that appear across persistence technologies into a single, standard API.

To make this concrete, Jakarta Data defines repository abstractions that eliminate significant boilerplate while remaining explicit and type-safe. These repositories fall into two broad categories.

Types of Repositories in Jakarta Data

The first category consists of built-in repository interfaces. At the root of this hierarchy is DataRepository, which is extended by more specialized interfaces such as CrudRepository and BasicRepository. These interfaces provide ready-made support for common data access operations while remaining extensible.

Java
 
@Repository
public interface CarRepository extends BasicRepository<Car, Long> {

    List<Car> findByType(CarType type);

    Optional<Car> findByName(String name);
}


Once defined, the repository can be injected and used directly by application services without additional plumbing:

Java
 
@Inject
CarRepository repository;

Car ferrari = Car.id(10L)
                 .name("Ferrari")
                 .type(CarType.SPORT);

repository.save(ferrari);


This style will feel familiar to developers coming from other ecosystems, but Jakarta Data is careful not to force this approach. Real-world domains often do not map cleanly to one-repository-per-entity designs, especially when operations span multiple aggregates or represent domain actions rather than CRUD semantics.

That leads to the second category: custom repository interfaces.

Custom repositories allow developers to define domain-centric operations explicitly, using lifecycle annotations such as @Insert, @Update, @Delete, and @Save. These repositories act as a bridge between domain language and persistence mechanics, without inheriting assumptions from generic repository hierarchies.

Java
 
@Repository
public interface Garage {

    @Insert
    Car park(Car car);
}


@Inject
Garage garage;

Car ferrari = Car.id(10L)
                 .name("Ferrari")
                 .type(CarType.SPORT);

garage.park(ferrari);


This approach tends to resonate strongly with DDD practitioners, as repository methods reflect domain actions rather than data access patterns. Jakarta Data deliberately supports both styles, allowing teams to choose the one that best fits their domain model rather than enforcing a single architectural shape.

Beyond the repositories themselves, Jakarta Data also standardizes several cross-cutting concerns that previously required ad hoc solutions. Pagination is a good example. Jakarta Data supports both offset-based and cursor-based pagination, recognizing that different data stores and workloads have different trade-offs. Offset pagination is simple and familiar, computing pages relative to a fixed offset. Cursor-based pagination, on the other hand, is designed to reduce missed or duplicated results by querying relative to observed values, which is particularly useful for large or frequently changing datasets.

Stateful Repository Operations in Jakarta EE 12

Jakarta Data intentionally avoids competing with frameworks on convenience features that depend on runtime enhancement or framework-specific infrastructure. Its focus is standardization, portability, and architectural clarity at the platform level.

Jakarta EE 12 M2 is where one of the most important deferred capabilities begins to land: stateful repository operations.

In Jakarta EE 11, repositories are stateless by design. Developers explicitly control when entities are saved or updated. Jakarta EE 12 introduces stateful repositories, which are aware of a persistence context, as defined by Jakarta Persistence. These repositories manage entity state transitions automatically and expose lifecycle annotations that mirror familiar JPA operations.

Java
 
@Repository
public interface Products extends DataRepository<Product, String> {

    @Persist
    void add(Product product);

    @Merge
    Product merge(Product product);

    @Remove
    void remove(Product product);

    @Refresh
    void reload(Product product);

    @Detach
    void detach(Product product);
}


Each of these annotations maps to a well-defined persistence operation:

Annotation Meaning
@Persist Inserts a new entity into the data store, immediately or on flush
@Merge Merges entity state, inserting if the entity does not exist
@Remove Deletes the entity from the data store
@Refresh Reloads entity state, discarding unsaved changes
@Detach Detaches the entity from the persistence context


A key design decision here is exclusivity: a repository is either stateless or stateful. Lifecycle annotations for one model cannot be mixed with those of another. This avoids ambiguous semantics and makes repository behavior explicit.

Another important evolution in Jakarta EE 12 is the tighter integration with Jakarta Query. What was previously known as Jakarta Data Query has been moved into Jakarta Query, which defines the minimum common behavior for querying across SQL and NoSQL databases. This separation clarifies responsibilities: Jakarta Data focuses on repositories, while Jakarta Query focuses on querying.

In addition to string-based queries, Jakarta Data also supports dynamic query construction through restrictions. Restrictions provide a fluent, type-safe way to build queries programmatically, without embedding query strings.

Given a repository like this:

Java
 
@Repository
public interface Countries extends CrudRepository<Country, String> {

    @Find
    List<Country> filter(Restriction<Country> filter);
}


Developers can express complex queries fluently:

Java
 
List<Country> result1 =
        countries.filter(Restrict.all(_Country.code.equalTo("MN")));

List<Country> result2 =
        countries.filter(Restrict.all(
                _Country.code.notNull(),
                _Country.code.in("FI", "FR", "GR"),
                _Country.code.notEqualTo("FR")));

List<Country> result3 =
        countries.filter(Restrict.any(
                _Country.code.equalTo("CO"),
                _Country.code.equalTo("MY")));


For cases where a declarative query is clearer, Jakarta Data repositories can also use @Query with the Jakarta Common Query Language (JCQL). JCQL is defined by Jakarta Query and is conceptualized as a subset of JPQL, deliberately constrained to be implementable across diverse data stores.

Java
 
@Repository
public interface BookRepository extends BasicRepository<Book, UUID> {

    @Query("where title like :titlePattern")
    List<Book> booksMatchingTitle(String titlePattern);

    @Query("where author.name = :author order by title")
    List<Book> findByAuthorSortedByTitle(String author);
}


This design ensures that developers familiar with JPQL feel immediately at home, while still allowing the same query model to apply to non-relational databases.

Jakarta Data’s evolution in Jakarta EE 12 M2 does not radically change the specification’s direction. Instead, it completes the picture that began in Jakarta EE 11. Repositories move from purely stateless abstractions to persistence-context-aware components. Querying is clarified and centralized. Dynamic and declarative approaches coexist. Most importantly, the specification continues to focus on consolidating what SQL and NoSQL databases have in common, without erasing what makes them different.

Jakarta Data is not about hiding databases. It is about giving enterprise Java developers a consistent, domain-centric way to work with data, regardless of where it resides. Jakarta EE 12 is where that vision starts to feel whole.

Data access Repository (version control)

Opinions expressed by DZone contributors are their own.

Related

  • Code Security Remediation: What 50,000 Repositories Reveal About PR Scanning
  • Shrink a Bloated Git Repository and Optimize Pack Files
  • Fast Data Access Part 2: From Manual Hacks to Modern Stacks
  • Using ChartMuseum as a Helm Repository

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook