1. Overview
This article is an introduction to Spring Data Neo4j, the popular graph database.
Spring Data Neo4j enables POJO based development for the Neo4j Graph Database and uses familiar Spring concepts such as a template classes for core API usage and provides an annotation based programming model.
Also, a lot of developers don’t really know if Neo4j will actually be a good match for their specific needs; here’s a solid overview on Stackoverflow discussing why to use Neo4j and the pros and cons.
2. Maven Dependencies
Let’s start by declaring the Spring Data Neo4j dependencies in the pom.xml. The below mentioned spring modules are also required for Spring Data Neo4j:
<dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-neo4j</artifactId> <version>${spring-data-neo4j.version}</version> </dependency> <dependency> <groupId>org.neo4j</groupId> <artifactId>neo4j-ogm-test</artifactId> <version>${neo4j-ogm-test.version}</version> <scope>test</scope> </dependency>
These dependencies include the required modules for testing along with an embedded server as well.
Note that the last dependency is scoped as ‘test’. But also note that, in a real world application development, you’re more likely to have an full Neo4J server running.
3. Neo4Jj Configuration
The Neo4j configuration is very straight forward and defines the connection setting for the application to connect to the server. Similar to the most of the other spring data modules, this is a spring configuration which can be defined as XML or Java configuration.
In this tutorial, we’ll use Java-based configuration only:
public static final String URL = System.getenv("NEO4J_URL") != null ? System.getenv("NEO4J_URL") : "http://neo4j:movies@localhost:7474"; @Bean public org.neo4j.ogm.config.Configuration getConfiguration() { org.neo4j.ogm.config.Configuration config = new org.neo4j.ogm.config.Configuration(); config.driverConfiguration().setDriverClassName( "org.neo4j.ogm.drivers.http.driver.HttpDriver").setURI(URL); return config; } @Override public SessionFactory getSessionFactory() { return new SessionFactory(getConfiguration(), "com.baeldung.spring.data.neo4j.domain"); }
As mentioned above, the config is simple and contains only two settings. First – the SessionFactory is referencing the models that we created to represent the data objects. Then, the connection properties with the server endpoints and access credentials.
Please note that in this example, the connection related properties are configured directly to the server; however in a production application, these should be properly externalized and part of the standard configuration of the project.
4. Neo4j Repositories
Aligning with the Spring Data framework, Neo4j supports the Spring Data repository abstraction behavior. That means accessing the underlying persistent mechanism is abstracted in the inbuilt GraphRepository where simple project can directly extend it and use the provided operations out-of-the-box.
The repositories are extensible by annotated, named or derived finder methods. Support for Spring Data Neo4j Repositories are also based on Neo4jTemplate, so the underlying functionality is identical.
4.1. Creating the MovieRepository & PersonRepository
We use two repositories in this tutorial for data persistence:
@Repository public interface MovieRepository extends GraphRepository<Movie> { Movie findByTitle(@Param("title") String title); @Query("MATCH (m:Movie) WHERE m.title =~ ('(?i).*'+{title}+'.*') RETURN m") Collection<Movie> findByTitleContaining(@Param("title") String title); @Query("MATCH (m:Movie)<-[:ACTED_IN]-(a:Person) RETURN m.title as movie, collect(a.name) as cast LIMIT {limit}") List<Map<String,Object>> graph(@Param("limit") int limit); }
As you can, the repository contains some custom operations as well as the standard ones inherited from the base class.
Next we have the simpler PersonRepository, which just has the standard operations:
@Repository public interface PersonRepository extends GraphRepository <Person> { // }
You may have already noticed that PersonRepository is just the standard Spring Data interface. This is because in this simple example, it is almost sufficient to use the inbuilt operations basically as our operation set is related to the Movie entity. However you can always add custom operations here which may wrap single/ multiple inbuilt operations.
4.2. Configuring Neo4jRepositories
As the next step, we have to let Spring know the relevant repository indicating it in the Neo4jConfiguration class created in section 3.1:
@Configuration @ComponentScan("com.baeldung.spring.data.neo4j") @EnableNeo4jRepositories( basePackages = "com.baeldung.spring.data.neo4j.repostory") public class LibraryNeo4jConfiguration extends Neo4jConfiguration { // }
5. The Full Data Model
We already started looking at the data model, so let’s now lay it all out – the full Movie, Role and Person. The Person entity references the Movie entity through the Role relationship.
@NodeEntity public class Movie { @GraphId Long id; private String title; private int released; private String tagline; @Relationship(type="ACTED_IN", direction = Relationship.INCOMING) private List<Role> roles; // standard constructor, getters and setters }
Notice how we’ve annotated Movie with @NodeEntity to indicate that this class is directly mapped to a node in Neo4j.
@JsonIdentityInfo(generator=JSOGGenerator.class) @NodeEntity public class Person { @GraphId Long id; private String name; private int born; @Relationship(type = "ACTED_IN") private List<Movie> movies; // standard constructor, getters and setters } @JsonIdentityInfo(generator=JSOGGenerator.class) @RelationshipEntity(type = "ACTED_IN") public class Role { @GraphId Long id; private Collection<String> roles; @StartNode private Person person; @EndNode private Movie movie; // standard constructor, getters and setters }
Of course, these last couple of classes are similarly annotated and the -movies– reference is linking Person to Movie class by the “ACTED_IN” relationship.
6. Data Access using MovieRepository
6.1. Saving a New Movie Object
Let’s save some data – first, a new Movie, then a Person and of course a Role – including all the relation data we have as well:
Movie italianJob = new Movie(); italianJob.setTitle("The Italian Job"); italianJob.setReleased(1999); movieRepository.save(italianJob); Person mark = new Person(); mark.setName("Mark Wahlberg"); personRepository.save(mark); Role charlie = new Role(); charlie.setMovie(italianJob); charlie.setPerson(mark); Collection<String> roleNames = new HashSet(); roleNames.add("Charlie Croker"); charlie.setRoles(roleNames); List<Role> roles = new ArrayList(); roles.add(charlie); italianJob.setRoles(roles); movieRepository.save(italianJob);
6.2. Retrieving an Existing Movie Object by Title
Let’s now verify the inserted movie by retrieving it using the defined title which is a custom operation:
Movie result = movieRepository.findByTitle(title);
6.3. Retrieving an Existing Movie Object by a Part of the Title
It is possible to search to search an existing movie using a part of the title:
Collection<Movie> result = movieRepository.findByTitleContaining("Italian");
6.4. Retrieving All the Movies
All the movies can be retrieve once and can be check for the correct count:
Collection<Movie> result = (Collection<Movie>) movieRepository.findAll();
However there are number of find methods provided with default behavior which is useful for customs requirements and not all are described here.
6.5. Count the Existing Movie Objects
After inserting several movie objects, we can get exiting movie count:
long movieCount = movieRepository.count();
6.6. Deleting an Existing Movie
movieRepository.delete(movieRepository.findByTitle("The Italian Job"));
After deleting the inserted movie, we can search for the movie object and verify the result is null:
assertNull(movieRepository.findByTitle("The Italian Job"));
6.7. Delete all Inserted Data
It is possible to delete all the elements in the database making the database empty:
movieRepository.deleteAll();
The result of this operation quickly removes all data from a table.
7. Conclusion
In this tutorial, we went through the basics of Spring Data Neo4j using a very simple example.
However Neo4j is capable of catering to very advanced and complex applications having a huge set of relations and networks. And Spring Data Neo4j also offers advanced features to map annotated entity classes to the Neo4j Graph Database.
The implementation of the above code snippets and examples can be found in the GitHub project – this is an Maven based project, so it should be easy to import and run as it is.