• Setup Spring Data cross store with SQL and Neo4j

    Posted on April 5, 2012 by in Dev Note, Java, Spring Roo

    Polyglot persistence is all about augmenting existing applications with more specialized, sophisticated data models. The Spring Data project delivers on this vision, providing a natural way to map domain objects to both JPA-supported data stores and the Neo4j graph database.

    Here is a quick walk through of my first attempt to have a domain object’s properties saved to a database via Hibernate and to Neo4j database.

    All we’re going to do to start is have a single property called “nickname” stored in a Neo4J datastore. Nothing very useful or interesting about this but I’ll use that as a way to show the minimal setup for getting the Object properties stored in multiple databases and combined on save/access.

    In a future post I’ll hopefully do something more interesting with this very cool/useful technique.

    Let’s start with a very simple Roo script to build a full enterprise java app.

    project --topLevelPackage com.ex --projectName Neo4jTest --java 6 --packaging JAR
    jpa setup --provider HIBERNATE --database HYPERSONIC_IN_MEMORY
    
    entity jpa --class ~.domain.UserAccount --activeRecord false
    field string --fieldName firstName
    field string --fieldName nickname
    
    repository jpa --interface ~.repository.UserAccountRepository --entity ~.domain.UserAccount
    service --interface ~.service.UserAccountService --entity ~.domain.UserAccount
    
    web mvc setup
    web mvc all --package ~.web

    This will output a spring configured webapp that saves to a SQL database via Hibernate. You can run the app with the following to try it out.

    mvn jetty:run

    So now the plan is to set it up so that the nickname property of the UserAccount domain object is saved to a neo4j database while the firstname property is saved to the SQL database.

    The first step is to update the maven pom.xml file with some additional dependencies.

    Add the following to the <properties> section for the versions to use

    <spring-data-neo4j.version>2.1.0.BUILD-SNAPSHOT</spring-data-neo4j.version>
    <neo4j.version>1.6</neo4j.version>

    Add the dependencies for the neo4j database and the spring-data connector.

    <dependency>
    <groupId>org.neo4j</groupId>
    <artifactId>neo4j-kernel</artifactId>
    <version>${neo4j.version}</version>
    <type>test-jar</type>
    <scope>test</scope>
    </dependency>
    <dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-neo4j-cross-store</artifactId>
    <version>${spring-data-neo4j.version}</version>
    </dependency>

    You might have some dependency conflicts (like I did) which will necessitate the following exclusion change to the existing spring-data-jpa dependency.

    <dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-jpa</artifactId>
    <version>1.0.3.RELEASE</version>
    <exclusions>
    <exclusion>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-commons-core</artifactId>
    </exclusion>
    </exclusions>
    </dependency>

    Lastly in order to get the neo4j aspect4j weaving done when the project build is called you need to add the following to the aspectj-maven-plugin plugin under the <aspectLibraries> section.

    <aspectLibrary>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-neo4j-aspects</artifactId>
    </aspectLibrary>

    Now we need to setup the Neo4j database. Spring makes this very simple via the neo4j namespace.

    Add the namespace to applicationContext.xml

    xmlns:neo4j="http://www.springframework.org/schema/data/neo4j"

    and the schema location

    http://www.springframework.org/schema/data/neo4j http://www.springframework.org/schema/data/neo4j/spring-neo4j-2.0.xsd

    then setting up the db is just the following one line config.

    <neo4j:config storeDirectory="target/db-test" entityManagerFactory="entityManagerFactory"/>

    Now add a couple Java Annotations (which adds some more aspect code) we can get the nickname property saved to the graph Database and the firstname property saved to the SQL database.

    Add the following annotations to the UserAccount.java file.

    @NodeEntity(partial = true)
    public class UserAccount {
    
    private String firstName;
    
    @GraphProperty
    String nickname;
    }

    This marks the Java Bean as to be “partially” saved to the Neo4j database and specifically the nickname property is not saved to the SQL database.

    the last piece to do is override the default service code that Roo created that saves the Entity via Hibernate. We need to add code to also save the Entity to Neo4J.

    Add the following to UserAccountServiceImpl.java.

    public class UserAccountServiceImpl implements UserAccountService {
    
    	public void saveUserAccount(UserAccount userAccount) {
    		userAccountRepository.save(userAccount);
    		userAccount.persist();
    	}
    
    	public UserAccount updateUserAccount(UserAccount userAccount) {
    		if (userAccount != null) {
    			userAccount.persist();
    		}
    		return userAccountRepository.save(userAccount);
    	}
    
    	public UserAccount findUserAccount(Long id) {
    		if (id == null) return null;
    			final UserAccount userAccount = userAccountRepository.findOne(id);
    		if (userAccount != null) {
    			userAccount.persist();
    		}
    		return userAccount;
    	}
    }

    Once you add these methods Roo will automatically remove the same generated methods from the .aj aspect files.

    Next time I will add some relationships and code to do things that take advantage of the Graph structure of the Neo4j database. Also a useful tool is Neoclipse for visualizing the saved Neo4j data.