SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Scala+Liftによる実践Webアプリケーション開発

Scala+LiftフレームワークのModel

Scala+Liftによる実践Webアプリケーション開発(3)

Modelを定義する

 では、Modelを定義してみましょう。第2回のView層の解説で利用したTwitterクローンアプリケーションでは、投稿されたメッセージはメモリ上に保持しており、サーバを再起動すると消えてしまいました。

 Modelを利用して、投稿されたメッセージをデータベースに保存するようにしてみます。

データベースの設定

 Modelを利用してデータベースへアクセスするためには、接続先のデータベースの設定を行わなければなりませんが、mavenを利用してLiftのプロジェクトを作成した時点で、既にBoot.scalaに記述されているため、開発者が改めて書く必要は無いのです。

 デフォルトの設定では、Liftに同梱されているH2 Database Engineを利用するようになっています。今回のサンプルでは、H2 Database Engineをそのまま利用するので、特に設定の必要はありません。もし、接続先の情報を変更したい場合は、JDBCドライバのクラスやURLをPropertyファイルに定義すれば、このコードをそのまま利用できます。

 プロパティファイルでデータベースへの接続情報を定義するには、「src/main/webapp/WEB-INF/classes/props/default.props」に、リスト1の形式で記述します。また、使用するデータベースにあったJDBCドライバのjarファイルをpom.xmlに定義しておく必要があります。詳細は、Lift公式のWikiページを参照ください。

[リスト1]データベースの接続先を変更する場合
db.driver   = JDBCドライバのクラス名
db.url      = 接続先URL
db.user     = ユーザー名
db.password = パスワード

Message Modelの定義

 では、投稿されたメッセージを表すMessage Modelを定義します。

 プロジェクトディレクトリ以下の「src/main/scala/demo/twitterclone/model/」ディレクトリに、「Message.scala」というファイル名で新しいファイルを作成します。

[リスト2]Message Modelの定義
// パッケージ宣言
package demo.twitterclone.model

// import宣言
import java.util.Date
import _root_.net.liftweb.mapper._
import _root_.net.liftweb.util.Helpers._

/**
 * 投稿されたメッセージに対応するMapperクラスです。
 */
class Message extends LongKeyedMapper[Message] with IdPK { *1
  def getSingleton = Message  // MetaMapperを返す *2

  *3
  object status extends MappedString(this, 140)  // 本文
  object dateOf extends MappedDateTime(this)     // 投稿日時
  object user extends MappedLongForeignKey(this,User) // 投稿したユーザー
  
  dateOf( new Date ) // 投稿日時のデフォルトは現在時刻
}

/**
 * 投稿されたメッセージに対応するMetaMapperオブジェクトです。
 */
object Message extends Message with LongKeyedMetaMapper[Message]{ *4
  // ソート順の指定
  override def fieldOrder = List( dateOf ,id , status )
}
Mapperクラス

 MapperクラスであるMessageクラスは、LongKeyedMapper[Message]IdPKを継承しています。

 *1はMessageクラスの宣言です。LongKeyedMapperはtraitであり、Long型のkeyを持つMapperであることを意味します。IdPKは、主キーのidを自動採番するためのtraitです。

 Messageクラスの内部を見てみましょう。*2defgetSingletonと定義されている関数は、このMessageクラスに対応するMetaMapperオブジェクトを返すように定義します。

 *3は、Messageクラスのプロパティの定義です。Messageクラスには、status(本文)とdateOf(投稿日時)とuser(投稿したユーザー)の3つのプロパティが定義されています。

 各プロパティは対応するデータベースのカラムのデータ型に対応するObjectを継承して定義します。status(本文)は文字列なので、MappedStringを継承しています。例にあるMappedString(this,140)では、140字までのデータ長であることを宣言しています。

 dateOf(投稿日時)は、日付型なので、MappedDatetimeを継承して定義します。user(投稿したユーザー)は、投稿したユーザーのIDを保持するカラムです。このユーザーIDは、Liftプロジェクトを作成した時点で既に定義されているユーザーテーブルへの外部キーとなっています。よって、MappedLongForeignKey(this,User)のように、Long型外部キーでUserテーブルへの参照を指定しています。

MetaMapperオブジェクト

 MetaMapperオブジェクトであるMessageオブジェクトは、Messageクラスを継承しており、さらにLongKeyedMetaMapperも継承します。

 MessageクラスとMessageオブジェクトは、同一パッケージかつ同一ファイル内で同じクラス名とオブジェクト名で定義されています。scalaでは、このような関係のクラスとオブジェクトをコンパニオンクラス/コンパニオンオブジェクトと呼び、それぞれに特権的なアクセス権(privateなメンバーへのアクセスなど)が付与されます。

 MetaMapperオブジェクトの内部では、fieldOrder関数をオーバーライドして、Mapperに用意されているユーティリティ関数でフォーム用HTMLを生成したときのフィールドの出力順を指定しています。

スキーマの移行(Schemifier)

 LiftのModelには、Modelクラスで定義した内容を元に、自動的にデータベース上のテーブル定義を更新するSchemifierという機能があります。Schemifierを利用すると、Modelの定義とは別にCREATE TABLE文などのDDLを用意する必要がなくなります。

 Schemifierは対話型コンソールからも実行できますが、開発中は設定ファイルであるBoot.scalaにSchemifierの実行を定義しておくとよいでしょう。このようにしておくことで、開発サーバを起動するたびに、Modelの定義とデータベースのスキーマの内容を比較して、更新されている場合はModelの定義に合わせてデータベースのスキーマを自動的に同期してくれます。

[リスト3]Schemifierの呼び出し
class Boot {
  def boot {
  
  // ...省略
  
  // Schemifierの呼び出し。第3引数以降に
  // 同期させたいモデルを指定する
  Schemifier.schemify(true, Log.infoF _, User,Message)
  
  // ...省略
  }
}
対話型コンソールでの動作確認

 以上でModelの定義ができました。早速動作確認をしてみましょう。Liftは、対話型コンソールからModelの操作を行うことができます。

 まずは、mavenコマンドで対話型コンソールを起動しましょう。

[リスト4]対話型コンソールを起動
$ mvn scala:console
Welcome to Scala version 2.7.5.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_15).
Type in expressions to have them evaluated.
Type :help for more information.

scala>

 このように、Mavenからscalaの対話型コンソールを起動することで、あらかじめ必要なライブラリがクラスパスに通った状態で起動できます。

 次に、Liftの設定を読み込みます。コンソールに次のように入力しましょう。

[リスト5]Liftの設定読み込み
scala> (new bootstrap.liftweb.Boot).boot 
INFO - CREATE TABLE users (id BIGINT NOT NULL GENERATED ALWAYS AS IDENTITY , firstname VARCHAR(32) , lastname VARCHAR(32) , email VARCHAR(48) , locale VARCHAR(16) , timezone VARCHAR(32) , password_pw VARCHAR(48) , password_slt VARCHAR(20) , textarea VARCHAR(2048) , superuser SMALLINT , validated SMALLINT , uniqueid VARCHAR(32)) 
INFO - ALTER TABLE users ADD CONSTRAINT users_PK PRIMARY KEY(id)
INFO - CREATE TABLE message (id BIGINT NOT NULL GENERATED ALWAYS AS IDENTITY , user_c BIGINT , status VARCHAR(140) , dateof TIMESTAMP) 
INFO - ALTER TABLE message ADD CONSTRAINT message_PK PRIMARY KEY(id)
INFO - CREATE INDEX users_email ON users ( email )
INFO - CREATE INDEX users_uniqueid ON users ( uniqueid )
INFO - CREATE INDEX message_user_c ON message ( user_c )

scala>

 Liftの設定を読み込むと、ログにCREATE TABLEなどのSQL文が表示されました。これは、Boot.scalaにSchemifierの呼び出しを定義しているため、設定読み込みの際にスキーマとの同期が実行されているためです。初回の起動ではテーブルが存在しないため、UserモデルとMessageモデルに対応するテーブルがCREATE TABLE文で作成されています。

 では、実際にMessageモデルを操作してみましょう。リスト6のように入力してみてください。

[リスト6]Modelの操作
scala> import demo.twitterclone.model._ *1
import demo.twitterclone.model._

scala> val msg = Message.create.status("hello") *2
msg: demo.twitterclone.model.Message = demo.twitterclone.model.Message={id=-1,user=NULL,status=hello,dateof=Mon Oct 19 15:03:48 JST 2009}

scala> msg.save *3
res3: Boolean = true

scala> Message.findAll *4
res4: List[demo.twitterclone.model.Message] = List(demo.twitterclone.model.Message={id=1,user=NULL,status=hello,dateof=2009-10-19 00:00:00.0})

 *1では、Modelが定義してあるパッケージをimportしています。

 *2では、Messageモデルをnewします。status("hello")は、status(本文)プロパティに"hello"という文字列を設定しています。モデルの各プロパティへの値の設定は、メソッドチェーンで続けて設定できるようになっています。

 *3は、モデルのsave関数で、*2で作成したMessageモデルをデータベースにinsertしています。

 *4は、MetaMapperオブジェクトであるMessageオブジェクトのfindAll関数を呼び出して、Messageテーブルから全件検索を行っています。*3で保存されたデータが検索されていることが分かると思います。

次のページ
まとめ

この記事は参考になりましたか?

Scala+Liftによる実践Webアプリケーション開発連載記事一覧

もっと読む

この記事の著者

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるLaravel実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

WINGSプロジェクト 尾崎 智仁(オザキ トモヒト)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。 2026年時点での登録メンバは約50名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/4648 2009/12/30 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー