Spring Data Neo4j example
In this example we shall demonstrate how to integrate Neo4J, a graph based NoSql database with Spring Data.
1. Introduction
Neo4j is an open source, graph based NoSQL database developed in Java and Scala. Like traditional relational Databases, Neo4J offers support to ACID properties. The graph based databases find their uses in use cases where the focus is strongly on inter-relationship between the entities of the domain like match-making,social networks, routing.
2. Installation
Neo4J can be downloaded from here. For the purpose of this demo we are using the community edition.
The user can install the Neo4j by simply following the steps provided by the installer, downloaded earlier.
3. Project Set-up
We shall use Maven
to setup our project. Open Eclipse and create a simple Maven project and check the skip archetype selection
checkbox on the dialogue box that appears. Replace the content of the existing pom.xml
with the one provided below:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.jcg.springNeo4J</groupId> <artifactId>SpringDataNeo4JExample</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-neo4j</artifactId> <version>4.0.0.RELEASE</version> </dependency> <dependency> <groupId> org.neo4j </groupId> <artifactId> neo4j-kernel </artifactId> <version> 2.1.3 </version> </dependency> <dependency> <groupId> javax.transaction </groupId> <artifactId> jta </artifactId> <version> 1.1 </version> </dependency> <dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>1.0.0.GA</version> </dependency> </dependencies> </project>
4. Implementation
We start by creating the entity models for our example. Here are the entities:
Account.java
package com.jcg.examples.entity; import java.io.Serializable; import org.springframework.data.neo4j.annotation.GraphId; import org.springframework.data.neo4j.annotation.NodeEntity; @NodeEntity public class Account implements Serializable { private static final long serialVersionUID = -8860106787025445177L; @GraphId private Long accountId; private String accountType; private Double balance; public Long getAccountId() { return accountId; } public void setAccountId(Long accountId) { this.accountId = accountId; } public String getAccountType() { return accountType; } public void setAccountType(String accountType) { this.accountType = accountType; } public Double getBalance() { return balance; } public void setBalance(Double balance) { this.balance = balance; } @Override public String toString() { return "Account [accountId=" + accountId + ", accountType=" + accountType + ", balance=" + balance + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((accountId == null) ? 0 : accountId.hashCode()); result = prime * result + ((accountType == null) ? 0 : accountType.hashCode()); result = prime * result + ((balance == null) ? 0 : balance.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Account other = (Account) obj; if (accountId == null) { if (other.accountId != null) return false; } else if (!accountId.equals(other.accountId)) return false; if (accountType == null) { if (other.accountType != null) return false; } else if (!accountType.equals(other.accountType)) return false; if (balance == null) { if (other.balance != null) return false; } else if (!balance.equals(other.balance)) return false; return true; } }
Person.java
package com.jcg.examples.entity; import java.io.Serializable; import org.springframework.data.neo4j.annotation.Fetch; import org.springframework.data.neo4j.annotation.GraphId; import org.springframework.data.neo4j.annotation.NodeEntity; import org.springframework.data.neo4j.annotation.RelatedTo; @NodeEntity public class Person implements Serializable { private static final long serialVersionUID = -5378396373373165919L; @GraphId private Long id; private String personName; @RelatedTo @Fetch private Account account; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Account getAccount() { return account; } public void setAccount(Account account) { this.account = account; } @Override public String toString() { return "Person [id=" + id + ", account=" + account + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((account == null) ? 0 : account.hashCode()); result = prime * result + ((id == null) ? 0 : id.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (account == null) { if (other.account != null) return false; } else if (!account.equals(other.account)) return false; if (id == null) { if (other.id != null) return false; } else if (!id.equals(other.id)) return false; return true; } public String getPersonName() { return personName; } public void setPersonName(String personName) { this.personName = personName; } }
@NodeEntity
This annotation is used to mark the PoJo as Neo4J Entity.@GraphId
This marks the annotated field as the Node ID. It must be of the typejava.lang.Long
. If the field name isid
, it need not be annotated.@RelatedTo
annotation is used to relate to other entities.@Fetch
If this tag is present on a relationship property, it eagerly fetches that entity.
Now that the entities are configured, we can create the DAO layer by configuring the basic repositories:
AccountRepo.java
package com.jcg.examples.repo; import org.springframework.data.neo4j.repository.GraphRepository; import org.springframework.stereotype.Repository; import com.jcg.examples.entity.Account; @Repository public interface AccountRepo extends GraphRepository { }
PersonRepo.java
package com.jcg.examples.repo; import org.springframework.data.neo4j.repository.GraphRepository; import org.springframework.stereotype.Repository; import com.jcg.examples.entity.Person; @Repository public interface PersonRepo extends GraphRepository { }
Spring Data provides a number of inbuilt method for manipulating the Data. We need not write the queries for basic data manipulation and reading. It is achieved by extending the GraphRepository
and declaring the proper Generics as per the PoJo, which in our case is the Person
and Account
.
In case the Developer is not satisfied with the existing method, he can create his own method by specifying the Query using the @Query
annotation.
The Spring IoC Container creates an instance of this Repository and makes it available to be used as a Bean since we have annotated it with the stereotype annotation @Repository
and enabled component-scan
in the spring configuration xml.
Here’s a trivial implementation of the service layer for Person
PersonService.java
package com.jcg.examples.service; import java.util.Collection; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.jcg.examples.entity.Person; import com.jcg.examples.repo.PersonRepo; @Service public class PersonService { @Autowired private PersonRepo personRepo; @Transactional public Person save(Person person) { return personRepo.save(person); } @Transactional public void delete(Long personId) { personRepo.delete(personId); } @Transactional public Person get(Long personId) { return personRepo.findOne(personId); } @SuppressWarnings("unchecked") public Collection findAll() { return personRepo.findAll().as(Collection.class); } public PersonRepo getPersonRepo() { return personRepo; } public void setPersonRepo(PersonRepo personRepo) { this.personRepo = personRepo; } }
We have annotated the service methods with @Transactional
to wrap around the operations within a transaction boundary.
The last and the most important part is to configure the Spring Container using the spring-configuration.xml
:
spring-configuration.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:neo4j="http://www.springframework.org/schema/data/neo4j" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/data/neo4j http://www.springframework.org/schema/data/neo4j/spring-neo4j.xsd"> <context:component-scan base-package="com.jcg.examples" /> <neo4j:config storeDirectory="C:\\Users\\chandansingh\\Documents\\Neo4j" base-package="com.jcg.examples.entity"/> <neo4j:repositories base-package="com.jcg.examples.repo"/> <bean id="applicationTest" class="com.jcg.examples.main.ApplicationTest" /> </beans>
Line-13 : Declares the Neo4J store location and the location of the Neo4J entities.
Line-15 : Scan and initiate the Neo4J repositories.
Now that all is set, let’s run the application and test out the code! Here’s Application class that loads the XML file to instantiate the Spring Container and execute a few queries.
ApplicationTest.java
package com.jcg.examples.main; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.core.io.ClassPathResource; import com.jcg.examples.entity.Account; import com.jcg.examples.entity.Person; import com.jcg.examples.service.AccountService; import com.jcg.examples.service.PersonService; public class ApplicationTest { @Autowired private PersonService personService; @Autowired private AccountService accountService; public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext(new ClassPathResource("spring-configuration.xml").getPath()); ApplicationTest applicationTest = context.getBean(ApplicationTest.class); System.out.println("Starting with the Tests.."); Long personId = applicationTest.savePerson(); applicationTest.printPerson(personId); System.out.println("Finished!"); } private Long savePerson() { Person person = new Person(); person.setPersonName("Chandan Singh"); Account account = new Account(); account.setBalance(212.21); account.setAccountType("Savings"); person.setAccount(account); person = personService.save(person); System.out.println("Saved Person sucessfully!"); return person.getId(); } private void printPerson(Long personId) { System.out.println(personService.get(personId)); } public PersonService getPersonService() { return personService; } public void setPersonService(PersonService personService) { this.personService = personService; } public AccountService getAccountService() { return accountService; } public void setAccountService(AccountService accountService) { this.accountService = accountService; } }
Here’s the sample output of the program :
Starting with the Tests.. Saved Person sucessfully! Person [id=6, account=Account [accountId=7, accountType=Savings, balance=212.21]] Finished!
5. Download Source Code
In this example, we studied how we can integrate Neo4J with Spring Data.
You can download the source code of this example here: SpringDataNeo4JExample.zip