SHOEISHA iD

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

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

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

Google App Engine/JavaによるScala/Liftアプリケーション開発

Google App Engine/JavaによるScala/Liftアプリケーション開発(前編)

Liftアプリケーション開発の基本とDataStoreの利用

プロトタイプの作成

 では、基本的なLiftの機能を利用して、プロトタイプを作成しましょう。作成の順序としては、まずイベントの情報を保持するModelを作成し、次に一覧画面と登録画面を作ります。

Event情報のModelの作成

 では、イベントの情報を保持するModelを作成します。

Google App Engineでのデータアクセス

 Google App Engineのデータストアにアクセスするための手段としては「Java Data Objects(JDO)」「Java Persistence API(JPA)」を利用する2種類の方法の他に、Low Level APIと呼ばれるGoogleが提供するAPIを利用する、計3通りの方法があります。

 JDOやJPAを利用する方法は、これまでのJavaアプリケーションと同じようにデータストアへのアクセスができる、という利点があります。しかし、JDOやJPAは、本来リレーショナルデータベースへのアクセスのために用意された仕様であり、BigTableをベースとしたGoogle App Engineでのデータストアに適用するために、さまざまな制約が発生しています。

 最近のGoogle App Engineでのアプリケーション開発では、リレーショナルデータベースではなくBigTableに合わせたデータ設計が必要であり、そのためにはJDOやJPAではなくLow Level APIを利用して柔軟に実装する方が良い、と言われています。

 例えば、Google App Engineに最適化されたフレームワークであるSlim3で提供されるSlim3 Datastoreは、Low Level APIのラッパーとして提供され、Low Level APIのEntityオブジェクトとユーザー定義のデータクラスを透過的に扱えるようになっています。

 上記のような理由で、本稿で作成するアプリケーションでのデータアクセスは、Low Level APIを利用する形で作成します。

Low Level APIを利用したModel

 今回作成するModelは、一回のイベントの情報をEventというクラス名のcase class(*1)として扱うようにします。データストアへのアクセスを実際に行うのは、EventクラスのコンパニオンオブジェクトであるEventオブジェクト(*2)が行う形です。

 Eventオブジェクトが、データストアからの検索や保存を行い、Eventクラスのインスタンスを作成します。Google App Engineにおけるデータストアでは、データはkindという種別と、データ項目であるプロパティを持っています。

 kindは、データベースにおけるテーブルに、プロパティはデータの属性(カラム)に相当しますが、リレーショナルデータベースのテーブルのようにカラムを定義するわけではなく、レコードごとに任意の名前のプロパティを持たせることができます。つまり、データの形が決まっていない(ソフトスキーマである)ことが、リレーショナルデータベースと決定的に異なる点なのです。

 今回作成するイベント情報のデータは、"event"という名前のkindで保存することにします。

 ソースコードですが、「src/main/scala/demo/model」ディレクトリに、Event.scalaというファイル名で、下記の内容のファイルを新規作成します。

[リスト6]Event.scala
package demo.model
  ...中略...
  // イベントの情報を持つクラスです。
 case class Event ( // *1
  id:Option[Long],name:String, description:String, fromDate:Date, toDate:Date,
  place:String, join:Long, count:Long)

  // イベントEntityを操作するオブジェクトです。
object Event { // *2   // GAEのLowLevelAPIを利用するためのDatastoreServiceを返します
  private def dsService     = DatastoreServiceFactory.getDatastoreService // *5

  // Eventオブジェクトを保存します。
  def save( event:Event ) = // *6
    dsService.put( event2Entity( event ) )

  // IDからentityを検索して返します。
  def find( id:Long ) = { // *7
    val key = KeyFactory.createKey("event",  id)
    entity2Event( dsService.get( key )) match{
      case null => None
      case e => Some(e)
    }
  }

  // Eventの情報を検索し、Listで返します。
  def findAll(limit:Int) = { // *8
    val q = new Query( "event"
      ).addSort("toDate",  SortDirection.ASCENDING)

    dsService.prepare( q ).asList(
      FetchOptions.Builder.withLimit( limit )
    ).map { e => entity2Event( e ) }
  }

  // 以下はユーティリティ関数

  // GAEのEnityがもつPropertyの値を型を指定してとりだします。
  def entityValue[T]( e:Entity, name:String ) =
    e.getProperty( name ).asInstanceOf[T]

  // GAEのEntityオブジェクトからEventオブジェクトを作成します。
  def entity2Event( e:Entity ) = { // *3
    Event(      Some(e.getKey.getId), entityValue[String]( e, "name" ),
      entityValue[String]( e, "description" ),
      new Date(entityValue[Long]( e, "fromDate" )),
      new Date(entityValue[Long]( e, "toDate" )),
      entityValue[String]( e, "place" ), entityValue[Long]( e, "join" ),
      entityValue[Long]( e, "count" )
    )
  }

  // EventオブジェクトからGAEのEntityオブジェクトを返します。
  def event2Entity( event:Event ) = { // *4
    val e = event.id match {
      case Some(id) => new Entity( KeyFactory.createKey("event", id) )
      case None => new Entity( "event" )
    }
    e.setProperty( "name", event.name )
    e.setProperty( "description", event.description )
    e.setProperty( "fromDate", event.fromDate.getTime )
    e.setProperty( "toDate", event.toDate.getTime )
    e.setProperty( "place", event.place )
    e.setProperty( "join", event.join )
    e.setProperty( "count", event.count )
    e
  }
}

 Low Level APIを利用したデータストアでは、一行のレコードはEntityクラスのインスタンスです。

 Entityクラスからのプロパティの設定はEntity#setProperty( String name , Object value )を利用し、プロパティの取得はEntity#getProperty( String name )を利用します。

 そのため、EventクラスとEntityクラスの相互変換を行うために、entity2Event(*3)とevent2Entity(*4)というユーティリティ関数を用意しました。

 実際にデータストアへのアクセスを行うためには、DatastoreServiceクラスのインスタンスを取得する必要があります。このため、*5でdsServiceという関数を用意して、DatastoreServiceを取得できるようにしました。このdsService関数を、保存や検索を行う際に呼び出して利用します。

 データの保存は、DatastoreService#put( Entity entity )を呼び出します。*6のsave関数では、引数に受け取ったEventクラスのインスタンスを、*4で作成したevent2Entity関数でデータストア用のEntityオブジェクトに変換してDatastoreService#putを呼び出しています。

 データの検索は、データストア側で採番される一意なキー(Keyオブジェクト)を指定して一件取得する方法と、Queryオブジェクトを利用して問い合わせを行う方法があります。

 *7のfind関数は、Keyオブジェクトを指定して検索する関数です。Longのidを基に、KeyFactory.createKey(String kind, long id)でKeyオブジェクトを作成し、DataStoreService#get( Key key )を呼び出してEntityオブジェクトを取得します。

 取得したEntityオブジェクトは、*3で作成したentity2Event関数により、Eventクラスに変換されて、Option型に包んで返すようになっています。

 複数件のデータを、条件を指定して取得したい場合はどのようにするのでしょうか?

 Low Level APIでは、検索条件を指定するQueryというオブジェクトを利用することで、データストアへの問い合わせを行うことができます。

 *8のfindAll関数は、Queryオブジェクト並び替えの条件を指定して作成しています。このQueryオブジェクトをDataStoreService#prepare( Query query)に渡して、返されるPreparedQueryオブジェクトのasList関数を呼び出して、List[Entity]を取得しています。

 取得したEntityは、find関数と同じく1件づつentity2Event関数でEventクラスに変換し、List[Event]型で結果を返すようになっています。

 以上、簡単に今回作成したModelの処理を解説しました。Low Level APIの詳しい使い方は、JavaDocをごらんください。

次のページ
ViewとTemplateの作成

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

Google App Engine/JavaによるScala/Liftアプリケーション開発連載記事一覧
この記事の著者

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

静岡県榛原町生まれ。一橋大学経済学部卒業後、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/5004 2010/04/16 14:00

イベント

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

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

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

メールバックナンバー