Graphs in the Cloud: Spring + Neo4j on Heroku

Last week I hosted a webinar about running Java apps on Heroku that use the Spring Framework and the [Neo4j graph database][1]. Here is the recording of that webinar:

In the webinar I began by deploying a copy of the Spring MVC + Hibernate template app from [][2] on Heroku. Then I made a few modifications to the app to switch the persistence from Hibernate / JPA to Neo4j. You can get the [full source code on GitHub][3].

Here is a quick recap of what I did to switch the template app to use Neo4j:

  1. Added the [Neo4j Heroku Add-on][4]: ```bash heroku addons:add neo4j

  2. Added the Spring Data Neo4j dependencies (optionally you can remove the unused JPA dependencies) to the "[pom.xml][5]" Maven build descriptor: ```xml
  1. Modified the “[src/main/java/com/example/service/][6]” interface to use the Neo4j GraphRepository: ```java package com.example.service;

import com.example.model.Person; import;

public interface PersonService extends GraphRepository {


  4. Removed the unneeded "src/main/java/com/example/service/" DAO.
  5. Modified the "[src/main/java/com/example/model/][7]" POJO to be a **@NodeEntity** (instead of JPA Entity) and switched the "id" primary key property to be a **Long** annotated as a **@GraphId**: ```java
package com.example.model;


public class Person {

    private Long id;

    // the rest is omitted
  1. Modified the “[src/main/java/com/example/controller/][8]” Spring MVC controller to use the new “PersonService”, take a Long parameter in the “deletePerson” method, and make the “deletePerson” and “addPerson” methods transactional: ```java package com.example.controller;

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod;

import com.example.model.Person; import com.example.service.PersonService;

import java.util.Map;

@Controller public class PersonController {

private PersonService personService;

public String listPeople(Map<String, Object> map) {
    map.put("person", new Person());
    map.put("peopleList", personService.findAll().iterator());
    return "people";

@RequestMapping(value = "/add", method = RequestMethod.POST)
public String addPerson(@ModelAttribute("person") Person person) {;
    return "redirect:/people/";

public String deletePerson(@PathVariable("personId") Long personId) {
    return "redirect:/people/";


  7. Then I modified the "[src/main/resources/applicationContext.xml][9]" Spring config file to use a file for local Neo4j storage in the "default" profile and then in the "prod" profile the "NEO4J\_REST\_URL", "NEO4J\_LOGIN", and "NEO4J\_PASSWORD" environment variables are used to connect to the Neo4j Heroku add-on service: ```xml
<?xml  version="1.0" encoding="UTF-8"?>
<beans xmlns=""

    <context:annotation-config />
    <context:spring-configured />
    <context:component-scan base-package="com.example" />

    <neo4j:repositories base-package="com.example.service"/>


    <bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />

    <tx:annotation-driven />

    <beans profile="default">
        <neo4j:config storeDirectory="target/neo4j-db"/>

    <beans profile="prod">
        <bean class="" id="graphDatabaseService">
            <constructor-arg index="0" value="#{systemEnvironment['NEO4J_REST_URL']}"/>
            <constructor-arg index="1" value="#{systemEnvironment['NEO4J_LOGIN']}"/>
            <constructor-arg index="2" value="#{systemEnvironment['NEO4J_PASSWORD']}"/>
        <neo4j:config graphDatabaseService="graphDatabaseService"/>

  1. After testing my changes locally (which actually didn’t work in my webinar due to a problem with Eclipse) I committed my changes to the git repo and pushed them to Heroku: ```bash git push heroku master

If you want to just skip to a working example on the cloud, simply follow the instructions in the [project README][10].

Hopefully that helps you get started with Neo4j and Java applications on the cloud!

BTW: If you watched the webinar, you probably noticed that my Maven and Eclipse were misbehaving. Turns out that M2E didn't read my Maven config and all I had to do was right-click on the project, select Maven, and then Update Project Configuration. That got everything back in sync. My excuse for not being able to figure that out during the demo... I usually use IntelliJ IDEA. :)