Developer Guides Getting Started Getting Started What is a Graph Database? Intro to Graph DBs Video Series Concepts: RDBMS to Graph Concepts: NoSQL to Graph Getting Started Resources Neo4j Graph Platform Graph Platform Overview Neo4j Desktop Intro Neo4j Browser Intro… Read more →

Developer Guides

Want to Speak? Get $ back.

Spring Data Neo4j

Goals
For Java developers who use the Spring Framework or Spring Boot, this guide introduces Spring integration through the Spring Data Neo4j library. The library provides convenient access to Neo4j including object mapping, Spring Data repositories, conversion, transaction handling, and more.
Prerequisites
You should be familiar with graph database concepts and the property graph model. Having installed Neo4j and tried out the Cypher query language helps too. You should also be familiar with Spring. Knowing Spring Data and Spring Boot are both great additions to your toolbox, as well. When developing with Neo4j, please use JDK 8 or later and your favorite IDE.
Intermediate

Neo4j for Spring Users

sdn

Neo4j offers a rich set of possibilities for developers using Spring. If you are looking to use Neo4j on the JVM in general, the Java developer’s guide explains from that perspective. However, if you want to benefit from full-fledged object mapping and the other helpful support that comes with Spring Data, this guide shows how to do that with Spring Data Neo4j.

Neo4j’s Spring Data integration was the founding project of the Spring Data efforts, started by Rod Johnson and Emil Eifrem. It is built on Spring Framework and also offers Object-Graph Mapping (OGM) on top of Neo4j.

The Neo4j-OGM provides a plain, Java object-graph mapper and integrates in the Spring Data infrastructure, including Spring Data repository and annotated object-mapping support.

Spring Data Neo4j is also supported by Spring Boot. You can use the Spring Initializr page to get started with Spring Data Neo4j immediately.

Spring Data Neo4j

Though Spring Data Neo4j has been around for a long time, Neo4j’s APIs and usage evolved quickly from an embedded, Java-only database to a server solution with mostly Cypher interactions.

To use Spring Data Neo4j, all you need is the Spring Data Neo4j dependencies and a few annotations to use the object-graph mapping. Then, you can annotate your entities and define your Spring Data repositories as convenient interfaces to your persistence layer.

Features

  • Spring Boot integration
  • Annotation-based object-graph mapping
  • Interface-based repository support with annotated and derived finder methods
  • Fast class metadata scanning
  • Optimized management of data loading and change tracking for minimal data transfers
  • Multiple transports – binary protocol, HTTP, and embedded
  • Persistence lifecycle events

Quickstart

Spring Boot takes on much of the responsibility of application configuration and bootstrap, so we have chosen to take advantage of that assistance in our project for this guide. The example project code for Spring Data Neo4j is in GitHub. You can clone the repository and run the code along with this guide, or you can build the project from the ground up from the Spring Initializr page.

We will be using the movie domain that you may already be familiar with – people (actors, directors, etc) and the movies those people are involved in.

First, the dependency for Spring Data Neo4j needs to be in the dependency file to use the capabilities. Here, we are using Maven, the dependency file is a pom.xml.

Spring-Data-Neo4j dependency
<dependencies>
    <dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-data-neo4j</artifactId>
	</dependency>
</dependencies>

Entities are Nodes

The entities outline our main objects in the application. In our graph, they will be the nodes.

GraphModel

There is a Person entity and a Movie entity, so a domain class for each is needed. The first few lines of each domain class are shown below.

Nodes
@NodeEntity
public class Person {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    @Property("born")
    private int birthyear;
}

@NodeEntity
public class Movie {
    @Id
    @GeneratedValue
    private Long id;
    private String title;
    private int released;
    @Property("tagline")
    private String description;
}

Each class is annotated with @NodeEntity to use these objects as nodes in the graph. Each contains an id field that is annotated as the id and populated with a value generated by the database (@GeneratedValue). After the id, additional fields are set up to hold different information we want to capture about the object.

Now we need to add lines in each class to map the relationships between the nodes.

Relationships

In our graph data, movies and people are connected by a few different relationships – ACTED_IN, DIRECTED, etc. On the ACTED_IN relationship, we store a relationship property that contains a list of the role(s) that the person played in a specific movie. We will need to create a relationship entity to tie it to the node entities for Movie and Person and capture the additional attributes about the ACTED_IN relationship.

Relationship Entity
@RelationshipEntity(type = "ACTED_IN")
public class Role {
    @Id
    @GeneratedValue
    private Long id;
    private List<String> roles = new ArrayList<>();

    @StartNode
    private Person person;

    @EndNode
    private Movie movie;
}

We create a relationship entity called Role to connect Person to a Movie by the Role he/she played. We annotate this new class as a relationship entity (@RelationshipEntity) and specify the name of the relationship (ACTED_IN).

The Role class also has an id field that is managed by the database and a List type field to contain the possible roles that a person could play in a movie (could be more than one). Then, annotations are added to mark the Person and Movie nodes as start node and end node for the relationship.

Relationship Mapping
@NodeEntity
public class Person {
    ....

    @Relationship(type = "ACTED_IN")
    private List<Role> actedIn = new ArrayList<>();

    @Relationship(type = "DIRECTED")
    private List<Movie> directed = new ArrayList<>();
}

@NodeEntity
public class Movie {
    ....

    @Relationship(type = "ACTED_IN", direction = INCOMING)
    private List<Role> actors = new ArrayList<>();

    @Relationship(type = "DIRECTED", direction = INCOMING)
    private List<Person> directors = new ArrayList<>();
}

Reference fields in both the Person and Movie classes have annotations with @Relationship and the relationship types as ACTED_IN and DIRECTED. The direction property is outgoing by default, so we must specify that the relationships are incoming on the Movie node.

Finally, because these entities are all connected together, when we pull one entity in a request, it will pull the rest. When it pulls the other entities, it will follow the relationships back to the starting entity, which will go back to the related entities, creating a round-robin infinite recursion loop. To avoid this error, we can add an annotation to ignore certain fields when it traverses the relationships.

Avoid Request Recursion
@NodeEntity
public class Person {
    ....

    @JsonIgnoreProperties("person")
    @Relationship(type = "ACTED_IN")
    private List<Role> actedIn = new ArrayList<>();

    @JsonIgnoreProperties({"actors", "directors"})
    @Relationship(type = "DIRECTED")
    private List<Movie> directed = new ArrayList<>();
}

@NodeEntity
public class Movie {
    ....

    @JsonIgnoreProperties("movie")
    @Relationship(type = "ACTED_IN", direction = INCOMING)
    private List<Role> actors = new ArrayList<>();

    @JsonIgnoreProperties({"actedIn", "directed"})
    @Relationship(type = "DIRECTED", direction = INCOMING)
    private List<Person> directors = new ArrayList<>();
}

@RelationshipEntity(type = "ACTED_IN")
public class Role {
    ....

    @StartNode
    @JsonIgnoreProperties({"actedIn", "directed"})
    private Person person;

    @EndNode
    @JsonIgnoreProperties({"actors", "directors"})
    private Movie movie;
}

The @JsonIgnoreProperties annotation is put on all the relationship variables to ignore the fields on the next entity that connect back, avoiding infinite recursion errors and duplicate information returning. We now have our graph structure mapped in our application. This is the object-graph mapping (OGM) piece.

Repository for Queries

The repository interface allows the developer to create methods and queries for retrieving the data from the database.

Declare repository interfaces
public interface PersonRepository extends Neo4jRepository<Person, Long> {
}

public interface MovieRepository extends Neo4jRepository<Movie, Long> {
}

The interfaces extend the Neo4jRepository, which extend the Spring CRUD repository for persisting and retrieving data. With the CRUDRepository, a lot of CRUD methods (such as findOne, findAll, save, delete, etc.) come already out-of-the-box. So, without even having to specify any method, basic data access is provided.

However, we want to define a few specific methods, which are shown below.

PersonRepository
public interface PersonRepository extends Neo4jRepository<Person, Long> {
    Person getPersonByName(String name);

    Iterable<Person> findPersonByNameLike(String name);

    @Query("MATCH (am:Movie)<-[ai:ACTED_IN]-(p:Person)-[d:DIRECTED]->(dm:Movie) return p, collect(ai), collect(d), collect(am), collect(dm)")
    List<Person> getPersonsWhoActAndDirect();
}

Let’s start with our PersonRepository. The first two methods are defined in a specific pattern so that the queries can be derived for us (like the getPersonByName method). These will allow us to search for specific people in our graph by providing a name or search string to the request to retrieve either a single result (getPersonByName) or a list of potential matches (findPersonByNameLike).

Other methods must be specifically written and annotated with @Query, which is how our last query is defined. In this case, we want to retrieve Person nodes who have acted in and directed movies. The custom query we defined using Cypher will retrieve the Person results, along with that individual’s relationships and movies.

For more information, see the Spring Data Neo4j documentation.

In our MovieRepository, we will only define a couple of methods that can be derived for us.

MovieRepository
public interface MovieRepository extends Neo4jRepository<Movie, Long> {
    Movie getMovieByTitle(String title);

    Iterable<Movie> findMovieByTitleLike(String title);
}

As with our PersonRepository above, these methods in our MovieRepository will allow us to search for a specific movie title or retrieve a list of possible matches.

Resources

For a more thorough walkthrough of the code, see the resources linked below. We also provide reactive development through the Spring Data Neo4j RX project.

Projects

Spring Data Neo4j

Authors

The Neo4j, GraphAware, and Pivotal teams.

Package

http://maven.org^

Source

https://github.com/spring-projects/spring-data-neo4j

Issues

JIRA

Docs

Reference, JavaDoc, ChangeLog

Articles

GraphAware, SDN and OGM

Video

Spring Data Neo4j 5 and OGM3

Examples

SDN Example from Spring, Spring Data Neo4j