Contents

Building LadybugDB data access layer with Spring Data

The search for a local graph database

While working on my new side project, archiledger, I encountered a specific architectural need. I was looking for a graph database that could run locally, either fully embedded or alongside the application, without requiring a complicated installation process.

After some research, I found that a graph database called LadybugDB (https://ladybugdb.com) is a perfect fit for this use case.

The embedded columnar graph database built for highly regulated industries. Production-ready in minutes.

It’s a relatively new project, currently at version 0.15.1, that is fast and very promising. However, since it is still in its early stages, the provided Java API is quite raw and low-level. I didn’t want to write boilerplate code to map rows to Java objects or handle raw database connections for every query in my MCP server.

To solve this problem, I decided to build a Spring Data module that supports this database: spring-data-ladybugdb.

Spring Data LadybugDB

My goal was simple. I wanted to bring familiar Spring Data patterns to LadybugDB so that I could focus on the logic of my MCP server instead of the database hardwiring.

I also wanted type-safe query building. Since LadybugDB uses Cypher, I integrated support for the Neo4j Cypher DSL. Because the Cypher implementations are so similar, most Cypher queries generated by the Neo4j DSL work completely out of the box with LadybugDB!

The main features currently supported by the integration are:

  • Repository pattern: Spring Data-style NodeRepository for CRUD operations on graph nodes.
  • Template support: LadybugDBTemplate for executing Cypher queries with seamless connection management.
  • Connection pooling: Built-in connection pooling via PooledConnectionFactory (using Apache Commons Pool2).
  • Cypher DSL integration: Direct support for the Neo4j Cypher DSL for type-safe Cypher query building.
  • Entity mapping: Annotation-based entity mapping using @NodeEntity and @Id.
  • Custom queries: Native support for the @Query annotation on repository interfaces, including support for loading LadybugDB extensions, such as vector search.

Early days

The project is currently in its very early stages. For now, it covers the basic set of features I needed to start my Java MCP Memory project. It handles entity mapping, relationship parsing, and basic transactional binding. It also supports enabling extensions for specific sessions, such as vector search.

If you are interested, you can find the repository under its own namespace and use either the LadybugDBTemplate or the SimpleNodeRepository to easily persist your graph entities.

To use it in your project, add the following Maven dependency:

<dependency>
    <groupId>com.thecookiezen</groupId>
    <artifactId>spring-data-ladybugdb</artifactId>
    <version>0.0.1</version>
</dependency>

Here’s a quick look at what the API looks like in practice. Defining an entity is simple. Just annotate your class with @NodeEntity and @Id:

@NodeEntity(label = "Person")
public class Person {
    @Id
    private String name;
    private int age;
    
    // constructors, getters, setters
}

As mentioned, it also supports enabling extensions, such as vector search, for specific sessions via the @Query annotation.

public interface NoteRepository extends NodeRepository<Note, String, Void, Note> {

    @Query(
        value = "MATCH (n:Note) WHERE vector_search(n.embedding, $query, metric := 'cosine') < 0.5 RETURN n",
        loadExtensions = {"vector"}
    )
    List<Note> findSimilarNotes(@Param("query") float[] query);
}

Looking ahead

Although the current version meets my immediate needs, I plan to expand the project with a few exciting ideas.

  1. Migrating to the FFM API: The current underlying driver uses Java Native Interface (JNI) to communicate between Java and the C++ core of LadybugDB. Recent Java versions have advanced to the point that switching to the Foreign Function & Memory (FFM) API (Project Panama) would make the integration much safer and more efficient. It would also completely remove the need for JNI boilerplate.
  2. Static Meta Model: Leveraging the static meta model from the Cypher DSL generator. This allows us to generate domain-specific builders for type-safe query construction at compile time, reducing magic strings even further.
  3. Advanced Relationship Mapping: Expanding the @NodeEntity to better handle complex, nested relationship mapping and cascading saves.
  4. Reactive Support: Depending on the capabilities of the underlying driver in the future, provide a reactive template and repository layer.

Building standard Spring abstractions on top of a cutting-edge embedded database like LadybugDB has been a great side quest. I hope it might be useful to others experimenting with local graph databases in Java.