"The Jakarta Data specification provides an API to simplify data access. It enables Java developers to focus on the data model while delegating the complexities of data persistence…" The official goal of the specification could also be summarized as an attempt to standardize the good parts of libraries like Spring Data and DeltaSpike Data (repositories, pagination, etc.) for Java developers.
I had the pleasure of visiting JFokus 2025 in Stockholm—though only as a booth babe. Since it's a conference packed with quality content, the expo area is technically dead during presentations, so I took full advantage of my presence by attending a few sessions. The one I enjoyed the most was about Jakarta Data. Not necessarily because I agreed with everything Gavin King, "the father of Hibernate," had to say, but probably at least partly due to his somewhat controversial style, which forced me to rethink some of my (perhaps too well-established) middle-aged conventions. 😍 It was also entertaining to hear the story of why somebody who didn’t really value the whole pattern 4 years ago is now, at least to some extent, promoting Jakarta Data.
I took some notes from the presentation and experimented a bit with Hibernate Data Repositories (the only implementation of the specification so far). Here is a summary of my thoughts on Jakarta Data. I built my Vaadin integration example on top of Gavin's original Quarkus example (with some extensions and an actual functional UI instead of just a limited REST API). Although the example is based on Quarkus, Hibernate Data Repositories can also be used with traditional Jakarta EE or Spring projects.
So, what is "wrong" with the old repositories?
Gavin didn't want to say the name aloud, but I’m controversial enough to do it: Spring Data (JPA) is the leading Java library implementing the "repository pattern." I've long been a big fan of it and will probably continue to use it for many Spring Boot-based projects (if they are locked to RDBMS). However, Gavin’s presentation made some solid points about why the repository pattern—and its "leading Java implementation"—might be more popular than it actually "deserves."
One of his arguments was that, for example, the EntityManager
itself (from the JPA specification) is already a kind of generic repository. The repository pattern just adds "artificial typing" on top of it. Maybe he has a point, but I believe that simple things should be simple. When it comes to JPA (and relational database access in general), I personally welcome any simplifications that are available. 😊
The method name-based queries are the "magic" that makes most developers (myself included!) enthusiastic about Spring Data JPA. I love (and hate) relational databases so much that I always feel immensely satisfied when I can avoid writing an SQL/JPQL query—one way or another. Name-based queries provide an easy way to achieve that. However, they have their limitations, and once you start using them, it’s too easy to exceed those limits. My custom query methods have become unreadable due to overly long method names—when I should have already switched to writing actual queries instead (Spring Data JPA is very good for that as well!). Debugging issues in these naming-based methods is also a pain, often requiring multiple application or test restarts.
Although Gavin augmented the problems well with method name-based queries, I think I’ll still keep them in my toolchain in trivial cases. As long as the method names stay short, they often provide both a descriptive name for the method and, at the same time, reveal exactly what kind of query against the database will be executed.
Gavin’s main hypothesis on why the repository pattern is so popular convinced me: repositories provide a convenient way to store and organize your queries—regardless of the approach used to define them. The problem, however, is where to place the most interesting (and often most important) queries that span multiple entities. 🤷♂️
The good parts of Jakarta Data
A feature I know some of my colleagues would appreciate is the reduced reliance on (naming) convention-based magic. Specifically, Jakarta Data does not support name-based queries, allowing you to name your methods as descriptively as you wish. However, it still provides a similar experience to "auto-generating" queries using parameter names, return types, and some annotations. For example, a query likeSELECT book WHERE isbn = ?
can be achieved with the following repository method (taken from Gavin's GitHub example):
@Find
Optional<Book> byIsbn(String isbn);
Although Jakarta Data offers a slightly different approach to the "magic to avoid writing simple queries," I suspect it can also be severely overused. When your query method starts to become more complex, like with Spring Data’s name-based queries, it is probably best to turn to actual query languages (which are naturally supported as well) sooner rather than later.
With my limited experience so far, my favorite feature of Jakarta Data—or technically Hibernate Data Repositories—is that its "magic" that generates the actual implementations is implemented with Java Annotation Processing. Infamously, Spring Data creates its implementations dynamically at runtime. Fixing small typos in name-based query methods often requires restarting the application. With annotation processing, at least some of these errors can be caught at compile time (as long as your IDE is properly set up). Changing, for example, the parameter name from where the property name for the predicate is derived will immediately show a compilation error in your IDE.
A Jakarta Data repository is defined with a simple interface and the @Repository
annotation. One benefit of this approach is that it prevents developers from accidentally exposing queries that should never be used in a particular application context.
As there is no mandatory super interface with an entity type parameter to extend from, Jakarta Data does not force you to organize your queries by entity type. In one of Gavin’s examples, a repository named "Library" contained queries for both Book
and Author
entity types. Some DDD enthusiasts might find this controversial, but I really like the approach and flexibility it gives me as a developer. In many cases, it might be a better way to organize queries. Whether the terminology is "correct" or not, you now have the flexibility to structure your queries as you see fit. Options include:
- One repository per entity
- One repository for all entities
- A repository dedicated to a group of related entities
If you’ve recently used, for example, Spring Data (JPA) repositories, you might quickly start missing the "queries you get for free" from its super interfaces. Although Gavin didn’t emphasize them much, Jakarta Data does provide CrudRepository
and BasicRepository
interfaces, which probably cover the most common requirements. In my example, I tested the CrudRepository
on the Author entity type and built a fully functional CRUD UI on top of it. With an empty interface like below, you get DAO methods like insert(T), save(T), delete(T), findAll(PageRegues, Sort).
@Repository
@Transactional
public interface AuthorRepository extends CrudRepository<Author, String> {}
More as a "good to know" rather than a “good part”: Hibernate Data is built on top of Hibernate’s StatelessSession. This improves performance in certain cases, but for example, some common workarounds for handling lazy loading may no longer work as you have learned.
Would I use Jakarta Data?
Absolutely—in certain circumstances! My initial reaction is that Jakarta Data is yet another example of the principle that all good ideas are borrowed. It takes a fresh approach to solving the same problems that libraries like Spring Data and DeltaSpike Data have already tackled well, and based on my early testing, some aspects feel better.
Choosing between them is subjective, and I don’t have enough experience to have an opinion yet. However, keep in mind that Jakarta Data is still in its early stages! That can be both a good and bad thing. If you're in the Spring ecosystem, the maturity and wealth of expertise around Spring Data are hard to ignore. However, as developers, we are always eager to learn new things, and Jakarta Data certainly has its merits.
In my fairly simple test app, I quickly started to miss support for EntityGraphs
or some other way to load (lazy-loaded) relations without manually defined join queries. There is a feature for this in Hibernate Data APIs, but it is not covered by the standard. If you want to adopt Jakarta Data today, you’re probably better off by mentally using Hibernate Data (with its Hibernate-specific features) rather than sticking strictly to the standard Jakarta Data API - similar to how we used to work with JPA in its early days.