In einer gut gestalteten relationalen Datenbank hat jedes Element einen Primärschlüssel, über den es innerhalb seiner Tabelle eindeutig referenziert werden kann. Das in Play2 enthaltene Data Access Layer Anorm erlaubt es, diesen Schlüssel direkt in der Deklaration des Datenmodell mit zu definieren, indem man den Datentypen des Feldes einfach mit der von Anorm mitgebrachten Option Pk wrapped.
case class MyModel(id: Pk[Long], foo: String)
Der Wert des Primärschlüssels wird dann von der Datenbank automatisch erstellt (in der Regel als Auto-Inkrement).
MyModel(anorm.NotAssigned, "bar")
Ist der Primärschlüssel bekannt erlaubt Anorm dennoch, den Wert explizit anzugeben.
MyModel(anorm.Id(1), "bar")
Damit der Json Parser von Play allerdings mit der Option klar kommt muss man einen sog. Formatter schreiben. Andernfalls weiß der Compiler nicht sicher, wie der Parser sich verhalten soll, und bricht ab. Scala erlaubt es, den Formatter implizit zu deklarieren. Er wird dann automatisch verwendet, wenn ein Formatter für Pk[Long] benötigt wird.
implicit object PkFormat extends Format[Pk[Long]] { def reads(json: JsValue): JsResult[Pk[Long]] = json.asOpt[Long].map { id => JsSuccess(Id[Long](id)) }.getOrElse(JsSuccess(anorm.NotAssigned)) } def writes(id: Pk[Long]): JsValue = id.map(JsNumber(_)).getOrElse(JsNull) }
Da man in vielen Applikationen eigentlich nicht will, dass die ID eines Datensatz außerhalb beeinflusst werden kann, sollte man bei Reads einfach einen entsprechenden Fehler generieren.
implicit object PkFormat extends Format[Pk[Long]] { def reads(json: JsValue): JsResult[Pk[Long]] = JsError() def writes(id: Pk[Long]): JsValue = id.map(JsNumber(_)).getOrElse(JsNull) }