Configuration
Driver class name
The Neo4j JDBC Driver is org.neo4j.jdbc.Neo4jDriver
.
With modern Java tools, you should not need to touch this class directly, but there are some connection pools and front-ends that will ask you for this.
The class is public API.
We also provide org.neo4j.jdbc.Neo4jDataSource
as javax.sql.DataSource
.
Causal clustering and bookmarks
The Neo4j JDBC Driver uses bookmarks by default to provide causal consistency in all Neo4j deployments.
Bookmarks are managed on the driver level itself, not on the connections spawned by an instance of the driver, so all connections spawned by one instance will partake in the same causal chain of transactions.
Connections from different driver instances will not use the same set of bookmarks and there is no built-in machinery that would enable this.
If you want or need this, you can directly access the Neo4jDriver
type to retrieve the current set of known bookmarks and pass them to another driver instance.
Neo4j transactional metadata
Neo4j supports attaching metadata to transactions, see SHOW TRANSACTIONS
.
As there is no explicit transaction object in the JDBC specification, the Neo4j JDBC driver needs another mechanism to make these configurable.
The JDBC driver provides the extension interface Neo4jMetadataWriter
.
Our driver, the connection implementation, and all statement variants can be unwrapped accordingly.
The configuration is additive: metadata configured for a driver instance will be used for all connections spawned from that driver, connections can add further metadata, and statements can also add their own metadata.
Metadata added on a statement has precedence over connection metadata which in turn has precedence over driver metadata:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.neo4j.jdbc.Neo4jDriver;
import org.neo4j.jdbc.Neo4jMetadataWriter;
public final class TransactionMetadata {
private static final Logger LOGGER = Logger.getLogger(TransactionMetadata.class.getPackageName());
public static void main(String... args) throws SQLException {
var url = "jdbc:neo4j://localhost:7687";
var driver = (Neo4jDriver) DriverManager.getDriver(url);
driver.withMetadata(Map.of("md_from_driver", "v1", "will_be_overwritten", "irrelevant"));
var properties = new Properties();
properties.put("user", "neo4j");
properties.put("password", "verysecret");
try (
var con = driver.connect(url, properties)
.unwrap(Neo4jMetadataWriter.class)
.withMetadata(Map.of("md_from_connection", "v2", "will_be_overwritten", "xxx"))
.unwrap(Connection.class);
var statement = con.createStatement()
.unwrap(Neo4jMetadataWriter.class)
.withMetadata(Map.of("md_from_stmt", "v3", "will_be_overwritten", "v4"))
.unwrap(Statement.class)
) {
try (var result = statement.executeQuery("SHOW TRANSACTIONS YIELD metaData")) {
while (result.next()) {
var metaData = result.getObject("metaData", Map.class);
LOGGER.log(Level.INFO, "{0}", metaData);
}
}
}
}
}
The output will be similar to this:
Juli 17, 2024 1:18:16 PM org.neo4j.jdbc.docs.TransactionMetadata main
INFORMATION: {md_from_driver=v1, md_from_connection=v2, md_from_stmt=v3, will_be_overwritten=v4}
URL and connection properties
The canonical URL format for the Neo4j JDBC Driver is
jdbc:neo4j://<host>:<port>/<database>?param1=value1¶m2=value2
The database name and all query parameters are optional and can be omitted.
All configuration arguments can be passed either as query parameters or via a java.util.Properties
object.
The latter is sometimes defined by tooling for you.
With regard to authentication, we recommend to follow the JDBC specification, which discourages using any form of URL authentication.
All query parameters must be percent-encoded if they contain special characters, e.g. …?param1=space%20separated
.
The driver supports the following URI schemes, which tweak the security configuration:
|
The driver accepts the following configuration arguments, either as properties or as URL query parameters:
Name | Type | Meaning | Default |
---|---|---|---|
|
|
Timeout for connection acquisition in milliseconds |
|
|
|
User agent |
|
|
|
Flag that enables automatic translation from SQL-to-Cypher (requires a translator on the classpath) |
|
|
|
Flag that enables caching of translations. SQL translations are not "free": parsing of SQL costs a bit of time, and so does Cypher® rendering. In addition, we might up look up metadata to be able to project individual properties. If this takes too long, translations may be cached. |
|
|
|
Flag that allows you to use |
|
|
|
Optional flag, alternative to |
|
|
|
Optional configuration for fine-grained control over SSL configuration. Allowed values are |
|
|
|
The username (principal) to use for authentication. NOT RECOMMENDED as URL query parameter for security reasons. |
|
|
|
The password (credentials) to use for authentication. NOT RECOMMENDED as URL query parameter for security reasons. |
|
|
|
The realm to use for authentication. NOT RECOMMENDED as URL query parameter for security reasons. |
|
|
|
The authentication scheme to use. NOT RECOMMENDED as URL query parameter for security reasons. Currently supported values are:
|
|
|
|
Enables bookmark management for full causal cluster support. This is enabled by default and the recommended setting for all scenarios that use a connection pool. If you disable it, it will only be disabled for the specific connection. Other connections retrieved from the driver instance to the same or to other databases are not affected, and the individual connections will still manage their bookmarks. |
|
Getting a driver or a connection instance
This section likely only applies if you use the Neo4j JDBC Driver as part of application development in contrast to using it as part of front-end tool such as DBeaver, DataGrip or UI-based ETL tools. |
The easiest way to acquire a connection is directly through the java.sql.DriverManager
.
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
class Configuration {
void obtainConnection() throws SQLException {
var url = "jdbc:neo4j://localhost:7687";
var username = "neo4j";
var password = "verysecret";
var connection = DriverManager.getConnection(url, username, password);
}
}
While our connection implementation is thread-safe, it allows only one concurrent transaction per connection (as dictated by the JDBC specification). For a multi-thread application, use a connection pool. There’s HikariCP, but usually application server and containers/frameworks bring their own. It’s safe to use any of them, as the Neo4j JDBC Driver does no internal pooling.
If you need access to an instance of the Neo4j driver itself, you can use the following approach:
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
class Configuration {
void obtainDriverAndConnection() throws SQLException {
var url = "jdbc:neo4j://localhost:7687";
var driver = DriverManager.getDriver(url);
var properties = new Properties();
properties.put("username", "neo4j");
properties.put("password", "verysecret");
var connection = driver.connect(url, properties);
}
}
Securing your connection by using SSL
The Neo4j JDBC Driver supports the same SSL option of the common Java driver, with the same URL protocols, using +s
or +ssc
as additional indicators for the required level of security.
The same configuration can also be achieved with a URL query parameter or an entry in the properties passed to the DriverManager
or driver instance when asking for a connection.
As long as you don’t specify contradicting values, it’s fine to combine both approaches.
Understanding the SSL mode
The following list is ordered by ascending security:
-
disable
— (default), "I don’t care about security and don’t want to pay the overhead for encryption." -
require
— "I want my data to be encrypted, and I accept the overhead. I trust that the network will make sure I always connect to the server I want." (Server must support encryption, no hostname/CA validation is done. This saves the hassle of proper certificates and is only secure on private networks; it should not really be used over public networks.) -
verify-full
— "I want my data encrypted, and I accept the overhead. I want to be sure that I connect to a server I trust, and that it’s the one I specify."
The Neo4j JDBC Driver does not include revocation checks. |
The most secure option can also be enabled by setting ssl=true
either as query parameter or as property entry passed to the DriverManager
.
This option corresponds to neo4j+s
.
On the other hand, require
corresponds to neo4j+ssc
.
The additional enum allows us to possibly support additional modes in the future, such as letting the service decide about SSL, or being able to express a preference towards SSL without requiring it.
Neo4j servers can offer both plain Bolt connections and encrypted SSL connection, or just one of them. The fact that you can connect using neo4j+s does not mean that you cannot connect using just neo4j , or viceversa. This is dependent on the server setup. Neo4j Aura, Neo4j’s managed cloud service, only supports encrypted connections, so you must use +s , ssl=true , or sslMode=verify-full .
|
Valid URLs
The following URLs are all valid:
neo4j+s://xyz.databases.neo4j.io
-
Use full verification with the
xzy
instance at AuraDB neo4j://xyz.databases.neo4j.io?ssl=true
-
The same, but using the shorthand URL parameter
neo4j://xyz.databases.neo4j.io?sslMode=verfiy-full
-
The same, but using the explicit mode
neo4j+s://xyz.databases.neo4j.io?ssl=true&sslMode=verify-full
-
Not more secure, but does not fail on you
neo4j+ssc://this.is.a.trustworthy.instance.for.sure.com
-
Trust whatever certificate and hostname there is, but do use SSL
neo4j://my-testing-instance.local
-
Use a plain connection.
The driver only refuses contradicting configurations, such as:
-
+s
withssl=false
, orsslMode
set todisable
-
+ssc
withssl=false
, or anysslmode
not equal torequire
Basically, you cannot ask to use SSL and not use it at the same time. The driver offers several mechanism so that you can use a fixed URL with dynamic query parameters, or dynamic URLs, or whatever way of configuring you prefer in a programmatic way.
Using .dotenv files
When you create a Neo4j Aura instance, you will be asked to download a text-file named similar to Neo4j-9df57663-Created-2023-06-12.txt
.
This is essentially a .dotenv file containing the information required to connect to the database.
These files can be directly used via Neo4jDriver.fromEnv()
(see Getting a connection via environment variables).
This method exists in several overloads, which let you configure both filename and directory.
Additionally, the builder lets you configure options that are not contained in the files from Aura.
Logging
Via standard Java settings
To generate a useful and detailed log, you must configure java.util.logging
. This can be done either through a configuration file or programmatically.
The following section includes an example configuration that sets the global logging level to INFO
, Neo4j JDBC Driver itself to DEBUG
and everything that is network related to WARN
:
.level = INFO
handlers=java.util.logging.ConsoleHandler
# The handler must be configured for the lowest level it should catch
java.util.logging.ConsoleHandler.level=FINEST
# Enables all Logging for the JDBC Driver
org.neo4j.jdbc.level=ALL
# But reduces the noise on the network and the result set
org.neo4j.jdbc.result-set.level=WARNING
org.neo4j.jdbc.network.level=WARNING
# This is an example how to configure the outbound and inbound messages
org.neo4j.jdbc.network.OutboundMessageHandler.level=FINE
org.neo4j.jdbc.network.InboundMessageHandler.level=FINE
# This produces a date, followed by the level, 3 dashes, the message and the source of the log if available
java.util.logging.SimpleFormatter.format = %1$tFT%1$tk:%1$tM:%1$tS.%1$tL%1$tz %4$-15s --- %5$s [%2$s]%n
Such a file must be passed to the running Java program as a system property to Java itself with:
java -Djava.util.logging.config.file=docs/src/main/resources/logging-to-console.properties <your program>
Capturing logs to a file requires to change the handler:
.level = INFO
handlers=java.util.logging.ConsoleHandler
# Keep everything by default at INFO
java.util.logging.ConsoleHandler.level=INFO
# The handler must be configured for the lowest level it should catch
java.util.logging.FileHandler.level=FINEST
java.util.logging.FileHandler.pattern=jdbc.log
# XML Format by default
# java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
# Change the handler for JDBC
org.neo4j.jdbc.handlers=java.util.logging.FileHandler
# Keep the rest as above
org.neo4j.jdbc.level=ALL
org.neo4j.jdbc.result-set.level=WARNING
org.neo4j.jdbc.network.level=WARNING
org.neo4j.jdbc.network.OutboundMessageHandler.level=FINE
org.neo4j.jdbc.network.InboundMessageHandler.level=FINE
java.util.logging.SimpleFormatter.format = %1$tFT%1$tk:%1$tM:%1$tS.%1$tL%1$tz %4$-15s --- %5$s [%2$s]%n
The diagnosability features of the JDBC driver are based on the standard java.util.logging framework. Configuration of that framework itself is out of scope for this documentation.
For a complete overview you might want to start at "Java Logging Overview".
The formatter Java uses is the java.util.Formatter .
|
Inside a Spring Boot application
The Neo4j JDBC Driver will integrate itself into the Spring Boot logging configuration and take all relevant format and other settings from there. The levels can be configured with the levels Spring does understand. The following configuration is equivalent to the one above:
logging.level.org.neo4j.jdbc=trace
logging.level.org.neo4j.jdbc.result-set=warn
logging.level.org.neo4j.jdbc.network=warn
logging.level.org.neo4j.jdbc.network.OutboundMessageHandler=debug
logging.level.org.neo4j.jdbc.network.InboundMessageHandler=debug
Inside a Quarkus application
The Neo4j JDBC Driver will integrate itself into the Quarkus logging configuration and take all relevant format and other settings from there. The levels can be configured with the levels Quarkus does understand. The following configuration is equivalent to the ones above:
quarkus.log.category."org.neo4j.jdbc".min-level=TRACE
quarkus.log.category."org.neo4j.jdbc".level=TRACE
quarkus.log.category."org.neo4j.jdbc.result-set".level=DEBUG
quarkus.log.category."org.neo4j.jdbc.network".level=WARNING
quarkus.log.category."org.neo4j.jdbc.network.OutboundMessageHandler".level=DEBUG
quarkus.log.category."org.neo4j.jdbc.network.InboundMessageHandler".level=DEBUG
Metrics
The Neo4j JDBC Driver will publish metrics via Micrometer. If you put Micrometer on your classpath, no further configuration is necessary. This is for example automatically the case with Spring Boot Actuator or Micrometer for Quarkus. To make best use of this feature, you will additionally configure your monitoring system.
The Neo4j JDBC Driver does publish the following metrics:
org.neo4j.jdbc.connections
-
a gauge representing the number of open connections, tagged with
url
to which the driver is connected org.neo4j.jdbc.statements
-
a gauge representing the number of open statements, tagged with both the
url
to which the statement is opened and the JDBC type of the statement (eitherStatement
,PreparedStatement
orCallableStatement
) org.neo4j.jdbc.queries
-
a composite meter containing the counts of successful and failed queries and a timer measuring the duration of queries
org.neo4j.jdbc.cached-translations
-
a gauge representing the number of cached SQL to cypher translations
Tracing
The Neo4j JDBC Driver supports tracing and will provide traces spans for the following operations on JDBC Statement
instances:
-
#execute
-
#executeQuery
-
#executeUpdate
and all overloads thereof and in all inheriting types.
In addition, when using a JDBC ResultSet
, it will open a span when the result set is first moved from being before the first row until it is either moved beyond the last row or actively closed.
Spans will contain various events, such as when a query has been actually executed by the Neo4j database or when a batch of records has been pulled from the database.
The feature is optional: when you want to use it, you have to bring in additional dependencies. This applies to all distributed versions, including the full bundles, so that an optional feature doesn’t make the bundles bigger. For Maven, use this additional dependency declaration:
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-jdbc-tracing-micrometer</artifactId>
<version>6.3.1</version>
</dependency>
And for Gradle you would want to declare the following runtime dependency:
dependencies {
runtimeOnly 'org.neo4j:neo4j-jdbc-tracing-micrometer:6.3.1'
}
This dependency brings in Micrometer Tracing. Micrometer Tracing allows you to bind to various tracers and exporters, such as Zipkin or OpenTelemetry.
Once you have a Micrometer Tracer, you can configure a Neo4jDataSource
like this:
Neo4jDataSource
spawning connections that trace their usageimport javax.sql.DataSource;
import org.neo4j.jdbc.Neo4jDataSource;
import org.neo4j.jdbc.tracing.micrometer.Neo4jTracingBridge;
import io.micrometer.tracing.Tracer;
public class TracingExampleSetup {
DataSource neo4jDataSource() {
Tracer tracer = Tracer.NOOP; (1)
var neo4jDataSource = new Neo4jDataSource();
neo4jDataSource.setUrl("jdbc:neo4j://yourhost:7687");
neo4jDataSource.setPassword("neo4j");
neo4jDataSource.setUser("verysecret");
neo4jDataSource.setTracer(Neo4jTracingBridge.to(tracer)); (2)
return neo4jDataSource;
}
}
1 | In most scenarios, your application framework will provide a Micrometer tracer instance for you |
2 | With this call you enable tracing in the data source and all connections spawned from it |
You can also enable tracing on individual connections by unwrapping them into the Neo4jConnection
extension like this:
import java.sql.Connection;
import java.sql.DriverManager;
import org.neo4j.jdbc.Neo4jConnection;
import org.neo4j.jdbc.tracing.micrometer.Neo4jTracingBridge;
import io.micrometer.tracing.Tracer;
public class TracingExampleSetup {
Connection connection() {
Tracer tracer = Tracer.NOOP;
return DriverManager.getConnection("jdbc:neo4j://yourhost:7687", "neo4j", "verysecret")
.unwrap(Neo4jConnection.class) (1)
.withTracer(Neo4jTracingBridge.to(tracer));
}
}
1 | Unwrap the connection into our extension so that you can configure the tracer. |
Using tracing with Spring Boot
The tracing implementation that is provided by the Neo4j JDBC Driver utilizes Micrometer Tracing, which is well accepted in the Spring Boot ecosystem, but also in Quarkus, Micronaut and others. We provide a few examples, but refer to your frameworks documentation on how to configure tracing.
Configuring the tracing datasource
Usually, Spring Boot configures a HikariDataSource
based on you driver.
Hikari provides connection pooling, and you should not ditch that.
There’s no need to create your own data source properties for all configuration values, you can just inject it like this:
import javax.sql.DataSource;
import org.neo4j.jdbc.Neo4jDataSource;
import org.neo4j.jdbc.tracing.micrometer.Neo4jTracingBridge;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import io.micrometer.tracing.Tracer;
@Configuration
class TracingTestConfiguration {
@Bean
DataSource neo4jDataSource(Tracer tracer, DataSourceProperties dataSourceProperties) {
var neo4jDataSource = new Neo4jDataSource();
neo4jDataSource.setUrl(dataSourceProperties.getUrl());
neo4jDataSource.setPassword(dataSourceProperties.getPassword());
neo4jDataSource.setUser(dataSourceProperties.getUsername());
neo4jDataSource.setTracer(Neo4jTracingBridge.to(tracer));
var cfg = new HikariConfig();
cfg.setDataSource(neo4jDataSource);
return new HikariDataSource(cfg);
}
}
Bring org.springframework.boot:spring-boot-starter-actuator
onto your class path too, and it will configure Micrometer for you.
The setup of your data source stays the same: no change is required here for the following examples to work, as our Tracing API will automatically pick the correct tracer through Micrometer! |
Exporting to Zipkin
To export your traces to Zipkin, add io.micrometer:micrometer-tracing-bridge-brave
and io.zipkin.reporter2:zipkin-reporter-brave
and set management.zipkin.tracing.export.enabled
to true
in your Spring application.
You should see spans like this:

Exporting via OpenTelemetry
OpenTelemetry allows to export not only traces, but metrics and logging too.
Here’s an example how to use Dash0 via OpenTelemetry. The exporter should work the same for other services. OpenTelemetry has split its SDK into several modules, hence you will first import their BOM into your build file. Here’s an example for Maven:
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-bom</artifactId>
<version>1.47.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
First you need to bring in the bridge from Micrometer to OpenTelemetry via: io.micrometer:micrometer-tracing-bridge-otel
, so that Micrometer pushes into that SDK.
Next, bring in the following dependencies so that Spring Boot can configure the exporter:
-
io.opentelemetry:opentelemetry-sdk
-
io.opentelemetry:opentelemetry-exporter-common
-
io.opentelemetry:opentelemetry-exporter-otlp
-
io.opentelemetry:opentelemetry-exporter-sender-jdk
In the case of Dash0, the application configuration will look similar to this:
management.otlp.tracing.export.enabled=true
management.otlp.tracing.endpoint=https://ingress.eu-west-1.aws.dash0.com/v1/traces
management.otlp.tracing.headers.Authorization=Bearer auth_XX_your_token_XX
management.otlp.metrics.export.step=30s
management.otlp.metrics.export.enabled=true
management.otlp.metrics.export.url=https://ingress.eu-west-1.aws.dash0.com/v1/metrics
management.otlp.metrics.export.headers.Authorization=Bearer auth_XX_your_token_XX
The trace from our Movie application looks like this:
