Scala ORP

Object-Relational Programming in Scala

Download .zip Download .tar.gz View on GitHub

Welcome to the Scala ORP Project.

This Project extends the Scala Programming Language with Object-Relational Programming (ORP). It was developed as part of a Master's thesis (available in German only) at the FernUniversität in Hagen. Most of the research papers are also written in German:

"Towards Object-Relational Programming" is written in English and explains the basic ideas:

While the relational data model and its commercial implementations have undergone considerable extensions to become object-relational, comparable advances from mainstream object-oriented programming languages seem to be rare. Complementing recent efforts to integrate SQL-like querying of memory-based collections into languages such as C# and Java, we specify a gentle extension of the object-oriented programming model with bidirectional relationships that are uniform across all uses. [...] Perhaps most remarkable is the fact that our extension of objects does not introduce relationships as a first class language construct.

This Project implements an annotation-based extension of Scala, that uses a Compiler Plugin to transform the Source Code. The API is straightforward and explained below.

API and Usage

The ORP API defines four Annotations (Types omitted for brevity):

  • @relationship: Marks an Object as Relationship with two Roles
  • @role(multiplicity = Many): A Role in a Relationship, with Multiplicity One or Many (the default)
  • @plays(roles*): A Class can play one or more Roles
  • @playsFor(role, forClass): In some cases a Class plays a Role only for one other Class

A Role can be selected with role[T], just like you select classes with classOf[T].

Example for plays

The following defines a bidirectional, one-to-many relationship between a Customer and his Contracts:

import orp.api._

@relationship
object CustomerContract {
  @role(One) trait Customer
  @role trait Contract
}

Now the Class MyCustomer plays the Role Customer and the Class MyContract plays Contract:

import orp.api._

@plays(role[CustomerContract.Customer])
class MyCustomer

@plays(role[CustomerContract.Contract])
class MyContract

You can use the classes like this:

  val customer = new MyCustomer
  val contract = new MyContract

  customer.addContract(contract)
  assert(1 == customer.getContracts.size)
  customer.removeContract(contract)
  assert(customer.getContracts.isEmpty)

Example for playsFor

Defining a part-whole relationship with additional methods:

import orp.api._

@relationship
object PartWhole {

  @role trait Part {
    def belongsTo(whole : Whole) = partOf(whole, this)
  }

  @role(One) trait Whole {
    def contains(part : Part) = partOf(this, part)
  }

  def partOf(whole : Whole, part : Part) = whole.getParts.contains(part)
}

This relationship is very wide, a Class that plays Part is typically playing this for another Class. A Car could play the Role Whole, but not for every Part, just for Parts of Class Engine. This is where the playsFor-annotation is needed: Engine plays Part for Car and Car plays Whole for Engine:

import orp.api._

@playsFor(role[PartWhole.Part], classOf[Car])
class Engine
@playsFor(role[PartWhole.Whole], classOf[Engine])
class Car

@plays(role[PartWhole.Part])
class OtherPart

Now you can only add Engines to a Car:

new Car().addPart(new Engine) // Works
new Car().addPart(new OtherPart) // Doesn't work
new Car().addEngine(new Engine) // This also works, but only for playsFor-Relations

Authors and License

This project is maintained by Simon Olofsson and licensed under the Apache 2 license. For Setup instructions and License details, see the Project page. If you have any questions or comments, please contact @simono.