https://github.com/michael-simons/neo4j-from-the-jvm-ecosystem
Examples of how to connect to Neo4j on the JVM, reading and writing data.
https://github.com/michael-simons/neo4j-from-the-jvm-ecosystem
helidon java jvm micronaut neo4j neo4j-driver quarkus spring spring-data-neo4j
Last synced: 3 months ago
JSON representation
Examples of how to connect to Neo4j on the JVM, reading and writing data.
- Host: GitHub
- URL: https://github.com/michael-simons/neo4j-from-the-jvm-ecosystem
- Owner: michael-simons
- Created: 2020-11-05T17:20:40.000Z (almost 5 years ago)
- Default Branch: master
- Last Pushed: 2025-01-17T14:57:04.000Z (9 months ago)
- Last Synced: 2025-07-15T04:27:06.361Z (3 months ago)
- Topics: helidon, java, jvm, micronaut, neo4j, neo4j-driver, quarkus, spring, spring-data-neo4j
- Language: Java
- Homepage:
- Size: 322 KB
- Stars: 18
- Watchers: 5
- Forks: 3
- Open Issues: 0
-
Metadata Files:
- Readme: README.adoc
Awesome Lists containing this project
README
= Neo4j from the JVM ecosystem.
Examples of how to connect to Neo4j on the JVM, reading and writing data.
The idea of this project is to show how to access the standard movie graph and create a web api for it on different platforms running on the JVM.
All projects have been created either via their official "starter" services or Maven archetypes.
NOTE: The order in which the frameworks is alphabetical and doesn't represent any preference.
As I prefer Maven, I chose to create all projects as Maven projects.https://github.com/spring-projects/spring-data-neo4j[Spring Data Neo4j 6] can be used inside a CDI container as well and is known to work in Liberty and Helidon.
We provide a CDI extension for that. You need to bring in an application scoped instance of the Neo4j Driver.
The portable CDI extension https://quarkus.io/blog/quarkus-dependency-injection/#extension-points[won't work in Quarkus].
So in case you really want to have great Object Mapping but cannot decide to use Spring as a container,
you might want to try out the CDI extension.Neo4j-OGM can work on GraalVM native since version 3.2.19 when you provide an index of the node entities.
The index must be in `META-INF/resources/name/of/your/package/neo4j-ogm.index`.
Also the entities in question must be registered for reflection (either through pure GraalVM `reflection-config.json` or through the means of your selected framework.)The goal of this repository is to check and demonstrate what is necessary to achieve the following inside the current Java application frameworks and runtimes:
* Support a couple of "standard" use cases presented as HTTP Api, transporting JSON. This is a hard requirement
* See if they provide automatic setup of the Neo4j driver or at least a dynamic configuration mechanism to do this
* Check if there's an officially supported Data Mapping framework available for Neo4j.
Having a data mapping framework layer is really nice to have, but if this is not possible, the Neo4j Java driver
plays very well with custom mapping functions or things like https://mapstruct.org[MapStruct].
* Built-in dev-mode with reload / restart feature
* Dedicated support for optimized docker images (Preferable via the chosen build tool but a two step process via a dedicated docker file is ok)
* See if a Reactive Streams implementation is present and whether it works with the Neo4j Java driver
* See if the application can be turned into a GraalVM native image via the chosen build tool without
fiddling around with `native-image` parametersA red cross doesn't mean a given Framework is bad in anyway.
It is just an indicator that something either doesn't work without additional configuration or programming or is just not there.
An assessment whether a particular thing is needed or not is up to the reader and must be reasoned about in the context of the framework and it's actually present features.All the frameworks used and tested here support the actual "business" logic.
== Shared Api
All projects provide the following API:
=== `POST /api/people`
[source,bash]
.Example request
----
$ curl 'http://localhost:8080/api/people' -i -X POST \
-H 'Content-Type: application/json' \
-d '{"name":"Lieschen Müller","born":2020}'
----[source,http,options="nowrap"]
.Example response
----
HTTP/1.1 201 Created
Content-Type: application/json
Content-Length: 64{
"born" : 2020,
"name" : "Lieschen Müller",
"id" : 339
}
----=== `GET /api/movies`
[source,bash]
.Example request
----
$ curl 'http://localhost:8080/api/movies' -i -X GET
----[source,http,options="nowrap"]
.Example response
----
HTTP/1.1 200 OK
transfer-encoding: chunked
Content-Type: text/event-stream;charset=UTF-8
Content-Length: 1270[ {
"title" : "A Few Good Men",
"description" : "In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth.",
"actors" : [ {
"roles" : [ "Capt. Jack Ross" ],
"name" : "Kevin Bacon"
}, {
"roles" : [ "Lt. Cdr. JoAnne Galloway" ],
"name" : "Demi Moore"
}, {
"roles" : [ "Man in Bar" ],
"name" : "Aaron Sorkin"
}, {
"roles" : [ "Lt. Col. Matthew Andrew Markinson" ],
"name" : "J.T. Walsh"
}, {
"roles" : [ "Lt. Jonathan Kendrick" ],
"name" : "Kiefer Sutherland"
}, {
"roles" : [ "Lt. Sam Weinberg" ],
"name" : "Kevin Pollak"
}, {
"roles" : [ "Cpl. Jeffrey Barnes" ],
"name" : "Noah Wyle"
}, {
"roles" : [ "Cpl. Carl Hammaker" ],
"name" : "Cuba Gooding Jr."
}, {
"roles" : [ "Dr. Stone" ],
"name" : "Christopher Guest"
}, {
"roles" : [ "Lt. Daniel Kaffee" ],
"name" : "Tom Cruise"
}, {
"roles" : [ "Col. Nathan R. Jessup" ],
"name" : "Jack Nicholson"
}, {
"roles" : [ "Pfc. Louden Downey" ],
"name" : "James Marshall"
} ],
"directors" : [ {
"born" : 1947,
"name" : "Rob Reiner",
"id" : 194
} ],
"released" : 1992
} ]
----=== Status / Health checks
==== Liveness
General Liveness of the application:
[quote,Spring Boot Documentation]
____
The “Liveness” state of an application tells whether its internal state allows it to work correctly, or recover by itself if it’s currently failing.
____[source,bash]
.Example request
----
$ curl 'http://localhost:8080/management/health/liveness' -i -X GET
----[source,http,options="nowrap"]
.Example response
----
HTTP/1.1 200 OK
Content-Type: application/json
Transfer-Encoding: chunked
Date: Mon, 16 Nov 2020 12:51:24 GMT
Content-Length: 21{
"status" : "UP"
}
----==== Readiness
[quote,Spring Boot Documentation]
____
The “Readiness” state of an application tells whether the application is ready to handle traffic. A failing “Readiness” state tells the platform that it should not route traffic to the application for now.
____We expect the Neo4j database connection to be taken into consideration for readiness.
[source,bash]
.Example Request
----
$ curl 'http://localhost:8080/management/health/readiness' -i -X GET \
-H 'Accept: application/json'
----[source,http,options="nowrap"]
.Example response
----
HTTP/1.1 200 OK
Content-Type: application/json
Transfer-Encoding: chunked
Date: Mon, 16 Nov 2020 12:54:29 GMT
Content-Length: 21{
"status" : "UP"
}
----== Helidon SE
* ✅ Automatic setup of the Neo4j driver (A configuration framework however is provided)
* ⚠️ Officially supported Data Mapping framework available (https://github.com/neo4j/neo4j-ogm[Neo4j-OGM] can be used outside a Spring environment and can use the Driver as is, without CDI.)
* ❌ Built-in dev-mode with reload / restart feature
* ⚠️ Dedicated support for optimized docker images (A Docker file is provided)
* ✅ Reactive Streams: Helidon's own implementation
* ✅ GraalVM native compilation
* ✅ Health (Liveness, Readiness and detailed status)Demo projects provided:
* helidon-se-reactive
Created via `mvn -U archetype:generate -DinteractiveMode=false -DarchetypeGroupId=io.helidon.archetypes-DarchetypeArtifactId=helidon-quickstart-se -DarchetypeVersion=2.3.0`,
version tested *2.3.0*.=== Configuration of the Neo4j Java Driver
No manual work is required. Any namespace in a YAML or properties file can be used. The integration is provided as an artifact under those coordinates:
[source,xml]
----io.helidon.integrations.neo4j
helidon-integrations-neo4j----
This is an example how to use Neo4j properties under the key `neo4j`:
[source,java]
----
var driver = config.get("neo4j").as(Neo4j::create).map(Neo4j::driver).orElseThrow();
----The driver can than be used in the SE application / microservice.
=== Running
[source,console]
----
mvn clean package
java -jar target/helidon-se-reactive.jar
----=== Testing
Manual work required, can be solved with JUnit 5 means and the easy to use Helidon SE api.
=== Create docker images
Two steps required, a `Dockerfile` is provided.
[source,console]
----
mvn clean package
docker build .
----=== Create native images
----
# For your current system, GraalVM 11 is required
mvn clean package -Pnative-image
# As a native docker image
mvn clean package
docker build -f Dockerfile.native .
----Helidon offers `io.helidon.common.Reflected` for classes that needs to be included in the image and require reflection based access.
=== Health
Health infrastructure provided with Helidon Health Checks `io.helidon.health:helidon-health` and `io.helidon.health:helidon-health-checks`,
support for Neo4j included via `io.helidon.integrations.neo4j:helidon-integrations-neo4j-health`.However, the build-in support in 2.3.0 is broken for native compilation, see https://github.com/oracle/helidon/issues/3060[GH-3060].
Format of the individual Neo4j status:
[source,bash]
.Request
----
$ curl 'http://localhost:8080/management/health/readiness' -i -X GET
----[source,json]
----
{
"outcome": "UP",
"status": "UP",
"checks": [
{
"name": "Neo4j connection health check",
"state": "UP",
"status": "UP",
"data": {
"database": "neo4j",
"server": "Neo4j/4.2.0@localhost:7687"
}
}
]
}
----== Micronaut
* ⚠️ Automatic setup of the Neo4j driver (Only URL, credentials and TLS settings supported as properties)
* ⚠️ Officially supported Data Mapping framework available (GORM, in beta for Neo4j, SDN 6 might work as well)
* ✅ Built-in dev-mode with reload / restart feature
* ✅ Dedicated support for optimized docker images
* ✅ Reactive Streams: https://github.com/ReactiveX/RxJava[RxJava2 and 3]
* ✅ GraalVM native compilation
* ✅ Health (Liveness, Readiness and detailed status)Demo projects provided:
* micronaut-reactive
Created via: https://micronaut.io/launch/, version tested *2.2.0*.
=== Configuration of the Neo4j Java Driver
Support of the some 4.0.x config options under the namespace `neo4j.*`
Basic setup:[source,yaml]
----
neo4j:
username: neo4j
password: secret
uri: bolt://localhost:7687
----=== Running
[source,console]
----
./mvnw mn:run
----=== Testing
Directly supported only with an older, not reactive capable version of Neo4j embedded.
=== Create docker images
The packaging in the `pom.xml` must be set via a property and the `io.micronaut.build:micronaut-maven-plugin` Maven plugin must be defined.
Both is done via the generator by default.
The Maven plugin uses Googles https://micronaut-projects.github.io/micronaut-maven-plugin/1.1.0/examples/package.html[jib-maven-plugin] under the hhod.[source,console]
----
./mvnw clean package -Dpackaging=docker
----The plugin can also be used to generate a Docker file for further editing.
Similar tooling with the same features is available for Gradle.=== Create native images
[source,console]
----
# For your current system, GraalVM 11 20.3 is required
./mvnw clean package -Dpackaging=native-image
# As a native docker image
./mvnw clean package -Dpackaging=docker-native
----Micronaut offers `io.micronaut.core.annotation.Introspected` for classes that should be instrospected as Beans on compile time.
There is also `io.micronaut.core.annotation.ReflectiveAccess` to mark constructors, methods and fields for reflective access..The Maven setup requires the `io.micronaut:micronaut-graal` annotation processor to correctly produce native image calls:
[source,xml]
----org.apache.maven.plugins
maven-compiler-plugin
io.micronaut:micronaut-graal
micronaut-graal
${micronaut.version}
----
=== Health
Health infrastructure provided with Micronaut Management (`io.micronaut:micronaut-management`), *Neo4j included ootb*.
NOTE: Path to individual endpoints for `liveness` and `readiness` seems not to be changeable.
Format of the individual Neo4j status:
[source,bash]
.Request
----
$ curl 'http://localhost:8080/management/health' -i -X GET
----[source,json]
----
{
"name": "micronaut-reactive",
"status": "UP",
"details": {
"neo4j": {
"name": "micronaut-reactive",
"status": "UP",
"details": {
"server": "Neo4j/4.1.0@localhost:7687"
}
}
}
}
----== Quarkus
* ✅ Automatic setup of the Neo4j driver
* ✅ Officially supported Data Mapping framework available (https://github.com/neo4j/neo4j-ogm[Neo4j-OGM], JVM and dev modes work ootb, native mode needs an index, which is just an enumeration of the mapped entities.)
* ✅ Built-in dev-mode with reload / restart feature
* ✅ Dedicated support for optimized docker images
* ✅ Reactive Streams: https://smallrye.io/smallrye-mutiny/[Smallrye Mutiny]
* ✅ GraalVM native compilation
* ✅ Health (Liveness, Readiness and detailed status)Demo projects provided:
* quarkus-imperative
* quarkus-ogm (Take note of the entity index https://github.com/michael-simons/neo4j-from-the-jvm-ecosystem/tree/master/quarkus-ogm/src/main/resources/META-INF/resources/org/neo4j/examples/jvm/quarkus/ogm/movies/neo4j-ogm.index[here].)
* quarkus-reactiveCreated via: https://code.quarkus.io, version tested: *1.9.2.Final*.
=== Configuration of the Neo4j Java Driver
Support of the all relevant 4.1.x config options under the namespace `quarkus.neo4j.*`, including TLS:
Basic setup:[source,properties]
----
quarkus.neo4j.uri=bolt://localhost:7687
quarkus.neo4j.authentication.username=neo4j
quarkus.neo4j.authentication.password=secret
----=== Running
[source,console]
----
./mvnw quarkus:dev
----=== Testing
Easy setup of test connections (via a custom `QuarkusTestResourceLifecycleManager`). Can be used with Neo4j embedded test harness (as in the example) or with Neo4j Test-Containers.
=== Create docker images
(Extension `container-image-docker` must be provided once, via `./mvnw quarkus:add-extension -Dextensions="container-image-docker"`).
[source,console]
----
./mvnw clean package -Dquarkus.container-image.build=true
----=== Create native images
[source,console]
----
# For your current system, GraalVM 11 is required
./mvnw clean package -Pnative
# As a native docker image
./mvnw package -Pnative -Dquarkus.native.container-build=true -Dquarkus.container-image.build=true
----Quarkus offers `io.quarkus.runtime.annotations.RegisterForReflection` for classes that needs to be included in the image and require reflection based access.
=== Health
Health infrastructure provided with Quarkus Smallrye Health (`io.quarkus:quarkus-smallrye-health`), *Neo4j included ootb*.
All paths can be easily configured (health separate from liveness and readiness).Format of the individual Neo4j status:
[source,bash]
.Request
----
$ curl 'http://localhost:8080/management/health' -i -X GET
----[source,json]
----
{
"status": "UP",
"checks": [
{
"name": "Neo4j connection health check",
"status": "UP",
"data": {
"server": "Neo4j/4.1.0@localhost:7687",
"database": "neo4j"
}
}
]
}
----== Spring
* ✅ Automatic setup of the Neo4j driver
* ✅ Officially supported Data Mapping framework available (SDN 6 for current, SDN5+OGM for older version)
* ✅ Built-in dev-mode with reload / restart feature
* ✅ Dedicated support for optimized docker images
* ✅ Reactive Streams: https://projectreactor.io[Project Reactor]
* ⚠️ GraalVM native compilation (Currently in beta, not part of a standard setup)
* ✅ Health (Liveness, Readiness and detailed status)Demo projects provided:
* spring-plain-imperative
* spring-plain-reactive
* spring-data-imperative
* spring-data-reactiveCreated via: https://start.spring.io, version tested: *2.5.1*.
There is an additional project, `spring-boot24-with-sdn-ogm` that does a bit work on the dependencies
so that people can use SDN+OGM with the most recent versions of Spring Boot.
The preferred way of using SDN+OGM however is Spring Boot prior to 2.4.=== Configuration of the Neo4j Java Driver
Full support of all official 4.1.x config options under the namespace `spring.neo4j.*`.
Basic setup:[source,properties]
----
spring.neo4j.uri=bolt://localhost:7687
spring.neo4j.authentication.username=neo4j
spring.neo4j.authentication.password=secret
----=== Running
[source,console]
----
./mvnw spring-boot:run
----=== Testing
Easy setup of test connections (via `@DataNeo4jTest` and a custom `@DynamicPropertySource`). Can be used with Neo4j embedded test harness (as in the example) or with Neo4j Test-Containers.
=== Create docker images
[source,console]
----
./mvnw -DskipTests clean spring-boot:build-image
----Similar tooling with the same features is available for Gradle.
=== Create native images
Provide the compile time dependency
[source,xml]
----org.springframework.experimental
spring-graalvm-native
0.8.3
compile----
Adapt the build config like this
[source,xml]
----
org.springframework.boot
spring-boot-maven-plugin
paketobuildpacks/builder:tiny
1
-Dspring.spel.ignore=true
-Dspring.native.remove-yaml-support=true
----
And `./mvnw -DskipTests clean spring-boot:build-image` will create a native image. Tested with Spring Boot 2.4 and Spring GraalVM Native 0.8.3.
=== Health
Health infrastructure provided with Spring Boot Actuator (`org.springframework.boot:spring-boot-starter-actuator`), *Neo4j included ootb*.
NOTE: Path to individual endpoints for `liveness` and `readiness` seems to be changeable with
`management.endpoint.health.group.live.include=livenessState`, but that feels not very intuitive.Format of the individual Neo4j status:
[source,bash]
.Request
----
$ curl 'http://localhost:8080/management/health' -i -X GET
----[source,json]
----
{
"status": "UP",
"components": {
"neo4j": {
"status": "UP",
"details": {
"server": "Neo4j/4.1.0@localhost:7687",
"edition": "enterprise",
"database": "neo4j"
}
}
}
}
----== TCK
I have created a TCK - basically a glorified end-to-end-test - that brings up each application and ensure it's expected behaviour.
You need bash, Docker and JDK 16 to run it:[source,console]
----
./runTck.sh
----It will bring up a Neo4j docker instance and take each project, build a docker image, start it and than executes a couple of requests against it.
The script is tested currently only under macOS.== Some numbers
All scripts to build, verify and benchmark the images are in this repository.
The path of least resistance (or effort) has been chosen to build the images,
JVM and native one, following the official instructions.All tests have been conducted with https://github.com/apigee/apib[apib: API Bench] using the following options:
[source,console]
----
apib -c20 -d60 http://localhost:$EXPOSED_PORT/api/movies
----The application was connected against a Neo4j container running on the same host:
The Neo4j container has been shutdown and restarted between each benchmark run.apib fully supports HTTP 1.1 including keep-alives and chunked encoding.
Apache Bench cannot reliable benchmark the reactive infrastructure without it.Time to readiness is the time from container start until the container reports `UP`
in a `GET /management/health/readiness`..Some numbers
[options="header"]
|=======================
|Framework | Image size | Time to healthy | Memory usage (before) | Throughput | Average latency | Memory usage (after )
|helidon-se-reactive | 231.0MB | 0m2.016s🥉 | 148.1MiB | 885.052 requests/second | 25.628 milliseconds | 788.3MiB
|helidon-se-reactive-native | 33.6MB🥇 | 0m0.676s🥈 | 60.73MiB🥉 | 729.026 requests/second | 32.379 milliseconds | 853.7MiB
|micronaut-imperative | 362.0MB | 0m2.581s | 169.9MiB | 1006.241 requests/second | 19.904 milliseconds🥈 | 799.7MiB
|micronaut-reactive | 362.0MB | 0m2.579s | 172.00MiB | 147.376 requests/second | 135.795 milliseconds | 693.0MiB
|micronaut-reactive-native | 91.2MB🥈 | 0m0.594s🥇 | 68.30MiB | 135.618 requests/second | 147.520 milliseconds | 962.3MiB
|quarkus-imperative | 464.0MB | 0m4.283s | 103.8MiB | 1095.802 requests/second🥉 | 18.272 milliseconds🥇 | 666.6MiB
|quarkus-imperative-native | 150.0MB | 0m3.316s | 26.09MiB🥇 | 996.390 requests/second | 20.096 milliseconds🥉 | 699.3MiB
|quarkus-ogm | 467.0MB | 0m4.335s | 111.7MiB | 622.312 requests/second | 32.175 milliseconds | 908.1MiB
|quarkus-reactive | 466.0MB | 0m4.411s | 117.0MiB | 487.273 requests/second | 41.087 milliseconds | 665.8MiB
|quarkus-reactive-native | 151.0MB | 0m3.353s | 26.42MiB🥈 | 446.196 requests/second | 44.868 milliseconds | 650.8MiB
|spring-boot23-with-sdn-ogm | 290.0MB | 0m4.719s | 197.40MiB | 49.789 requests/second | 400.856 milliseconds | 411.4MiB🥇
|spring-boot24-with-sdn-ogm | 286.0MB | 0m4.843s | 206.10MiB | 42.680 requests/second | 468.021 milliseconds | 610.5MiB
|spring-data-imperative | 300.0MB | 0m6.105s | 236.90MiB | 709.336 requests/second | 28.226 milliseconds | 797.1MiB
|spring-data-imperative-native | 134.0MB🥉 | 0m3.462s | 71.08MiB | 605.255 requests/second | 33.078 milliseconds | 722.0MiB
|spring-data-reactive | 302.0MB | 0m6.268s | 241.50MiB | 673.214 requests/second | 29.762 milliseconds | 738MiB
|spring-plain-imperative | 297.0MB | 0m6.066s | 197.80MiB | 1258.016 requests/second🥈 | 15.917 milliseconds | 414.6MiB🥈
|spring-plain-reactive | 299.0MB | 0m5.945s | 253.40Mi B | 1272.513 requests/second🥇 | 15.738 milliseconds | 588.4MiB🥉
|=======================Numbers taken on a MacBook Pro with 2.4Ghz Intel Core i9 and 32 GB Ram.
Docker set to use at Max 8 "CPUs" and 8GiB memory in total.
The numbers for `quarkus-ogm` reflect the state prior to commit 7e74c8e1c373cb72715a2e095b15f1c95b9d109f.