rgoussu@goussu: ~/library/java/storage
~/library/java/storage cat graph-databases.md

Graph databases from Java

# Neo4j via Bolt, Cypher, Spring Data Neo4j and OGM, TinkerPop/Gremlin as the vendor-neutral layer, and when graphs beat recursive SQL.

Conceptsaved 2026-08-09 #java#storage#graph#neo4j#gremlin

Overview

Graph access from Java has one dominant vendor stack and one vendor-neutral one. Neo4j's world is the Bolt binary protocol, Cypher as the query language, and Spring Data Neo4j (with the Neo4j-OGM lineage) for object mapping. Apache TinkerPop's Gremlin is the portable alternative: a traversal DSL embedded in Java that runs against JanusGraph, Amazon Neptune, and other TinkerPop-enabled stores. The prior question is whether the data earns a graph store at all — a few self-joins do not; variable-depth traversals over rich relationships do.

Key points

  • Neo4j Java driver: official neo4j-java-driver speaks Bolt; Session/Transaction API, parameterized Cypher ($param — never string-concatenate), sync and reactive modes, routing awareness for clusters.
  • Cypher is declarative pattern matching: MATCH (a:Person)-[:KNOWS*1..3]->(b) RETURN b expresses a bounded variable-depth traversal in one line — the query shape that is painful in SQL.
  • Spring Data Neo4j (SDN): @Node/@Relationship mapped classes, repositories with derived finders and @Query Cypher, Neo4jTemplate underneath; current SDN integrates the mapping layer directly (the standalone Neo4j-OGM remains for non-Spring apps — session-based, JPA-flavoured).
  • Quarkus and Micronaut: both integrate the official driver (Quarkiverse Neo4j extension, micronaut-neo4j-bolt) — injection, health checks, config; object mapping is bring-your-own (OGM or hand-rolled mappers).
  • TinkerPop/Gremlin: GraphTraversalSource g with traversals like g.V().has("name","alice").out("knows").values("name"); runs embedded or against a remote Gremlin Server. The JDBC-of-graphs: same code targets JanusGraph, Neptune, and others.
  • JanusGraph (open source, pluggable Cassandra/HBase backends + Elasticsearch indexing) and Neptune (managed, both Gremlin and openCypher endpoints) are the main TinkerPop targets from Java.
  • Testing: Neo4j test harness (neo4j-harness) runs an embedded in-JVM server for tests; Testcontainers' Neo4j module is the closer-to-production alternative. TinkerGraph is the in-memory TinkerPop reference graph for Gremlin unit tests.
  • When a graph earns its keep: variable/unbounded traversal depth, path queries (shortest path, reachability), and relationship-heavy models queried in many directions. Fixed shallow joins and hierarchies are fine in SQL — WITH RECURSIVE handles trees; graphs win when depth, direction, and relationship properties dominate the queries.

Details

The two stacks

Neo4j stack TinkerPop stack
Protocol Bolt Gremlin Server (WebSocket) or embedded
Query language Cypher (declarative) Gremlin (imperative traversal DSL in Java)
Object mapping Spring Data Neo4j, Neo4j-OGM none standard — traversal results mapped by hand
Stores Neo4j (also openCypher on Neptune, Memgraph) JanusGraph, Neptune, TinkerGraph
Portability one vendor (openCypher partially portable) vendor-neutral by design

Choosing between them

  • Team writes queries analysts can read → Cypher. Need store portability or already on Neptune/JanusGraph → Gremlin. Gremlin's fluent Java API type-checks at compile time, which suits complex programmatic traversal building; Cypher strings are data.

Examples

// Neo4j driver: parameterized Cypher over Bolt
try (var session = driver.session()) {
    var names = session.executeRead(tx -> tx.run(
            "MATCH (p:Person {id: $id})-[:KNOWS*1..2]->(f) RETURN DISTINCT f.name AS name",
            Map.of("id", personId))
        .list(r -> r.get("name").asString()));
}
// Gremlin: same traversal, vendor-neutral, compile-time checked
List<Object> names = g.V().has("Person", "id", personId)
    .repeat(out("knows")).times(2).emit()
    .values("name").dedup().toList();

Related