Neo4j Spring Data & Scala

Spring Data is an excellent tool that generates implementations of repositories using the naming conventions similar to the convention used in the dynamic language runtimes such as Grails and Ruby on Rails. In this post, I am going to show you how to use Spring Data in your Scala code.
In this post, we will construct trivial application that uses the Spring Data Neo4j to persist simple User objects. The only difference is that we’ll use Scala throughout and highlight some of the sticky points of Spring Data in Scala.

We are going to be persisting the User class with two fields: id and username. Be careful to specify the type of the id field to be java.lang.Long, not scala.Long!

@NodeEntity
class User {
  @GraphId
  var id: java.lang.Long = _

  @Indexed(indexName = "username", indexType = IndexType.FULLTEXT)
  var username: String = _

  override def toString = {
    "User %d %s".format(id, username)
  }
}

To manipulate the User objects, we will use the UserRepository; and for this application, we will be happy with the methods from the GraphRepository[T]. So, our UserRepository simply becomes

trait UserRepository extends GraphRepository[User]

Really–that’s it; to use it, we’ll create command-line application that creates the Spring ApplicationContext. From that context, we get a bean that extends the UserRepository and call its methods:

object Neo4j {

  def main(args: Array[String]) {
    val ac = new GenericXmlApplicationContext(
      "classpath*:/META-INF/spring/module-context.xml")
    val ur = ac.getBean(classOf[UserRepository])

    val u = new User
    u.username = "foo"
    ur.save(u)

    for (u < - ur.findAll()) println(u)  (1)

    ac.close()
  }
}

The trouble with this code is that line (1) doesn't compile. That's because result type of the findAll method in the UserRepository is ClosableIterable[T], which does not contain the foreach[U](f: T => U) function (where T is the type in the container). Furthermore, as the name ClosableIterable[T] implies, we should close the underlying iterator when we're done iterating.

Let's deal with foreach function first. We will create another class that contains the method, but delegates the iteration to the instance of the ClosableIterable[T]:

class ScalaCloseableIterable[T](ci: ClosableIterable[T]) {

  def foreach[U](f: (T) => U) {
    val i = ci.iterator()
    try {
      while (i.hasNext) {
        val t = i.next()
        f(t)
      }
    } finally {
      ci.close()
    }
  }
}

So, we can now wrap the ClosableIterable[T] in the ScalaCloseableIterable[T] and then call the foreach function. We can now replace the problematic line (1) from the main procedure with:

object Neo4j {

  def main(args: Array[String]) {
    val ac = new GenericXmlApplicationContext(
      "classpath*:/META-INF/spring/module-context.xml")
    val ur = ac.getBean(classOf[UserRepository])

    val u = new User
    u.username = "foo"
    ur.save(u)

    for (u < - new ScalaClosableIterable(ur.findAll())) println(u)  (1)

    ac.close()
  }
}

Yuck! I certainly don't want to have to write new ScalaClosableIterable every time I want to iterate over the returned collection. Instead of writing this explicitly--and I bet you know where this is going!--we're going to add an implicit conversion from ClosableIterable[T] to ScalaClosableIterable[T], like so:

object Neo {

  implicit def toIterator[T](ci: ClosableIterable[T]) = 
    new ScalaCloseableIterable[T](ci)

  class ScalaCloseableIterable[T](ci: ClosableIterable[T]) {

    def foreach[U](f: (T) => U) {
      val i = ci.iterator()
      try {
        while (i.hasNext) {
          val t = i.next()
          f(t)
        }
      } finally {
        ci.close()
      }
    }
  }

  def main(args: Array[String]) {
    val ac = new GenericXmlApplicationContext(
      "classpath*:/META-INF/spring/module-context.xml")

    val ur = ac.getBean(classOf[UserRepository])

    val u = new User
    u.username = "foo"
    ur.save(u)

    for (u < - ur.findAll()) println(u)

    ac.close()
  }

}

To complete the picture, you need to see how we instruct Spring Data to generate the implementation of the UserRepository trait. It is the Spring XML configuration file:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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="...">

    <context:spring-configured/>
    <context:annotation-config/>

    <neo4j:config storeDirectory="target/neo4j-db"/>
    <neo4j:repositories base-package="com.bigfish.kernel.repository" />

    <tx:annotation-driven mode="aspectj"/>

</beans>

And so, this is all. You now have Scala code that takes advantage of all the features of Spring Data to generate implementations of the repository traits. You see, Spring remains useful, even in the brave new Scala world.

This entry was posted in Jan's Blog and tagged , , . Bookmark the permalink.

11 Responses to Neo4j Spring Data & Scala

  1. Pingback: Neo4j Spring Data & Scala « Another Word For It

  2. Eric says:

    Cool post on Spring and Scala. Would you mind if I repost DZone.com readers? Our community digs Neo4j, and I think they’d like a good tutorial like this one. Hope to hear from you soon!

  3. Jan Machacek says:

    Of course!

    I’ll be following this post up with one that deals with cross-persistence, combining Neo4j and MongoDB:

    @NodeEntity
    class Foo {
    @GraphId var id: java.lang.Long = _
    @RelatedDocument var picture: Attachment = _
    }

    @Document
    case class Attachment @PersistenceConstructor() (@Id id: String, @Field mimeType: String,
    @Field content: Array[Byte])

    I’ll show how to easily store and load the User instances in Neo4j with the picture property being automatically loaded from MongoDB. This will combine Scala with compile-time AOP in AspectJ.

  4. Pingback: Spring Data Neo4j and MongoDB | Cake Solutions Team Blog

  5. Mark Bower says:

    Is the complete project downloadable? The idea looks fantastic, but I could not figure out to how to get it to run.

  6. Jan Machacek says:

    Good point–leave it with me and I’ll put it on GitHub.

  7. Miguel says:

    Hello, I am trying to use neo4j with spring-data, but I am getting this exception:

    Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘neo4jTemplate’: Invocation of init method failed; nested exception is java.lang.NoSuchMethodError: scala.collection.JavaConverters$.mapAsScalaMapConverter(Ljava/util/Map;)Lscala/collection/JavaConverters$AsScala;

    For now, I’m not sure about how to “inject” the JavaConverters…

    Any ideas?

  8. Miguel says:

    Nevermind mi previous comment, the problem was the scala version, I was using 2.10, but only work with 2.9.2

    BTW, for neo4j 1.8.1 and spring-data-neo4j 2.1.0.RELEASE, cant be ClosableIterable; it should be:

    class ScalaEndResult[T](ci: EndResult[T]) {

    def foreach[U](f: (T) => U) {
    val i = ci.iterator()
    while (i.hasNext) {
    val t = i.next()
    f(t)
    }
    }
    }

  9. Connor says:

    In fact, you could simply import the implicit function to do this from the standard library: scala.collection.JavaConversions.asScalaIterator

  10. Yair says:

    Great post. Thanks.

    How would you handle Option wrappers in your case class? This is needed for optional segments in your data model.

  11. Jan Machacek says:

    The code does nothing with the underlying mapping. In fact, I now think that attempting to heavily annotate Scala classes in an attempt to use them in Java libraries is not a good idea: the Java annotations are not aware of the Scala collections and know nothing of Option. Hence, I’ve written another post: http://www.cakesolutions.net/teamblogs/2013/02/04/neo4j-and-spray-json/, which explains how we deal with Neo4j in another project.

    –J

Leave a Reply