Relations with Scala Collections and JPA

Java Collection

JPA’s One to Many and Many to Many relations require an attribute of type java.util.Collection like it is shown in the next example. This will be normally a List interface and a ArrayList implementation.

...
  @OneToMany
  public Collection organisations = new ArrayList()
...

Unfortunately the Scala Library has a different interface concept that we cannot use directly as java.util.Collection replacements. Nevertheless it should be possible to tweak EclipseLink or OpenJPA to use Scala’s collections directly. But this would not allow to use the JPA libraries out of the box.

Reusing the Java Collection in Scala

A direct replacement of the Java class and its Collection would look like this:

import java.{util => ju}
...
  @OneToMany
  @BeanProperty
  var organisations:ju.Collection[Organisation] = new ju.ArrayList[Organisation]()
...

The annotation @BeanProperty will take care of creating the corresponding getter and setter methods for collection organisations.
But in Scala we want to use Scala’s capabilities.

Property Access with JavaConversion Implicits

The Collection library supports the conversion from Java to Scala collections and vice versa by some implicit conversions defined in the JavaConversions package.
To use them I prefixed the Java collection with j__, which means ignore it. For accessing a Scala pendant I created two property access methods for property organisation. The implicit conversions will convert a Java List from and to a Scala mutable.Buffer.

import java.{util => ju}
import scala.collection.JavaConversions._
import collection.mutable.Buffer
...
  @OneToMany
  @BeanProperty
  var j__organisations:ju.List[Organisation] = new ju.ArrayList[Organisation]()

  def organisations_= (c:Buffer[Organisation]) = j__organisations = c
  def organisations:Buffer[Organisation] = j__organisations
...

As you see j__organisations uses two type modifiers List and ArrayList. Scala’s property declaration has some as well Buffer[Organisation]. They usually are the same anywhere in an application (obviously except type Organisation).
How to get rid of these type declarations?

Externalized Collection Type and Creation

Object CollectionUtil provides a factory method for creation of the Java collection and provides the type of collection for defining a entity property. So we have one place for refining Java’s collection type.

object CollectionUtil {
  def :+[T]:ju.List[T] = new ju.ArrayList[T]
}

import CollectionUtil._
...
  @OneToMany
  @BeanProperty
  var j__organisations = :+[Organisation]
...

Delegating Implicits and Additional Types

This type declaration can be done for the Scala collection as well. Together with a delegation of the required implicits the code looks like this:

import java.{util => ju}
import scala.collection.{JavaConversions => conv}
import collection.{mutable => cm}

object CollectionUtil {
  /**
   * type of the used Scala Collection
   */
  type SC[T] = cm.Buffer[T]

  /**
   * create new instance of the Java Collection
   * List will be implicitly converted to Buffer by JavaConversion
   * and vice versa
   */
  def :+[T]: ju.List[T] = new ju.ArrayList[T]

  /**
   * just to limit the implicit conversions and the
   * required imports
   */
  implicit def asList[A](b: cm.Buffer[A]): ju.List[A] = conv.asList[A](b)
  implicit def asBuffer[A](l: ju.List[A]): cm.Buffer[A] = conv.asBuffer[A](l)
}

import CollectionUtil._

...
  @OneToMany
  @BeanProperty
  var j__organisations = :+[Organisation]

  def organisations_=(c: SC[Organisation]) = j__organisations = c

  def organisations: SC[Organisation] = j__organisations
...

Example Usage Code

     withTrxAndCommit {
        val oa = new EntityWithScalaCollections
        OrganisationIdList.foreach {
          id =>
          val org = find[Organisation](id).get
          oa.organisations + org
        }
        persistAndFlush(oa)
        oa.getId
     }

Comments are closed.