Neo4j Spring Data & Scala

Posted by Jan Machacek

Find me on:

29-Mar-2012 19:30:59

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:

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

    &lt;context:spring-configured/&gt;
    &lt;context:annotation-config/&gt;

    &lt;neo4j:config storeDirectory="target/neo4j-db"/&gt;
    &lt;neo4j:repositories base-package="com.bigfish.kernel.repository" /&gt;

    &lt;tx:annotation-driven mode="aspectj"/&gt;

&lt;/beans&gt;

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.

Subscribe to Email Updates