EclipseStore: The Evolution of Java Persistence

Markus Kett

Database development in Java has always been a challenge, often feeling like a battle against mismatched paradigms. From the start, developers wrestled with complex mappings and performance bottlenecks when integrating Java with databases. EclipseStore, an open-source project under the Eclipse Foundation, was created to change this. Its mission is to deliver a Java-native persistence layer that feels like an integral part of the language, not a workaround for database limitations. By storing Java objects directly, it aims to simplify and accelerate development, aligning persistence with Java’s core strengths.

What We Love About Java

Java developers cherish the language for its object-oriented programming, type safety, and intelligent tooling. Features like code completion, compiler checks, and elegant APIs make coding intuitive and reduce potential errors. Clean and testable code is highly valued in the Java community. However, these strengths often vanish in database programming, where developers face clunky APIs and error-prone string-based queries statements that are neither type safe nor enable code completion, compiler checks, and DBUnit testing. In many aspects, the elegance of Java’s ecosystem and clean code standards are missing. At many Developers miss at various points the elegance of Java’s ecosystem and clean code.

What We Get with Databases

Databases operate on fundamentally different principles from Java, supporting SQL, stored procedures, functions, or PL/SQL, and numerous additional data types unknown in Java. That’s why the strengths of Java often vanish in database programming, where developers face clunky APIs and error-prone string-based queries statements that are neither type safe nor enable code completion, compiler checks, and DBUnit testing. In many aspects, the elegance of Java’s ecosystem and clean code standards are missing. At many Developers miss at various points the elegance of Java’s ecosystem and clean code.

Biggest Pain-Points with RDBMS

Relational Database Management Systems (RDBMS) pose significant challenges for Java developers. Mapping Java objects to relational tables requires intricate Object-Relational Mapping (ORM) frameworks, which introduce complexity and maintenance overhead. Query performance suffers due to latency from data conversions and network round-trips. Joins across multiple tables can cripple runtime performance, especially in high-load scenarios. Schema migrations are error-prone, often requiring manual intervention. Concurrency handling demands careful locking strategies, complicating multi-user applications. The mismatch between Java’s object model and relational structures creates a persistent “impedance mismatch.” These issues slow development and inflate costs, frustrating developers seeking seamless integration.

Persistence History in Java

In Java’s early days, database programming was rudimentary, relying on JDBC with raw SQL queries that were error-prone and hard to maintain. Enterprise JavaBeans (EJB) 1.0, introduced in 1998, aimed to simplify persistence but brought heavy complexity, with verbose XML configurations and poor performance. Hibernate, launched in 2001, revolutionized the field by mapping Java objects to relational tables, abstracting SQL, and offering a more natural way to store objects. The Java Persistence API (JPA), standardized in 2006, built on Hibernate’s ideas, providing a vendor and database-neutral ORM standard. The goal of these abstractions was to let developers focus on storing objects, not wrestling with database specifics. Spring Data, introduced in 2011, and Jakarta Data, evolving from Java EE, added higher-level abstractions, simplifying repository-based access. These tools made development more comfortable, but drawbacks persist: ORM frameworks still require mappings, introduce latency, and struggle with complex object graphs. Performance overhead from data conversions remains a challenge, and schema migrations can still disrupt workflows. Despite progress, the dream of truly Java-native persistence remained elusive.

NoSQL Databases

NoSQL databases emerged in the 2000s to address RDBMS limitations, prioritizing scalability and flexibility over strict consistency. They introduced new data models like key-value stores, JSON documents, and graphs, better suited for unstructured or semi-structured data. JSON document databases became very popular. However, Java developers face pain points also with NoSQL: APIs often require JSON conversions, breaking Java’s type safety, and complex queries lack the elegance of Java’s programming model. Key-value databases often force Java developers to manually handle object serialization/deserialization and lack built-in support for complex relationships and traversals that are natural in Java’s object-oriented and graph-based models. This leads to more code for managing data structures and querying. Graph databases require Java developers to learn proprietary graph models and query languages, differing from Java’s object-oriented approach. While OGMs exist, they introduce mapping complexity between Java object graphs and the database’s node-relationship structure, leading to a different paradigm for data modeling and retrieval. The shift to NoSQL solved some scalability issues but introduced new integration challenges for Java applications.

Introduction to EclipseStore.io

EclipseStore is a Java-native persistence framework designed to work as developers expect from their language. It uses the native Java object model, supporting any Java type, including complex object graphs of any size and depth, with full support for inheritance, polymorphism, and circular references. The primary goal is to store any Java object graph, however intricate, and restore it in RAM, either fully or partially, as needed. Objects can remain in memory for as long as required, and developers can remove them when RAM is scarce, with lazy-loading ensuring only necessary data is loaded. By eliminating mappings and conversions, EclipseStore avoids the latency of ORM frameworks and JSON-based NoSQL systems.

To achieve this, EclipseStore serializes objects directly to disk using the Eclipse Serializer, a custom-built component addressing the flaws of Java’s built-in serialization. The Eclipse Serializer is faster, handles complex object graphs effortlessly, and is highly secure, preventing malicious code injection by never executing code during deserialization. EclipseStore is open-source under the Eclipse Public License (EPL) 2.0, available on Maven Central, and integrates with frameworks like Micronaut, Spring Boot, Quarkus, and Helidon.

Java Object Graph

A Java object graph is a network of interconnected objects, where each object may reference others, forming a structure of arbitrary complexity. For example, a BookStore object might contain a List<Book>, where each Book has an Author and a List<Review>, with circular references (e.g., a Book referencing its Author, and the Author referencing their Books). EclipseStore requires a root object that serves as the entry point for the persisted objects. All objects that are reachable from the root object can be persisted. Vice versa, when you want to retrieve your persisted data, you retrieve it by accessing this root object. All other objects in the graph are then reachable from the root. EclipseStore lets developers design such models freely, without restrictions like annotations, specific interfaces, or external configurations. Any object model that works in Java can be stored by EclipseStore, preserving the full object graph, including relationships and state, for seamless persistence.

Store Operation and Storage

EclipseStore stores objects in an open binary format using the Eclipse Serializer. Objects are appended to the storage files, never overwritten, ensuring data integrity. Each store operation is a single method call, a blocking, atomic all-or-nothing operation, with a transaction journal guaranteeing ACID compliance and full consistency. For example:

storageManager.store(myObjectGraph);

Developers can choose what to store – full graphs, subgraphs, or specific objects – offering flexibility. Storage targets include local disks, mounted volumes, cloud object storage like AWS S3, or even traditional databases; however, they just store binaries without mapping to their data models. As EclipseStore just writes binary data directly to a storage medium supporting multithreaded IO operations, write performance and data throughput are extremely high and almost equivalent to the medium’s performance.

Loading Objects in RAM

At system startup, EclipseStore loads only the object IDs of the entire object graph into RAM, restoring the graph’s structure without loading data. Developers can configure eager loading for instantly needed objects or lazy loading for on-demand access, making EclipseStore efficient even on infrastructure with limited RAM. Objects are written directly to the heap, under the JVM’s control, ensuring standard garbage collection applies.

Access to EclipseStore

Access from external applications is possible through the implementation of REST endpoints or GraphQL interfaces. Both variants are supported by most applications, such as data analytics and BI tools. EclipseStore provides a REST interface and a tool with an UI for accessing the persistent binary data in the storage. For easy data migration, the framework offers CSV import and export.

Exploring Potential Challenges and Solutions

EclipseStore addresses several challenges to deliver robust persistence:

  • Schema Migration: When classes evolve, legacy type mapping ensures compatibility.
  • Storage Growth: EclipseStore’s housekeeping and garbage collection processes remove unreachable or corrupt objects from storage, preventing endless growth. Behind the scenes, a garbage collector constantly deletes corrupt or outdated files, maintaining efficiency.
  • Limited RAM: Lazy-loading ensures only necessary objects are loaded, allowing EclipseStore to handle databases larger than available RAM. Developers can define which subgraphs load on-demand, optimizing memory usage.
  • Concurrency in Multi-User Apps: In multi-user applications, all users access a single object graph in RAM. EclipseStore’s Locking API provides fine-grained locking to prevent conflicts. Developers can also use any standard Java concurrency concept, like synchronized blocks, reentrant locks, or ConcurrentHashMap, for robust handling.
  • I/O Overhead: Critics argue that I/O overhead could be high for large collections, but EclipseStore mitigates this with its optimized binary format and lazy-loading. Unlike traditional databases, which process queries on servers and transfer results, EclipseStore loads only required objects directly into RAM. Also, special pageable collections minimizing I/O. There is also an extension providing a powerful indexing concept comparable to database indexing.
  • RAM Management: Developers can manually remove objects from RAM or set timeouts, freeing memory without affecting storage. Objects are only removed from storage when no longer reachable by the JVM.
  • Indexing and GigaMap: For massive datasets, MicroStream’s enterprise extension offers indexing, enabling searches across billions of objects in milliseconds using off-heap bitmap indexes. The GigaMap, a high-performance map implementation, supports billions of entries with automated lazy-loading and minimal I/O, ideal for caching and real-time analytics.
  • Distributed Applications: EclipseStore operates within a single JVM, but MicroStream’s Cluster extension enables distributed applications. It supports load balancing, replication, and elastic scalability, available as SaaS or on-premise. Combined with Java Streams and indexing, it transforms EclipseStore into a powerful in-memory data processing and caching platform with ACID-compliant persistence.

Potential Use-Cases for EclipseStore

EclipseStore’s Java-native persistence and high-performance capabilities make it versatile for a range of applications – from monoliths to microservices, GraalVM native images, and even Android devices.

It can replace Hibernate in projects needing simple, low-overhead Java object persistence, eliminating complex mappings and delivering microsecond response times. In microservices architectures, where each service requires its own lightweight persistence, EclipseStore shines due to its minimal footprint and seamless integration with frameworks like Quarkus and Spring Boot. It’s ideal for UI data persistence, such as storing user session states or form data in web or mobile apps, where a lightweight storage solution ensures fast response times without database complexity. EclipseStore also enables graph analytics, enabling rapid traversal and querying of complex object graphs, like social network connections or supply chain networks, leveraging the Java’s in-memory efficiency. For data analytics, it accelerates real-time processing of large datasets, such as financial transactions or IoT sensor data, with the GigaMap extension handling billions of records. Fraud detection is another strong use-case, as EclipseStore’s microsecond queries and indexing enable real-time analysis of transaction patterns to flag anomalies instantly. Additional applications include caching for high-traffic APIs, event sourcing in domain-driven design, and embedded persistence in IoT devices, where its small footprint and ACID compliance ensure reliability and speed.

Challenges with EclipseStore

The biggest challenges with EclipseStore are not technical but human. EclipseStore represents a paradigm shift in database programming. There’s no longer a relational data model, no classic selects, no traditional database transactions, no SQL – even the entire database server no longer exists. Although EclipseStore is a pure Java approach, even Java developers often find it difficult to let go of the relational model and think completely object-oriented when it comes to data storage. Developers who have already worked with NoSQL databases may find this easier because they have already learned that data processing does not have to be relational. However, once you internalize this, the elegance of the approach becomes clear.

If the EclipseStore approach has to be discussed and defended with database specialists, things can become challenging. Relational databases have been leading the way for decades, ubiquitous, and their success proves them right. Database specialists can also usually point to many years of experience and projects. However, the disadvantages of relational databases and ORM frameworks – such as high complexity and development effort, data model limitations, performance issues, and lack of scalability – are often overlooked. Due to a lack of Java knowledge, you may not be able to fully grasp the advantages of EclipseStore. This can make the discussion about EclipseStore very difficult.

Conclusion

As Java celebrates 30 years, EclipseStore emerges as a transformative force in persistence, aligning database development with Java’s core strengths. By storing native Java object graphs without mappings or conversions, it eliminates the pain points of RDBMS and NoSQL systems. Its microsecond query times, ACID compliance, and support for diverse storage targets make it ideal for cloud-native microservices, monoliths, and edge devices. With features like lazy-loading, secure serialization, and enterprise-grade indexing, EclipseStore empowers developers to build faster, simpler, and more cost-effective applications. As Java evolves, EclipseStore ensures persistence feels like a natural extension of the language, ready for the next 30 years of innovation.

  • EclipseStore website: www.eclipsestore.io
  • Code on GitHub: https://github.com/eclipse-store/store
  • Getting started: https://docs.eclipsestore.io/manual/storage/getting-started.html
  • Indexing and distributed EclipseStore apps: https://microstream.one/

Total
0
Shares
Previous Post
Restate microservice orchestration

The Simplest Way to Build Resilient Applications

Next Post

Jump 20 years of Java with Migration Engineering

Related Posts