The amount of data collected by applications nowadays is growing rapidly. Many of them need to support both relational SQL and non-relational NoSQL databases. Jakarta Data provides an API to allow easy data access. Developers can split the persistence mechanism and the model using common features like the Repository pattern and seamlessly switch between SQL and NoSQL databases or even use both in the same application.
What are NoSQL Databases?
NoSQL databases, also called “not only SQL”, “non-Relational” or “non-SQL”, are a type of database management system, where storage and querying data are performed differently from traditional ways used in relational databases.
Instead of the tabular structure of a relational database with tables and columns, NoSQL databases often use different data structures, like a JSON document or a graph structure. As the non-relational database design does not require a schema, it offers greater flexibility and scalability to manage large and usually unstructured data.
NoSQL also works well for distributed databases, where data is copied and stored on multiple servers, either locally or remotely. This improves availability and reliability of data. If some data is unavailable, the rest of the database can continue to run. While most relational database systems won’t work anymore, if say the list of schemas or tables is missing or corrupted.
Features of NoSQL Databases
While relational databases optimize storage and avoid redundancy, NoSQL databases can handle large amounts of unstructured or semi-structured data, offering scalability and flexibility for modern distributed applications.
The key features of NoSQL databases are:
- Flexible data structures and dynamic schemas instead of fixed schemas
- Horizontal scalability
- Low latency
- Large number of concurrent users and data volumes
- Distributed nature
- Higher performance and speed
Types of NoSQL Databases
The main types of NoSQL databases are:
- Key-Value stores
- Column-oriented
- Document stores
- Graph databases
- Time series
Key-Value Stores
Key-value stores (databases) are the simplest type of NoSQL database. Data is stored in key-value pairs, essentially like a Map in the Java Collections API.
Examples of Key-value stores are Amazon DynamoDB, S3, Redis, Hazelcast or Memcached
Column-Oriented
Column-oriented databases store data in columns instead of rows, like relational databases do. This type of database is well-suited for mixing strucured, semi-structured and unstructured data, offering high concurrency and scalability.
Examples of Column-oriented databases are Apache HBase, Apache Cassandra, Sxylla, Clouddata, SimpleDB or Azure Cosmos DB.
Document-Oriented NoSQL
Document-oriented NoSQL databases store data in flexible documents, usually in JSON, BSON, or XML format, instead of rigid tables. These documents can have different structures, which is well-suited for applications that require variable data definitions and can enable faster, more agile development cycles.
Examples of document store NoSQL databases include MongoDB, CouchDB, Couchbase Server, Elasticsearch, Google Cloud Firestore, Amazon DocumentDB and DynamoDB.
Document Stores
Document stores allow to store, retrieve and edit documents in different formats like XML or more commonly JSON. Documents can be embedded inside other documents. Document stores are best suited for unstructured data like social media profiles, product sheets or content management.
Popular document stores include MongoDB, Couchbase, Apache CouchDB, Elastic or Oracle NoSQL Database.
Graph Databases
Graph databases allow to store more complex relationships than relational databases. The model graph applies graph theory, data is stored as a network of nodes and edges. Graph databases are well-suited for the “social graph” in social networks, but also AI models, especially Retrieval Augmented Generation (RAG) databases.
Examples for graph databases are Neo4J, ArangoDB, OrientDb, InfoGrid, Sones, HyperGraphDB or JanusGraph.
Time Series
A time series database can be seen as a specialized form of key-value store, with a timestamp as the key, and values associated with that timestamp.
Examples for time series databases are InfluxDB, KairosDB, TimescaleDB, Apache Pinot, Riak-TS or Prometheus.
Multi-Model
Some NoSQL databases combine more than one type, for example:
- OrientDB (Graph, Document)
- Couchbase (Key-value, Document)
- ArangoDB (Document, Graph, Key-value)
- Elasticsearch (Document, Graph)
SQL vs. NoSQL
These are the the main differences between SQL and NoSQL databases:
| Feature | SQL | NoSQL |
| Schema | Fixed | Dynamic or none |
| ACID/BASE | ACID | BASE or limited ACID |
| Data Model | Structured, table-based | Flexible: documents, key-value, graphs |
| Data Structure | Normalized | Denormalized |
| Scalability | Vertical scaling | Horizontal scaling |
| Best Use Cases | Transactional, flow-based applications | Big Data, AI, Real time data analytics, embedded/IoT |
ACID vs. BASE principles:
ACID

BASE

- Atomicity
- Consistency
- Isolation
- Durability
- Basically Available
- Soft state
- Eventual consistency
Here are the most common keywords used by different database types:
| SQL | Key-Value | Column | Document | Graph |
| Table | Bucket | Column family | Collection | |
| Row | Column | Document | Vertex | |
| Column | Key/value pair | Key/value pair | Vertex and Edge property | |
| Relationship | Link | Edge | ||
| Index |
Using JPA for NoSQL databases can be problematic:
- Async Saves
- Aysnc Callback
- Time to Live (TTL)
- Consistency Level
- SQL based
- Diversity of NoSQL
This is where Jakarta Data and Jakarta NoSQL come into play.
Jakarta Data
Jakarta Data version 1.0 was released as part of Jakarta EE 11. It offers simplified data access to both relational and NoSQL databases. Applying common concepts like repositories and custom queries, known from popular data access libraries like Hibernate, Spring Data or Micronaut Data. Most of them support Jakarta Data or plan to do so in future versions.

Key elements of the Jakarta Data API are:
- Repository
- Order
- Query
- Sort
- Page
Repository
Here is a Repository example:
@Repository
public interface Products extends DataRepository<Product, Long> {
List<Product> findByPriceLessThan(float maxPrice);
void save(Product product);
...
}
And how to use it:
@Inject
Products products;
products.save(new Product(1, "$25 gift card", 25.0f));
List<Product> found = products.findByPriceLessThan(100.0f);
Doing the same with the CrudRepository:
@Repository
public interface Products extends CrudRepository<Product, Long> {
List<Product> findByPriceLessThan(float maxPrice);
...
}
Inherits methods like:
save(E entity);
saveAll(Iterable<E> entities);
findAll();
findById(K id);
existsById(K id);
delete(E entity);
deleteById(K id);
...
Allowing a more compact repository implementation.
Ordering and Sorting
An example for Ordering and a dynamic Query:
@Repository
public interface Products {
Product findById(long productId);
@OrderBy("price")
List<Product> findByNameContains(String searchFor);
@Query("UPDATE Product o SET o.price = o.price - (?2 * o.price) WHERE o.id = ?1")
boolean discount(long productId, float discountRate);
void save(Product p);
}
Applying Sort:
found = products.findByNameContains(namePattern, Sort.desc("price"));
Pagination
Jakarta Data supports both offset and cursor pagination.
Here’s an example for offset pagination, using a simple numerical offset:
@Repository
public interface BeerRepository extends BasicRepository<Beer, String> { }
Pageable page = Pageable.ofPage(1).sortBy(Sort.desc("style"));
Page<Beer> page1 = repository.findAll(page);
System.out.println("The first page:");
page1.forEach(System.out::println);
Pageable secondPage = page.next();
Page<Beer> page2 = repository.findAll(secondPage);
System.out.println("The second page:");
page2.forEach(System.out::println);
Cursor-based pagination uses a reference, rather than a simple offset, in this case a cursor to break records into pages:
@Repository
public interface FruitRepository extends BasicRepository<Fruit, String> {
@Find
CursoredPage<Fruit> cursor(PageRequest pageRequest, Sort<Fruit> order);
}
var pageRequest = PageRequest.ofSize(size).beforeCursor(PageRequest.Cursor.forKey(before));
var page = fruitRepository.cursor(pageRequest, DESC);
Event Handling
Jakarta Data further supports persistence life cycle events like:
void afterUpdate(@Observes PostUpdateEvent<Book> event) {
// Perform post-update actions
}
Offering the same functionality without having to write e.g. database triggers.
Jakarta NoSQL
Jakarta NoSQL, while it started earlier than the factored-out Jakarta Data, released its 1.0 version in spring of 2025. The content of Jakarta EE 11 had already been finalized, so Jakarta NoSQL 1.1 aims for inclusion in Jakarta EE 12.
Jakarta NoSQL offers many annotations familiar to those in JPA, making it also blend-in well with Jakarta Data, especially if you switch between SQL and NoSQL databases:
| Annotation | Description |
| @Entity | Defines a class as a persistable entity |
| @Id | Marks the primary key of an entity |
| @Column | Maps a property or field to a database column |
| @Embeddable, @MappedSuperclass | Enable composition and inheritance |
| @Convert | Handles type conversion for persistence |
Repository
Using the compatible implementation Eclipse JNoSQL, you can define a very similar BeerRepository we saw earlier, but for a NoSQL database like the Oracle NoSQL Database:
import jakarta.data.repository.Query;
import jakarta.data.repository.Repository;
import org.eclipse.jnosql.databases.oracle.mapping.OracleNoSQLRepository;
@Repository
public interface BeerRepository extends OracleNoSQLRepository<Beer, String> {
Set<Beer> findByStyle(String style);
@Query("select * from Beer")
Set<Beer> query();
}
Template
In addition to the Repository, Jakarta NoSQL also supports the Template interface to connect Java entities with NoSQL databases. Through this interface, you can perform standard CRUD operations and fluent queries with a consistent API.

Let’s take an example of a Car entity:
@Entity
public class Car {
@Id
private Long id;
@Column
private String name;
@Column
private CarType type;
// Getters and setters...
}
And apply some simple CRUD operations using the template:
@Inject
Template template;
Car car = Car.id(1L)
.name("Ferrari")
.type(CarType.SPORT);
template.insert(car);
Optional<Car> result = template.find(Car.class, 1L);
template.delete(Car.class, 1L);
In addition to basic persistence, the Template interface also offers a typesafe and expressive fluent query API:
List<Car> cars = template.select(Car.class)
.where("type").eq(CarType.SUV)
.orderBy("name").asc()
.result();
template.delete(Car.class)
.where("type").eq(CarType.COUPE)
.execute();
Supported NoSQL Database Types
Eclipse JNoSQL currently supports the three most common types of NoSQL databases: key-value, column-based, document and graph.
Graph Databases
For graph databases JNoSQL builds on top of Apache TinkerPop, by which there are more than 30 supported NoSQL databases as of now.
Custom Annotations
For some NoSQL databases Eclipse JNoSQL offers custom annotations. Here an example for Apache Cassandra.
Entity:
@Entity("god")
public class God {
@Column
private String name;
@UDT("weapon")
@Column
private Weapon weapon;
}
Repository:
@Repository
interface GodRepository extends CassandraRepository<God, String> {
@CQL("select * from God where name = ?")
List<God> findByName(String name);
}
Conclusion
Jakarta Data and Jakarta NoSQL streamline the integration of Java applications with both relational and non-relational databases. Based on active and vibrant communities like Eclipse or Apache TinkerPop, and supported by some of the most popular Java persistence libraries including Hibernate, Micronaut or OpenLiberty, both Jakarta EE specs can help improve your data applications into a vendor-neutral future.

Featured in the special edition
JAVAPRO – Java 25 (Part 2)
Explore in-depth coverage of Java 25 and related topics in the full issue.
Discover the edition →