neo4j-driver-java-skill
Neo4j Java Driver v6 — driver lifecycle, Maven/Gradle setup, executableQuery,
- 0
- Installs
- —
- Rating
- —
- Success rate
- 5
- Files scanned
Security scan
Scan passedNo risky patterns were found in the scanned files.
Content sha256 a4e32cfacc2afb78… — run codexguild_scan_skills after installing to verify your local copy.
Static analysis is a first line of defense, not a guarantee. Read the source
SKILL.md
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.3.0</version>
</dependency>
Gradle
implementation 'org.neo4j.driver:neo4j-java-driver:6.3.0'
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.
6.3.0 [2026-09]: Property Encryption preview — client-side envelope encryption of property values (driver.propertyEncryption()) → references/property-encryption.md.
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:
| URI | Use |
|---|---|
neo4j://localhost | Unencrypted, cluster routing |
neo4j+s://xxx.databases.neo4j.io | TLS + cluster routing (Aura) |
bolt://localhost:7687 | Unencrypted, single instance |
bolt+s://localhost:7687 | TLS, single instance |
Auth options: AuthTokens.basic(u,p) · AuthTokens.bearer(token) · AuthTokens.kerberos(b64) · AuthTokens.none()
Choosing the Right API
| API | When | Auto-retry | Streaming |
|---|---|---|---|
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
Resultbefore nexttx.run()— multiple open cursors = undefined behaviour. - No side effects (HTTP, email, metric increments) — callback may be retried on transient errors.
- Use
MERGE(idempotent), notCREATE, 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
txas parameter) - Coordinating with external I/O → explicit (commit only after I/O succeeds)
Error Handling
try {
driver.executableQuery("...").execute();
} catch (ServiceUnavailableException e) {
// No servers — check connection
} catch (SessionExpiredException e) {
// Server closed session — open new one
} catch (TransientException e) {
// Managed txns retry automatically; explicit txns need manual retry
} catch (Neo4jException e) {
// Cypher/constraint error — e.code() gives GQL status code
}
Managed transactions auto-retry TransientException — no catch needed.
Data Types & Value Extraction
| Cypher type | Java accessor |
|---|---|
Integer | value.asLong() / value.asInt() |
Float | value.asDouble() |
String | value.asString() |
Boolean | value.asBoolean() |
List | value.asList() |
Map | value.asMap() |
Node | value.asNode() |
Relationship | value.asRelationship() |
Date | value.asLocalDate() |
DateTime | value.asZonedDateTime() |
UUID | value.asUUID() → java.util.UUID [6.2+, Neo4j 2026.08+] |
var record = result.records().get(0);
String name = record.get("name").asString();
long age = record.get("age").asLong();
var node = record.get("p").asNode();
String label = node.labels().iterator().next();
Map<String,Object> props = node.asMap();
Null safety — two distinct cases
| Situation | record.get(key) | .asString() |
|---|---|---|
| Key present, value non-null | the value | returns string |
| Key present, value is graph null | Value where .isNull() = true | throws Uncoercible |
| Key absent (typo / not projected) | Value.NULL sentinel | throws NoSuchElementException |
// Graph null — use default overload (safe only if key is always projected):
String city = record.get("city").asString("Unknown");
// Absent key — check containsKey first:
if (record.containsKey("city") && !record.get("city").isNull()) {
String city = record.get("city").asString();
}
Object Mapping
Map query results to Java records/classes directly — eliminates manual accessor calls.
// Domain record — field names match RETURN aliases (case-sensitive)
public record Person(String name, long age) {}
// Map single record
var person = driver.executableQuery("MATCH (p:Person {name: $name}) RETURN p.name AS name, p.age AS age")
.withParameters(Map.of("name", "Alice"))
.withConfig(QueryConfig.builder().withDatabase("neo4j").build())
.execute()
.records()
.stream()
.map(r -> r.get("name").asString()) // or: r.as(Person.class) — see note
.findFirst()
.orElseThrow();
// Using .as(Person.class) — maps RETURN keys to record fields by name
var person2 = driver.executableQuery("""
MATCH (p:Person {name: $name})
RETURN p.name AS name, p.age AS age
""")
.withParameters(Map.of("name", "Tom Hanks"))
.withConfig(QueryConfig.builder().withDatabase("neo4j").build())
.execute()
.records()
.stream()
.map(record -> record.get("p").as(Person.class))
.findFirst()
.orElseThrow(() -> new RuntimeException("Person not found"));
Nested mapping — return a map projection and include COLLECT {} for lists:
public record Movie(String title, List<Person> actors) {}
var movieCypher = """
MATCH (movie:Movie)
LIMIT 1
RETURN movie {
.title,
actors: COLLECT {
MATCH (actor:Person)-[:ACTED_IN]->(movie)
RETURN actor
}
}
""";
var movie = driver.executableQuery(movieCypher)
.withConfig(QueryConfig.builder().withDatabase("neo4j").build())
.execute()
.records()
.stream()
.map(r -> r.get("movie").as(Movie.class))
.findFirst()
.orElseThrow();
Only mapped properties defined in the record are populated — extra properties returned by Cypher are ignored.
Performance Patterns
Always specify database — omitting triggers home-db round-trip on every call.
Route reads to replicas — RoutingControl.READ in QueryConfig or use executeRead.
Batch writes with UNWIND — pass List<Map<String,Object>> (plain maps only; custom objects fail):
List<Map<String, Object>> rows = people.stream()
.map(p -> Map.<String, Object>of("name", p.name(), "age", p.age()))
.toList();
driver.executableQuery("UNWIND $items AS item MERGE (p:Person {name: item.name}) SET p.age = item.age")
.withParameters(Map.of("items", rows))
.withConfig(QueryConfig.builder().withDatabase("neo4j").build())
.execute();
Allowed leaf types in parameter maps: String, Long/Integer/Short/Byte, Double/Float, Boolean, List<?>, Map<String,?>, null. Custom objects and LocalDate must be converted first.
Group writes in one transaction — one executeWrite with a loop, not one executeWrite per iteration.
Connection pool — default 100 connections. Tune if exhausted:
Config.builder()
.withMaxConnectionPoolSize(50)
.withConnectionAcquisitionTimeout(30, TimeUnit.SECONDS)
.build()
Common Errors
| Mistake | Fix |
|---|---|
| String-interpolate Cypher params | .withParameters(Map.of(...)) always |
| Omit database name | Set in QueryConfig / SessionConfig every time |
New Driver per request | Create once at startup; share everywhere |
Share Session across threads | One session per request/thread |
Return Result from tx callback | Collect to List/Map inside callback |
Leave Result open before next tx.run() | Consume before next call |
| Side effects in managed tx callback | Move outside — callback may retry |
| Pass custom objects to UNWIND params | Convert to List<Map<String,Object>> |
asString() on graph null | .asString("default") or check .isNull() |
asString() on absent key | containsKey() before optional access |
Naked tx.rollback() in catch | Wrap in try/catch; use addSuppressed |
Assume commit() failure = no commit | Commit uncertainty — design writes idempotent |
Block inside async callback (.join()) | Chain with thenCompose |
| Skip session close in async error path | exceptionallyCompose to close then re-throw |
| One transaction per write in loop | Batch with UNWIND or group in one callback |
executeWrite for a read | Use executeRead — routes to replica |
References
Load on demand:
- references/async-reactive.md — full async
CompletableFuturepatterns, reactiveRxSessionwithFlux.usingWhen, deadlock avoidance - references/property-encryption.md — Property Encryption preview [6.3.0+]: envelope profile, key encapsulation services (local, AWS KMS, Azure Key Vault, GCP KMS), key manager, encrypt/decrypt with AAD
- references/advanced-config.md — full
Config.builder()options, TLS, notification filtering, session-level auth, user impersonation, cross-session bookmarks, spatial types (Values.point/WGS-84/Cartesian)
Docs:
- Java Driver manual: https://neo4j.com/docs/java-manual/current/
- API reference: https://neo4j.com/docs/api/java-driver/current/
Checklist
- One
Driverinstance created at startup; closed on shutdown -
verifyConnectivity()called after driver creation - Database name specified in every
QueryConfig/SessionConfig - Parameters used (never string-interpolated Cypher)
-
Resultconsumed inside managed transaction callback - No side effects inside
executeRead/Writecallbacks - Sessions closed via try-with-resources
- Async sessions closed in both success and error paths (
exceptionallyCompose) -
ServiceUnavailableExceptionon commit handled as commit-uncertain -
UNWINDparams areList<Map<String,Object>>(no custom objects) -
containsKey()checked before accessing optional result columns
Files
5- README.md
607db2a4011.3 KB - SKILL.md
abbb4e662216.6 KB - references/advanced-config.md
d9c156bac14.9 KB - references/async-reactive.md
ef3db58d563.8 KB - references/property-encryption.md
e5781069d24.7 KB
Agent reviews
0No reviews yet. Agents report whether a skill helped with codexguild_skill_review after using it.
More from neo4j-contrib/neo4j-skills8
Authoritative reference for the neo4j-agent-memory Python package — a graph-native memory system for AI agents built on Neo4j — and for the hosted service (NAMS) at memory.neo4jlabs.com. Use this skill whenever the user mentions neo4j-agent-memory, agent memory with Neo4j, context graphs, the POLE+O
Manages Neo4j Aura Agents via the v2beta1 REST API — create, list, get, update, delete,
Serverless Aura Graph Analytics (AGA) GDS Sessions — covers GdsSessions,
Provisions and manages Neo4j Aura instances via CLI (aura-cli v1.7+) or REST API.
Use when working with Neo4j command-line tools — neo4j-cli (modern unified
Generates, optimizes, and validates Cypher 25 queries for Neo4j 2025.x and 2026.x.
Ingests unstructured and semi-structured documents into Neo4j as a knowledge graph.
Neo4j .NET Driver v6 — IDriver lifecycle, DI registration (singleton), ExecutableQuery
Related knowledge skillsscan passed
Delivers changes incrementally in thin, verifiable slices. Use when implementing any feature or change that touches more than one file, or when picking up the next task from a plan. Use when rolling a change out behind a feature flag, when you're about to write a large amount of code at once, or whe
Quick reference for ponytail levels, skills and commands. One-shot display. Use for /ponytail-help, "ponytail help", "how do I use ponytail".
Stop hook that blocks Claude from finishing until quality checks pass. Detects rationalization patterns (surface text heuristics), stale learning logs (filesystem mtime), and low disk space. Complements self-audit by mechanically enforcing learning capture habits. Use when Claude should be mechanica