Reference

Introduction

Neo4j-OGM is a fast object-graph mapping library for Neo4j, optimised for server-based installations utilising Cypher.

It aims to simplify development with the Neo4j graph database and like JPA, it uses annotations on simple POJO domain objects to do so.

With a focus on performance, Neo4j-OGM introduces a number of innovations, including:

  • non-reflection based classpath scanning for much faster startup times

  • variable-depth persistence to allow you to fine-tune requests according to the characteristics of your graph

  • smart object-mapping to reduce redundant requests to the database, improve latency and minimise wasted CPU cycles

  • user-definable session lifetimes, helping you to strike a balance between memory-usage and server request efficiency in your applications.

Overview

This reference documentation is broken down into sections to help the user understand specifics of how Neo4j-OGM works.

Getting started

Getting started can sometimes be a chore. What versions of Neo4j-OGM do you need? Where do you get them from? What build tool should you use? Getting Started is the perfect place to well…​ get started!

Configuration

Drivers, logging, properties, configuration via Java. How to make sense of all the options? Configuration has got you covered.

Annotating your Domain Objects

To get started with your Neo4j-OGM application, you need only your domain model and the annotations provided by the library. You use annotations to mark domain objects to be reflected by nodes and relationships of the graph database. For individual fields the annotations allow you to declare how they should be processed and mapped to the graph. For property fields and references to other entities this is straightforward. Because Neo4j is a schema-free database, Neo4j-OGM uses a simple mechanism to map Java types to Neo4j nodes using labels. Relationships between entities are first class citizens in a graph database and therefore worth a section of it’s own describing their usage in Neo4j-OGM.

Connecting to the Database

Managing how you connect to the database is important. Connecting to the Database has all the details on what needs to happen to get you up and running.

Interacting with the Graph Model

Neo4j-OGM offers a session for interacting with the mapped entities and the Neo4j graph database. Neo4j uses transactions to guarantee the integrity of your data and Neo4j-OGM supports this fully. The implications of this are described in the transactions section. To use advanced functionality like Cypher queries, a basic understanding of the graph data model is required. The graph data model is explained in the chapter about in the introduction chapter.

Type Conversion

Neo4j-OGM provides support for default and bespoke type conversions, which allow you to configure how certain data types are mapped to nodes or relationships in Neo4j. See Type Conversion for more details.

Filtering your domain objects

Filters provides a simple API to append criteria to your stock Session.loadX() behaviour. This is covered in more detail in Filters.

Reacting to Persistence events

The Events mechanism allows users to register event listeners for handling persistence events related both to top-level objects being saved as well as connected objects. Event handling discusses all the aspects of working with events.

Testing in your application

Sometimes you want to be able to run your tests against an in-memory version of Neo4j-OGM. Testing goes into more detail of how to set that up.

Getting Started

Versions

Consult the version table to determine which version of Neo4j-OGM to use with a particular version of Neo4j and related technologies.

Compatibility

Neo4j-OGM Version Neo4j Version1

4.0.x2

4.4.x6, 5.x

3.2.x

3.2.x, 3.3.x, 3.4.x, 3.5.x, 4.0.x2, 4.1.x2, 4.2.x2, 4.3.x2,5, 4.4.x2,5

3.1.x3

3.1.x, 3.2.x, 3.3.x, 3.4.x

3.0.x3

3.1.9, 3.2.12, 3.3.4, 3.4.4

2.1.x4

2.3.9, 3.0.11, 3.1.6

2.0.24

2.3.8, 3.0.7

2.0.14

2.2.x, 2.3.x

1 The latest supported bugfix versions.

2 These versions only support connections via Bolt.

3 These versions are no longer actively developed.

4 These versions are no longer actively developed or supported.

5 Neo4j-OGM 3.2.24+ only.

6 Technical working but not officially supported

Dependency Management

For building an application, your build automation tool needs to be configured to include the Neo4j-OGM dependencies.

Neo4j-OGM dependencies consist of neo4j-ogm-core, together with the relevant dependency declarations on the driver you want to use. Neo4j-OGM 4.x provides only support for the Bolt driver, but for compatibility reasons you have to declare the dependency:

  • neo4j-ogm-bolt-driver - Uses native Bolt protocol to communicate between Neo4j-OGM and a remote Neo4j instance.

  • neo4j-ogm-bolt-native-types - Support for all of Neo4j’s property types through the Bolt protocol.

Neo4j-OGM projects can be built using Maven, Gradle or any other build system that utilises Maven’s artifact repository structure.

Maven

In the <dependencies> section of your pom.xml add the following:

Maven dependencies
<dependency>
    <groupId>org.neo4j</groupId>
    <artifactId>neo4j-ogm-core</artifactId>
    <version>4.0.19</version>
    <scope>compile</scope>
</dependency>

<dependency>
    <groupId>org.neo4j</groupId>
    <artifactId>neo4j-ogm-bolt-driver</artifactId>
    <version>4.0.19</version>
    <scope>runtime</scope>
</dependency>

Please also have a look at the native type system to take advantage of Neo4j-OGM’s support for native temporal and spatial types.

Gradle

Ensure the following dependencies are added to you build.gradle:

Gradle dependencies
dependencies {
    compile 'org.neo4j:neo4j-ogm-core:4.0.19'
    runtime 'org.neo4j:neo4j-ogm-bolt-driver:4.0.19'
}

Configuration

Configuration method

There are several ways to supply configuration to Neo4j-OGM:

  • using a properties file

  • programmatically using Java

  • by providing an already configured Neo4j Java driver instance

These methods are described below. They are also available as code in the examples.

Using a properties file

Properties file on classpath:

ConfigurationSource props = new ClasspathConfigurationSource("my.properties");
Configuration configuration = new Configuration.Builder(props).build();

Properties file on filesystem:

ConfigurationSource props = new FileConfigurationSource("/etc/my.properties");
Configuration configuration = new Configuration.Builder(props).build();

Programmatically using Java

In cases where you are not able to provide configuration via a properties file you can configure Neo4j-OGM programmatically instead.

The Configuration object provides a fluent API to set various configuration options. This object then needs to be supplied to the SessionFactory constructor in order to be configured.

By providing a Neo4j driver instance

Just configure the driver as you would do for direct access to the database, and pass the driver instance to the session factory.

This method allows the greatest flexibility and gives you access to the full range of low level configuration options.

Example providing a bolt driver instance to Neo4j-OGM
org.neo4j.driver.Driver nativeDriver = ...;
Driver ogmDriver = new BoltDriver(nativeDriver);
new SessionFactory(ogmDriver, ...);

Driver Configuration

For configuration through properties file or configuration builder the driver is automatically inferred from given URI. Empty URI means embedded driver with impermanent database.

Bolt Driver

Note that for the URI, if no port is specified, the default Bolt port of 7687 is used. Otherwise, a port can be specified with bolt://neo4j:password@localhost:1234.

Also, the bolt driver allows you to define a connection pool size, which refers to the maximum number of sessions per URL. This property is optional and defaults to 50.

Table 1. Basic Bolt Driver Configuration
ogm.properties Java Configuration
URI=bolt://neo4j:password@localhost
connection.pool.size=150
Configuration configuration = new Configuration.Builder()
        .uri("bolt://neo4j:password@localhost")
        .setConnectionPoolSize(150)
        .build()

A timeout to the database with the Bolt driver can be set by updating your Database’s neo4j.conf. The exact setting to change can be found here.

Credentials

If you are using the Bolt Driver you have a number of different ways to supply credentials to the Driver Configuration.

ogm.properties Java Configuration
username="user"
password="password"
Configuration configuration = new Configuration.Builder()
             .uri("bolt://localhost")
             .credentials("user", "password")
             .build()

Note: Currently only Basic Authentication is supported by Neo4j-OGM. If you need to use more advanced authentication scheme, use the native driver configuration method.

Transport Layer Security (TLS/SSL)

The Bolt and HTTP drivers also allow you to connect to Neo4j over a secure channel. These rely on Transport Layer Security (aka TLS/SSL) and require the installation of a signed certificate on the server.

In certain situations (e.g. some cloud environments) it may not be possible to install a signed certificate even though you still want to use an encrypted connection.

To support this, both drivers have configuration settings allowing you to bypass certificate checking, although they differ in their implementation.

Both of these strategies leave you vulnerable to a MITM attack. You should probably not use them unless your servers are behind a secure firewall.
Bolt
ogm.properties Java Configuration
#Encryption level (TLS), optional, defaults to REQUIRED.
#Valid values are NONE,REQUIRED
encryption.level=REQUIRED

#Trust strategy, optional, not used if not specified.
#Valid values are TRUST_ON_FIRST_USE,TRUST_SIGNED_CERTIFICATES
trust.strategy=TRUST_ON_FIRST_USE

#Trust certificate file, required if trust.strategy is specified
trust.certificate.file=/tmp/cert
Configuration config = new Configuration.Builder()
    ...
    .encryptionLevel("REQUIRED")
    .trustStrategy("TRUST_ON_FIRST_USE")
    .trustCertFile("/tmp/cert")
    .build();

TRUST_ON_FIRST_USE means that the Bolt Driver will trust the first connection to a host to be safe and intentional. On subsequent connections, the driver will verify that the host is the same as on that first connection.

Bolt connection testing

In order to prevent some network problems while accessing a remote database, you may want to tell the Bolt driver to test connections from the connection pool.

This is particularly useful when there are firewalls between the application tier and the database.

You can do that with the connection liveness parameter which indicates the interval at which the connections will be tested. A value of 0 indicates that the connection will always be tested. A negative value indicates that the connection will never be tested.

ogm.properties Java Configuration
# interval, in milliseconds, to check for stale db connections (test-on-borrow)
connection.liveness.check.timeout=1000
Configuration config = new Configuration.Builder()
    ...
    .connectionLivenessCheckTimeout(1000)
    .build();

Eager connection verification

OGM by default does not connect to Neo4j server on application startup. This allows you to start the application and database independently and Neo4j will be accessed on first read/write. To change this behaviour set the property verify.connection (or Builder.verifyConnection(boolean)) to true. This settings is valid only for Bolt drivers.

Logging

Neo4j-OGM uses SLF4J to log statements. In production, you can set the log level in a file called logback.xml to be found at the root of the classpath. Please see the Logback manual for further details.

An important logger is the BoltResponse logger. It has multiple "sub-logger" for Neo4j notification categories that may come up when using e.g. deprecated features. An overview can be seen in the following list.

  • org.neo4j.ogm.drivers.bolt.response.BoltResponse.performance

  • org.neo4j.ogm.drivers.bolt.response.BoltResponse.hint

  • org.neo4j.ogm.drivers.bolt.response.BoltResponse.unrecognized

  • org.neo4j.ogm.drivers.bolt.response.BoltResponse.unsupported

  • org.neo4j.ogm.drivers.bolt.response.BoltResponse.deprecation

  • org.neo4j.ogm.drivers.bolt.response.BoltResponse.generic

  • org.neo4j.ogm.drivers.bolt.response.BoltResponse.security

  • org.neo4j.ogm.drivers.bolt.response.BoltResponse.topology

You can still use the org.neo4j.ogm.drivers.bolt.response.BoltResponse logger as the main logger and just adjust the details in some details to your needs.

Class loading precedence

In some scenarios and environments (Spring Boot’s @Async annotated classes/methods, CompletableFuture usage, etc.) , it is necessary to declare the used class loading precedence for Neo4j-OGM to use. As default, it uses the current thread’s context class loader. To change this behaviour, the OGM_CLASS_LOADER has to be set only once for the Configuration class. This can be done during configuration of your application or similar.

Configuration.setClassLoaderPrecedence(Configuration.ClassLoaderPrecedence.OGM_CLASS_LOADER);

Annotating Entities

@NodeEntity: The basic building block

The @NodeEntity annotation is used to declare that a POJO class is an entity backed by a node in the graph database. Entities handled by Neo4j-OGM must have one empty public constructor to allow the library to construct the objects.

Fields on the entity are by default mapped to properties of the node. Fields referencing other node entities (or collections thereof) are linked with relationships.

@NodeEntity annotations are inherited from super-types and interfaces. It is not necessary to annotate your domain objects at every inheritance level.

Entity fields can be annotated with annotations like @Property, @Id, @GeneratedValue, @Transient or @Relationship. All annotations live in the org.neo4j.ogm.annotation package. Marking a field with the transient modifier has the same effect as annotating it with @Transient; it won’t be persisted to the graph database.

Persisting an annotated entity
@NodeEntity
public class Actor extends DomainObject {

   @Id @GeneratedValue
   private Long id;

   @Property(name="name")
   private String fullName;

   @Property("age") // using value attribute to have a shorter definition
   private int age;

   @Relationship(type="ACTED_IN", direction=Relationship.Direction.OUTGOING)
   private List<Movie> filmography;

}

@NodeEntity(label="Film")
public class Movie {

   @Id @GeneratedValue Long id;

   @Property(name="title")
   private String name;

}

The default label is the simple class name of the annotated entity. There are some rules to determine if parent classes also contribute their label to the child class:

  • the parent class is a non-abstract class (the existing of @NodeEntity is optional)

  • the parent class is an abstract class and has a @NodeEntity annotation

  • java.lang.Object will be ignored

  • interfaces do not create an additional label

If the label (as you can see in the example above) or the value attribute of the @NodeEntity annotation is set it will replace the default label applied to the node in the database.

Saving a simple object graph containing one actor and one film using the above annotated objects would result in the following being persisted in Neo4j.

(:Actor:DomainObject {name:'Tom Cruise'})-[:ACTED_IN]->(:Film {title:'Mission Impossible'})

When annotating your objects, you can choose to NOT apply the annotations on the fields. OGM will then use conventions to determine property names in the database for each field.

Persisting a non-annotated entity
public class Actor extends DomainObject {

   private Long id;
   private String fullName;
   private List<Movie> filmography;

}

public class Movie {

   private Long id;
   private String name;

}

In this case, a graph similar to the following would be persisted.

(:Actor:DomainObject {fullName:'Tom Cruise'})-[:FILMOGRAPHY]->(:Movie {name:'Mission Impossible'})

While this will map successfully to the database, it’s important to understand that the names of the properties and relationship types are tightly coupled to the class’s member names. Renaming any of these fields will cause parts of the graph to map incorrectly, hence the recommendation to use annotations.

Please read Non-annotated properties and best practices for more details and best practices on this.

@Properties: dynamically mapping properties to graph

A @Properties