検索
連載

Play 2.xからMySQLに接続してAnormでCRUD操作するにはScala+Play 2.0でWebアプリ開発入門(7)(3/3 ページ)

2.0からScalaに対応したWebアプリ開発の人気軽量フレームワーク「Play」について解説し、Webアプリの作り方を紹介する入門連載。今回は、DBに接続してSQL文でデータを追加/更新/削除/参照する方法を解説。EvolutionスクリプトでのDB管理方法も。

PC用表示 関連情報
Share
Tweet
LINE
Hatena
前のページへ |       

Play ScalaでAnormを使ってみる

 今回はPlayコンソール上でREPLを使用し、Anormの動作を確認してみましょう。先ほど作成したデータベースの起動を確認した後、playコマンドでPlayコンソールを立ち上げましょう。そしてconsoleコマンドを実行し、REPLを起動します。

% play
[gyro] $ console
Welcome to Scala version 2.10.0 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_04).
Type in expressions to have them evaluated.
Type :help for more information.
scala>

 このままではアプリが起動していないので、REPL上で起動させます。play.core.StaticApplicationをインスタンス化することで、Playアプリが起動します。

※下記コードはREPL上で入力してください。

scala> import play.core.StaticApplication
scala> new StaticApplication(new java.io.File("."))
・
・
[info] play - Application started (Prod)
res1: play.core.StaticApplication = play.core.StaticApplication@193d69e

SQLを実行

 アプリが起動したので、DBアクセスが可能になりました。SQLを実行してみましょう。必要パッケージをimportしてから、withConnectionブロック内で、anorm.SQLオブジェクトを使ってSQLクエリを実行します。

scala> import play.api.db._
scala> import play.api.Play.current
scala> import anorm._ 
scala> DB.withConnection { implicit c => 
 val result: Boolean = SQL("Select 1").execute()    
 println("result =" + result)
} 
[debug] c.j.b.PreparedStatementHandle - Select 1
result =true

 execute関数はSQL実行結果をBooleanで返します。結果が表示されて、SQLが成功したのが分かりますね。

insert

 では、次のようにしてUserテーブルにデータを登録してみましょう。insert文の場合、executeInsert関数を使用すると、自動生成されたキーをOption型で返します(主キーが単数かつ数値の場合)。

//Userテーブルにレコードを1件登録
scala> DB.withConnection { implicit c =>
        val count = SQL(
          """
            insert into User(name,email,password)
            values({name},{email},{password})
          """
        ).on('name -> "taro",
          'email -> "taro@classmethod.jp",
          'password -> "taropass").executeInsert()
        println("count = " + count)
      }
[debug] c.j.b.PreparedStatementHandle - insert into User(name) values('taro')
count = Some(1)

 on関数ではSQLへバインドするnameパラメータを「{}」を用いて指定しています。また、「"""」(ダブルクオート3つ)で文字列を囲えば、複数行の文字列を記述できるので、複雑なSQLもこの形式で記述できますね。SQL実行結果として、登録されたレコードのID「1」が取得できました。

update

 登録ができたので、次は更新を行ってみましょう。executeUpdate関数を使用すれば、update文を実行できます。この関数はアップデートされた行数を返します。

//id:1のレコードを更新
scala> DB.withConnection { implicit c =>
        val count = SQL(
          """
            update User set name={name} where id={id} 
          """
        ).on('name -> "hanako", 'id -> 6).executeUpdate()
        println("updateCount = " + count)
      }
[debug] c.j.b.PreparedStatementHandle - update User set name='hanako' where id=1
updateCount = 1 

delete

 削除を実行してみましょう。delete文の場合もexecuteUpdate関数を使用します。

//id:1のレコードを削除
scala> DB.withConnection { implicit c =>
        val count: Int = SQL(
          """
           delete from User where id = {id}
          """
          ).on('id -> 1).executeUpdate()
        println("deleteCount = " + count)
      }
[debug] c.j.b.PreparedStatementHandle - delete from User where id = 1
deleteCount = 1

Stream APIでselect

 更新系のSQLを使ったので、次は参照系のSQLを使ってみましょう。まずはMySQLのコンソールで、テスト用データを登録しておきます。

% mysql -u<ユーザー名> -p<パスワード>
mysql> use gyro;
mysql>  insert into User values(1,'taro','taro@classmethod.jp','taropass',now());
        insert into User values(2,'hanako','hanako@classmethod.jp','hanakopass',now());
        insert into User values(3,'mike','mike@mike.com','mikepass',now());
        insert into Post values(100,1,'title1','body1',now());
        insert into Post values(101,1,'title2','body2',now());
        insert into Post values(102,2,'title3','body3',now());

 参照系SQLを実行してみましょう。Stream APIを使用すればselect結果を取得/変換可能です。SQLオブジェクトのapply()を呼び出すと、Stream[Row]を取得できます。

 次の例ではStream APIを使用してユーザーの一覧を取得してリストとして返しています。

scala> val selectQuery = SQL("select * from User")
       val result = DB.withConnection { implicit c =>
       //Stream[Row] からList[(Long,(String,String))]に変換
       selectQuery().map(row => 
       row[Long]("id") -> (row[String]("name") , row[String]("email"))).toList
     }
[debug] c.j.b.PreparedStatementHandle - Select * from User
result: List[(Long, (String, String))] = List(
(1,(taro,taro@classmethod.jp)),
(2,(hanako,hanako@classmethod.jp)),
(3,(mike,mike@mike.com)))

 Parser APIを使用しても、select結果をパースできます。パーサは再利用することもできるため、Stream APIよりこちらの方が利用頻度は高いかもしれません。

Parser APIでselect

 次はParser APIを使ってみましょう。初めに、anorm.SqlParser._をインポートします。SQLオブジェクトのas関数を使用し、引数でカラムをパースします。

scala> import anorm.SqlParser._ 
scala> val result:List[Int~String~String] = DB.withConnection { implicit c =>
  SQL("select * from User").as( int("id") ~ str("name") ~ str("email") * )
}
[debug] c.j.b.PreparedStatementHandle - select * from User
result: List[anorm.~[anorm.~[Int,String],String]] = List(
~(~(1,taro),taro@classmethod.jp), 
~(~(2,hanako),hanako@classmethod.jp), 
~(~(3,mike),mike@mike.com))

 先ほどと同じように、ユーザーの一覧がリスト形式で取得できました。しかし、取得できた値の型が「List[Int~String~String]」となっており、このままでは使いにくいですね。

flatten関数で取得結果をタプル(Tuple)型のリストに変換

 flatten関数を適用し、タプル(Tuple)型のリストに変換してみましょう。

scala> val result = DB.withConnection { implicit c =>
  SQL("select * from User").as( int("id") ~ str("name") ~ str("email") map(flatten) * )
}
[debug] c.j.b.PreparedStatementHandle - select * from User
result: List[(Int, String, String)] = List(
(1,taro,taro@classmethod.jp), 
(2,hanako,hanako@classmethod.jp), 
(3,mike,mike@mike.com))

 mapとflattenによってTupleのリストとして取得できました。

取得結果を任意のクラスのリストに

 また、取得結果を任意のクラスのリストにすることも可能です。次の処理では、emailの値に応じてクラスの型を切り替えています。

scala> case class CmEmp(name:String)
scala> case class Person(name:String)
scala> val result = DB.withConnection { implicit c =>
  SQL("select * from User").as( int("id") ~ str("name") ~ str("email") map {
    //メールアドレスに@classmethod.jpが含まれていたら、CmEmpオブジェクトにする
    case id~name~email if(email.contains("@classmethod.jp")) => CmEmp(name)
    case id~name~email => Person(name)
  } *)
}
[debug] c.j.b.PreparedStatementHandle - select * from User
result: List[Product with Serializable] = 
List(CmEmp(taro), CmEmp(hanako), Person(mike))

 このように、AnormのParser APIを使用すれば、再利用可能なパーサを定義してSQL結果を変換できます。Stream API、Parser APIについても「Documentation: ScalaAnorm ― Playframework」で詳しく解説しているので、併せてご確認ください。

Slickも使ってみよう

 さて、今回はPlay2でDBアクセスを行うための方法について解説しましたが、いかがでしたか。

 AnormはいままでのDBアクセスライブラリの感覚で使用すると、ちょっと使いにくいかもしれませんが、少し慣れれば自然に扱えるようになると思います。

 また、先ほども少し言及しましたが、「Slick」というDBアクセスライブラリもあります。これはDBデータをScalaコレクションのように扱うことができ、Scalaでクエリを記述できるという特徴を持っています。Play2を使用する際には、こちらの使用も検討してみてください。

 次回はScala+Play2でテストを行う方法を紹介する予定です。

著者プロフィール

中村修太

中村修太(なかむら しゅうた)

クラスメソッド勤務の新しもの好きプログラマです。昨年、東京から山口県に引っ越し、現在はノマドワーカーとして働いています。好きなJazzを聴きながらプログラミングするのが大好きです。


Copyright © ITmedia, Inc. All Rights Reserved.

前のページへ |       
ページトップに戻る