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:
- "Wie die Objektorientierung relationaler werden sollte: Eine Analyse aus Sicht der Datenmodellierung"
- "Objektrelationale Programmierung"
"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.