neo4j-contrib/neo4j-skills4 files

Neo4j Driver Java Skill

Neo4j Java Driver v6 — driver lifecycle, Maven/Gradle setup, executableQuery,

Specification
Skill ID
neo4j-contrib/neo4j-skills/neo4j-driver-java-skill
Publisher
neo4j-contrib
Repository
neo4j-skills
Installs
0
Files
4
Synced
Sep 23, 2026
How to use it

Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.

neo4j-contrib/neo4j-skills/neo4j-driver-java-skillInstalls these files
  • README.md
  • SKILL.md
  • references/advanced-config.md
  • references/async-reactive.md

What this skill tells the agent

When to Use

  • Java/Kotlin code connecting to Neo4j (Aura or self-managed)
  • Setting up driver, sessions, transactions in Maven/Gradle projects
  • Debugging result handling, error recovery, connection pool issues
  • Async (CompletableFuture) or reactive (Project Reactor / RxJava) Neo4j access

When NOT to Use

  • Cypher query authoring/optimization → neo4j-cypher-skill
  • Driver version upgrades → neo4j-migration-skill
  • Spring Data Neo4j (@Node, @Relationship, Neo4jRepository) → neo4j-spring-data-skill

Dependency

Maven

<dependency>
    <groupId>org.neo4j.driver</groupId>
    <artifactId>neo4j-java-driver</artifactId>
    <version>6.2.1</version>
</dependency>

Gradle

implementation 'org.neo4j.driver:neo4j-java-driver:6.2.1'

Check latest: https://central.sonatype.com/artifact/org.neo4j.driver/neo4j-java-driver

6.2.0 [2026-06]: Neo4j UUID type + Bolt 6.1 support; QueryProfile in result summary. 6.2.1 [2026-08]: fixes Value#asObject() on UUID values — required if reading UUID properties generically. Neo4j 2026.08 server bundles 6.2.1; 2026.07 bundles 6.2.0.

Server-side UUID type and uuid() constructors ship in Neo4j 2026.08 — use driver >= 6.2.1 against those servers.


Environment Variables

Standard pattern for connection config — never hardcode credentials:

String uri      = System.getenv().getOrDefault("NEO4J_URI",      "neo4j://localhost:7687");
String user     = System.getenv().getOrDefault("NEO4J_USERNAME",  "neo4j");
String password = System.getenv().getOrDefault("NEO4J_PASSWORD",  "");
String database = System.getenv().getOrDefault("NEO4J_DATABASE",  "neo4j");

Spring Boot: inject via @Value("${spring.neo4j.uri}") or application.properties:

spring.neo4j.uri=neo4j+s://xxx.databases.neo4j.io
spring.neo4j.authentication.username=neo4j
spring.neo4j.authentication.password=secret

Driver Lifecycle

One Driver per application — thread-safe, expensive to create. Implement AutoCloseable or use try-with-resources.

// Long-lived singleton
var driver = GraphDatabase.driver(
    "neo4j+s://xxx.databases.neo4j.io",          // Aura TLS+routing
    AuthTokens.basic(user, password));
driver.verifyConnectivity();                      // fail fast

// Short-lived (tests / CLI)
try (var driver = GraphDatabase.driver(uri, AuthTokens.basic(user, password))) {
    driver.verifyConnectivity();
    // ...
}

URI schemes:

URIUse
neo4j://localhostUnencrypted, cluster routing
neo4j+s://xxx.databases.neo4j.ioTLS + cluster routing (Aura)
bolt://localhost:7687Unencrypted, single instance
bolt+s://localhost:7687TLS, single instance

Auth options: AuthTokens.basic(u,p) · AuthTokens.bearer(token) · AuthTokens.kerberos(b64) · AuthTokens.none()


Choosing the Right API

APIWhenAuto-retryStreaming
driver.executableQuery()Default for most queries✅❌ eager
session.executeRead/Write()Large results, callback control✅✅
session.beginTransaction()Multi-method, external coordination❌✅
session.run()Self-managing queries (CALL IN TRANSACTIONS)⚠️ one-shot [6.1+]✅
driver.asyncSession()Non-blocking CompletableFuture✅✅
driver.rxSession()Reactor/RxJava backpressure✅✅

CALL { … } IN TRANSACTIONS and USING PERIODIC COMMIT self-manage their transaction — use session.run() only. executableQuery and executeRead/Write will fail for these queries.

session.run() retry [6.1+]: single immediate retry on idempotent errors only (enabled by default). Disable per driver or per session:

// Driver-level — disable for all sessions
var config = Config.builder().withAutoCommitRetriesDisabled(true).build();

// Session-level — overrides driver
var sessionConfig = SessionConfig.builder()
    .withAutoCommitRetriesMode(AutoCommitRetriesMode.DISABLED)  // DEFAULT = follow driver
    .build();

executableQuery — Default

// Read — route to replicas
var result = driver.executableQuery("""
        MATCH (p:Person {name: $name})-[:KNOWS]->(friend)
        RETURN friend.name AS name
        """)
    .withParameters(Map.of("name", "Alice"))
    .withConfig(QueryConfig.builder()
        .withDatabase("neo4j")            // always specify — avoids home-db round-trip
        .withRouting(RoutingControl.READ)
        .build())
    .execute();

result.records().forEach(r -> System.out.println(r.get("name").asString()));
long ms = result.summary().resultAvailableAfter(TimeUnit.MILLISECONDS);

// Write
driver.executableQuery("CREATE (p:Person {name: $name, age: $age})")
    .withParameters(Map.of("name", "Bob", "age", 30))
    .withConfig(QueryConfig.builder().withDatabase("neo4j").build())
    .execute();

Never string-interpolate Cypher. Always .withParameters(Map.of(...)).


Managed Transactions (executeRead / executeWrite)

Sessions are NOT thread-safe — one per request/thread, always close.

try (var session = driver.session(SessionConfig.builder()
        .withDatabase("neo4j").build())) {

    // Read → replica routing
    var names = session.executeRead(tx -> {
        var result = tx.run(
            "MATCH (p:Person) WHERE p.name STARTS WITH $prefix RETURN p.name AS name",
            Map.of("prefix", "Al"));
        return result.stream().map(r -> r.get("name").asString()).toList(); // collect INSIDE
    });

    // Write → leader routing
    session.executeWriteWithoutResult(tx ->
        tx.run("CREATE (p:Person {name: $name})", Map.of("name", "Carol"))
    );
}

Result must be consumed INSIDE the callback

Result is a lazy cursor tied to the open transaction. Transaction closes when callback returns — any read after that throws ResultConsumedException.

// ❌ Returns Result — already closed by the time caller uses it
var result = session.executeRead(tx ->
    tx.run("MATCH (p:Person) RETURN p.name AS name"));
result.stream().forEach(...); // throws ResultConsumedException

// ✅ Collect to List inside callback
var names = session.executeRead(tx ->
    tx.run("MATCH (p:Person) RETURN p.name AS name")
      .stream().map(r -> r.get("name").asString()).toList());

Callback rules

  • Consume each Result before next tx.run() — multiple open cursors = undefined behaviour.
  • No side effects (HTTP, email, metric increments) — callback may be retried on transient errors.
  • Use MERGE (idempotent), not CREATE, for retry-safe writes.
  • executeRead → replica; executeWrite → leader.

TransactionConfig — timeouts & metadata

var config = TransactionConfig.builder()
    .withTimeout(Duration.ofSeconds(5))
    .withMetadata(Map.of("app", "myService", "user", userId))  // visible in SHOW TRANSACTIONS
    .build();
session.executeRead(tx -> { /* ... */ }, config);

Explicit Transactions

Use when work spans multiple methods or requires external coordination. Not auto-retried.

try (var session = driver.session(SessionConfig.builder().withDatabase("neo4j").build())) {
    var tx = session.beginTransaction();
    try {
        doPartA(tx);
        doPartB(tx);
        tx.commit();
    } catch (Exception e) {
        try { tx.rollback(); } catch (Exception rb) { e.addSuppressed(rb); }
        throw e;
    }
}

tx.rollback() is a network call — wrap in its own try/catch and use addSuppressed so the original exception is not lost.

Commit uncertainty: if tx.commit() throws ServiceUnavailableException, the commit may or may not have succeeded. Design writes as idempotent (MERGE + unique constraints) so retrying is safe.

Choose explicit vs managed:

  • Auto-retry needed → executeRead / executeWrite
  • Work spans multiple methods → explicit (pass tx as parameter)
  • Coordinating with external I/O → explicit (commit only after I/O succeeds)

Use this skill in a project

Open any RiverX project, search the Skills panel for neo4j-contrib/neo4j-skills/neo4j-driver-java-skill, and install it in one click.

Use this skill