Lift Mapperを拡張する

Lift Mapperの形が複雑に見えるので、Rails的タイムスタンプ付与を例題に、Mapper traitの拡張例を示しておきます。慣れれば簡単で、定型として覚えておくと便利なのではないでしょうか。

Lift標準のORマッパーの例

package myproject.model

import _root_.net.liftweb.mapper._
import _root_.net.liftweb.util._

class Product extends LongKeyedMapper[Product] with IdPK {
 def getSingleton = Product
 object name extends MappedString(this, 255)
 object price extends MappedInt(this)
 def destroy = this.delete_!
}

object Product extends Product with LongKeyedMetaMapper[Product]{
 override def dbTableName = "products"
 def findByName(name:String) = findAll(By(Product.name,name))
}

LongKeyedMapper/LongKeyedMetaMapperは、Long型のIDを自動採番するマッパーです。

lift mapperは、Mapper classMetaMapper objectのペアで表現します。一般的な言語のstaticメソッド/classメソッド(singleton)は、scalaではobjectで表現します。

find系のsingletonメソッドはMetaMapper object
エンティティに対するインスタンスメソッドはMapper class側に定義します。

使い方は以下のようなイメージになります。

Product.findAll.foreach( prod => prod.destroy )


Lift Mapperの拡張

ここで、LongKeyedMapper/LongKeyedMetaMapperをちょっと拡張してみます。Railsでよく知られている、エンティティ更新のタイムスタンプを自動付与するしくみを作ってみます。

package myproject.model

import _root_.java.util.Date
import _root_.net.liftweb.mapper._

trait MyCommonMapper[A <: MyCommonMapper[A]] extends LongKeyedMapper[A] {
 self: A =>
 object updated_at extends MappedDateTime(this)
 object created_at extends MappedDateTime(this)
}

trait MyCommonMetaMapper[A <: MyCommonMapper[A]] extends LongKeyedMetaMapper[A] {
 self: A =>
 val fill_updated_at = (a:A) => { a.updated_at(new Date);() }
 val fill_created_at = (a:A) => { a.created_at(new Date);() }
 var before_create = List( fill_created_at )
 var before_update = List( fill_updated_at )

 override def beforeCreate = before_create
 override def beforeSave = before_update
}

Mapper側でカラムを定義して、MetaMapper側でHook内に更新ロジックを定義します。
ちょっと型パラメータの関係が絡み合っていて困惑してしまうかもしれないですが、ここはめげずに観察しましょう。

これを継承したProductは、

class Product extends MyCommonMapper[Product] with IdPK {
  : //同様
}

object Product extends Product with MyCommonMetaMapper[Product]{
 // create hookの追加
 before_create :::= List( ( self:Product )=> println(self) )
 : //同様
}

これで、create/save時に、create_at/updated_atが自動で更新されます。


よりscalaらしく

継承ではなくMix-inで拡張するなら、LongKeyed はジャマになるので、もっと上位のMapper/MetaMapperを継承します。

trait TimestampMapper[A <: TimestampMapper[A]] extends Mapper[A] {
 //MyCommonMapperと同様
}
trait TimestampMetaMapper[A <: TimestampMapper[A]] extends MetaMapper[A] {
 //MyCommonMetaMapperと同様
}

class Product extends LongKeyedMapper[Product]
 with TimestampMapper[Product] with IdPK {
 //同様
}
object Product extends Product with LongKeyedMetaMapper[Product]
 with TimestampMetaMapper[Product]{
 //同様
}

型が複雑で拡張しずらく感じるかもしれませんが、定型だと思っておけば大丈夫。なんでこんな型になっているのかを考えてみると
view boundsやselftypeなど、scalaの不思議機能を掘り下げられると思います。


同じカテゴリのエントリ
1.Lift再入門 / 8.javascriptからsubmitできない / 7.Ajax Form / 6.Radio、Checkboxについて / 5.行列型の編集FORM / 4.サーバーサイドバリデーションとサーバサイド関数 / 3.ログインFORM - S.param使ったら負け / 2.Snippetメソッドとして許される型 / sbt0.12.xで依存jar抽出タスク / scala2.10+lift2.5+NetBeans7.2 / Scalaで入門関数プログラミング / reactive-webを試してみました / Lift2.2M1のテンプレート機能 / Scala Compiler Plugin / View Bound/Context Bound / ScalaZa01参加してきました / Akka Frameworkチュートリアルの次 / Akka Frameworkチュートリアルその2 / Akka Frameworkチュートリアル / LiftでJCaptcha / Url Rewrite Filter / sbt-android-plugin / Android SDK for Scala / 祝Lift2.0リリース / Liftの携帯対応まとめ / Scala2.8への移行 / Lift 2.0-scala280-SNAPSHOT/sbt0.7.1 / Scalaお絵かき環境 - Kojo / Lift+Quartzでバッチ / Scala&Liftを採用した理由 / Liftでdate_select系ヘルパーを作る / LiftでAjax / LiftのSubmitかしこい / lift-mapperのpaginateを使う / snippetをspecする / Lift Mapperを拡張する / LiftのDBをMySQLに / Liftプロジェクト環境を整える / Scala本読み比べてみました / NetBeans6.7&scala / じつはScalaはライトな言語 / Scalaバザ~ル / lift1.0所感 / specsを読む / implicit def / ScalaならNetBeansがサイコー / scala勉強会@東北がスタート / それでも俺はLiftをやるってのか / Scala&Liftセットアップ / ブログリニューアル /
コメント

コメントしてください

closed.