Spring Data Initial Concept
A brief discussion on the Initial Concept of Spring Data is described in this springcavaj – Spring Data page with an architecture diagram.
This guide will provide you the understanding of Spring Data to build an application that stores and retrieves data using Neo4j DB as the underlying database (Neo4j – A Cypher DB). While a lot of developers or Architects have a confusion what will be the advantage of using Neo4j database. Here’s a discussion on Stackoverflow the pros and cons of using Neo4j Database.
New features available with latest version of Neo4j
- It provides support for both imperative and reactive application development
- Lightweight mapping with built-in OGM (Object Graph Mapping) library
- Immutable entities (for both Java and Kotlin language)
- New Neo4j client and reactive client
Neo4j Default Schema
Before making the hands dirty, I want to provide an information about an example DBMS, as provided by Neo4j. This example DBMS is called Movie DB. Let’s provide an overview of Movie DB.
Overview of Movie DB
In Neo4j DB there is no concept of having Tables. They consists of Nodes. The Movie DB consists of 2 nodes as Movie and Person. While you can make relationship between Movie and Person that who are the actors in the so called Movie (The Matrix). A sample query is provided below:
MATCH (m:Movie)<-[:ACTED_IN]-(a:Person) WHERE m.title =~ (‘(?i).‘+$title+’.‘) RETURN a.name
Here you can see that I have passed the Movie Title as Input Parameter to the Query and I am making a relationship between Movie and Person by using the ACTED_IN keyword. This query will return a list of Names who has acted in that movie.
More details on Neo4j Queries are provided in this link. Have a look on it.
Advantage of using Spring Data with Neo4j
- No-code Repositories – One of the most popular persistence-related patterns is the Repository Pattern. It helps the developer to only focus on the implementation of business logic and hides the data store specific implementation details.
import org.springframework.data.neo4j.repository.Neo4jRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface SpringDataNeo4jMovieRestRepository extends Neo4jRepository<Movie, Long>
Here, you can see that I have used Neo4jRepository to perform the CRUD operations on the entity – Person. Because in this example, I have used Neo4j DB as the underlying database. This Neo4j DB provides an Example Database named as Movie DB. Neo4jRepository extends PagingAndSortingRepository and QueryByExampleExecutor . By this implementation you will get the default implementations of methods like:
- To insert, update and delete one or more User
- To find one or more User by their primary keys
- To count, get and delete all Users
- To check if a User with a given primary key already exists or not
- Reduce boilerplate code – Spring-Data provides a default implementation for each method like read or write operations.
- Generated Queries – Spring-Data generates the database queries based on their method names. You just need to define a method in your repository interface with a name that starts with findBy.
@Repository
public interface SpringDataNeo4jMovieRestRepository extends Neo4jRepository<Movie, String> {
public Movie findByTitle(@Param("title") String title);
@Query("MATCH (m:Movie) WHERE m.title =~ ('(?i).*'+$title+'.*') RETURN m")
public Collection<Movie> findByTitleContaining(@Param("title") String title);
@Query("MATCH (m:Movie)<-[:ACTED_IN]-(a:Person) WHERE m.title =~ ('(?i).*'+$title+'.*') RETURN a.name")
public Set<String> findActorsOfAMovie(@Param("title") String title);
}
Here you can see that this Repository Interface extends Neo4jRepository and I have attached the Movie entity in the concept of Generics as the 1st argument and in the 2nd argument provide the data type of the Primary Key as defined in the entity. In my case the data type of the primary key used in Movie entity is Long.
Using Spring Data with Spring Boot and use Neo4j DB as database
We can say now that the introduction of Spring-Data makes the implementation of persistent layer much easier to a developer. For this let’s make our hands dirty by creating a project. I have created a Demo project named as spring-data-neo4j-masterclass uploaded in my personal GitHub account. One can clone the project and can test locally in their system. All the steps that are required to download the project from GitHub and running it locally are mentioned in the README.md file of spring-data-neo4j-masterclass repository.
- Brief Description – I have developed a project named as spring-data-neo4j-masterclass using Spring Boot, Maven, Java and Neo4j as underlying database. In this project I mainly focus on implementing the Spring-data with Spring-REST service. It consists of a Controller annotated with @RestController annotation, a Repository extending the Neo4jRepository and the CRUD operations to the Neo4j Database. To test the Services, I have used Postman.
- Software Used – Software required to develop this project.
- Spring Tool Suite-4.7.0-RELEASE – If latest version is available then download that one
- Apache Maven 3.6.3 – If latest version is available then download that one
- Java 8 – Not less than Java8
- Git 2.27.0 – Latest version as available
- Neo4j Deskptop 1.4.5 – Neo4j DB Server (this tool comes with the inbuilt client). Four types of clients are there as Neo4j Browser, Neo4j Bloom, Neo4j ETL Tool and Terminal.
- Postman v8.3.0 – To test the REST Service
- List of the above software and their download link with the installation steps are briefly described in the README.md file of spring-data-neo4j-masterclass repository
- Project Components – The project that I develop in support to the spring-data with Neo4j concept is a Maven project. And I have used two dependencies as spring-boot-starter-data-neo4j and neo4j-ogm-core. The reason behind this as I have used Neo4j as the underlying database.
<!-- Spring Data Neo4j Dependency -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>
<!-- MySQL Connector Dependency -->
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ogm-core</artifactId>
<version>3.1.2</version>
</dependency>
In the next step I have configured the database connection in application.properties file. You can use either application.properties or application.yml file. A wonderful concept using Spring Boot and Spring Data is that they handle the default configuration for you, you only need to override the parameters you want to change.
spring.application.name=spring-data-neo4j-crud
server.port=7112
## Database Properties
spring.neo4j.uri=bolt://localhost:7687
spring.data.neo4j.username=neo4j
spring.data.neo4j.password=Provide the password of your choice.
- Structure of the Project – To use Spring Data and its libraries in your project you need to structure it in a right way.
Spring Boot expects that all source files are located in a sub-packages of the class annotated with @SpringBootApplication. In the above sample project, I have one class named as SpringDataNeo4jRestApplication.java which has the @SpringBootApplication annotations in it. And it is the starting point of the application.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringDataNeo4jRestApplication {
public static void main(String[] args) {
SpringApplication.run(SpringDataNeo4jRestApplication.class, args);
}
}
- Spring-Data Implementation – To implement Spring-Data I have used one of its dependencies named spring-boot-starter-data-neo4j. For this you need to define one Model class which will interact with the database and a Repository interface which you will use for the CRUD operations with the database. But here I have defined 3 models to support the predefined Movie database of Neo4j. The name of the models are as follows: Movie, Person and Role.
Let’s look to the Model class first named as Movie.java
@JsonIdentityInfo(generator = JSOGGenerator.class)
@NodeEntity
public class Movie {
@Id
@GeneratedValue
private Long id;
private String title;
private Long released;
private String tagline;
@Relationship(type = "ACTED_IN", direction = Relationship.INCOMING)
private List<Role> roles;
Here I have defined the property roles as the List<Role> model and it is of a default Relationship as INCOMING. It signifies that what are the roles performed by a Person in a specific Movie. Please follow the link to know in details about Relationship.
Now lets see another Model class named as Person.java
@JsonIdentityInfo(generator = JSOGGenerator.class)
@NodeEntity
public class Person {
@Id
@GeneratedValue
private Long id;
private String name;
@Relationship(type = "ACTED_IN")
private List<Movie> movies;
Here again you can see that I have defined another Relationship with the Movie. It means that a specific person has acted in how many movies?
Lastly lets discuss on the Model class named as Role.java
@JsonIdentityInfo(generator = JSOGGenerator.class)
@RelationshipEntity(type = "ACTED_IN")
public class Role {
@Id
@GeneratedValue
private Long id;
private Collection<String> roles;
@StartNode
private Person person;
@EndNode
private Movie movie;
Here you can see the Role Model is annotated with @RelationshipEntity annotation and provide the type as ACTED_IN. And this type is used in both the Movie and Person model class to establish the relationship between the 2 models.
Lets explore on the Repository interface
@Repository
public interface SpringDataNeo4jMovieRestRepository extends Neo4jRepository<Movie, Long>{
public Movie findByTitle(@Param("title") String title);
@Query("MATCH (m:Movie) WHERE m.title =~ ('(?i).*'+$title+'.*') RETURN m")
public Collection<Movie> findByTitleContaining(@Param("title") String title);
@Query("MATCH (m:Movie)<-[:ACTED_IN]-(a:Person) WHERE m.title =~ ('(?i).*'+$title+'.*') RETURN a.name")
public Set<String> findActorsOfAMovie(@Param("title") String title);
}
Here you can see that I have defined 3 methods with one as basic and the other 2 methods annotated with @Query annotation to publish the required results.
Let’s discuss on the above 3 methods.
- First method is a simple method it will return the full Movie object after you passed the movie title as input parameter to the Endpoint
- Second method used one query. The query written in the annotation is to return the Movie object supporting the Wildcard concept. That means if you pass the title as “The Matrix” it will return all the records matching the title and return the list of movies. In Movie Database they have 3 movie whose names are starting with the keyword – The Matrix, they are “The Matrix“, “The Matrix Revolutions” and “The Matrix Reloaded“. If you hit the endpoint with the desired title, it will return all the 3 results.
- And lastly another method which is annotated with @Query annotation and it returns the names of Persons who has worked in a Movie passed as input request parameter to the Endpoint. As for example if you pass the same above movie name (The Matrix) as input parameter then it will return the actors acted in that movie as [“Emil Eifrem”, “Hugo Weaving”, “Laurence Fishburne”, “Carrie-Anne Moss”, “Keanu Reeves”].
It should keep in mind that the queries related to Cypher database are very easy. If you want to explore more on it you can follow the Cypher Queries.
While in this demo application I have created another layer as known as the Service layer. In this layer I have defined one Service class named as SpringDataNeo4jMovieService.java. I added this layer to provide the business logic here. I don’t keep the logic in the Controller, rather I follow the MVC Design Pattern and put the logic in the Service layer. While if anyone doesn’t have that requirement then he/she can delete that layer and directly call the repository layer from the controller layer.
- Testing the application – In this application there is no UI component, I have used Postman to test the REST endpoint as defined in the Controller class.
- You clone the application from GitHub and set up the application locally in any one of the IDEs like Spring Tool Suite (STS) or Eclipse.
- Right click on the application
- Click the Option Run As
- Select the option Spring Boot App.
- It will start the application in the port no 7112.
- Now in Postman you can test the endpoints.
Below I have provided the list of endpoints as available in this application.
- Get Records of all Persons – localhost:7112/allPerson
- Get a specific Person record – localhost:7112/findSpecificPerson/{name}
- Get Records of all Movies – localhost:7112/allMovies
- Get a specific Movie Record – localhost:7112/findSpecificMovie/{title}
- Get a list of Movies in Wildsearch concept – localhost:7112/findMovies/{title}
- Save a Movie with the Actors act on it – localhost:7112/saveMovie – in the Body provide the JSON Object. A dummy JSON object is provided.
- Get The name of actors act in a Movie – localhost:7112/findMovieActors/{title}
Here you can see that I didn’t provide the Endpoint for Updating a Person or Movie as well as deleting a Person or Movie record from Neo4j Sample Movie Database. If you want to add that logic in your local clone application you can do that.
- Sample JSON Data – I have provided 1 sample JSON data one to insert the record in DB.
Insert Request – JSON Data
{
“personName” : “Dummy Name”,
“movieName” : “Dummy Movie”,
“releasedYear” : “2021”,
“roleName” : “Dummy Role Name”,
“tagLine” : “A tagline for the movie”
}
As a disclaimer the above data in the JSON request are all dummy, you can change accordingly. Possible data of roleName property of the above request JSON are like actor, actress, villain, producer, script writer, director, etc.
GitHub Code Link
Download the Source Code from GitHub
Common Faced Problems
Interview FAQs
Other Useful Links of Spring Data
Spring Data using RDBMS (MySQL DB) and Spring REST
Spring Data using NoSQL DB and Spring REST
Spring Data using Redis and Spring REST
Other Useful Links
Spring Apache Kafka – Producer & Consumer
Spring Kafka Confluent – Set Up
Spring Kafka Confluent – Producer & Consumer
Spring Cloud using Google Cloud SQL & Google Cloud Storage
Spring Cloud using Google Cloud Pub-Sub