ぜったいってなに

Software Engineerのブログです。

Play Scala モデル追加

DB connectionができたので、次はモデルを記述して、
モデルからテーブルのレコードをCRUDできるようにする。



こちらの記事をまるっと参考にさせていただきました。

qiita.com



まずは昨日時点のevolutionファイルを修正する。
PostgreSQLでは大文字小文字が区別されないようなので、
テーブル名、カラム名を小文字で統一。


/conf/evolutions/default/1.sql

  1 # --- !Ups
  2
  3 CREATE TABLE "user" (
  4     "username" TEXT NOT NULL,
  5     "password" TEXT NOT NULL,
  6     "name" TEXT NOT NULL
  7 );
  8
  9 CREATE TABLE "article" (
 10     "user_id" INT NOT NULL,
 11     "content" TEXT NOT NULL
 12 );
 13
 14 CREATE TABLE "comment" (
 15     "user_id" INT NOT NULL,
 16     "article_id" INT NOT NULL,
 17     "content" TEXT NOT NULL
 18 );
 19
 20 # --- !Downs
 21
 22 DROP TABLE "user";
 23 DROP TABLE "article";
 24 DROP TABLE "comment";


次に各テーブルのモデルを定義する。


/app/models/Models.scala

  1 package models
  2
  3 case class User(username: String,  password: String, name: String)
  4 case class Article(user_id: Int, content: String)
  5 case class Comment(user_id: Int, article_id: Int, content: String)



モデルファイルがエンティティによって分かれていないのがちょっと不思議。
ファイルを別にすることもできると思いますがひとまず例に倣ってこれで。


DAOは下記のように記述。


/app/dao/UserDAO.scala

  1 package dao
  2
  3 import scala.concurrent.Future
  4
  5 import javax.inject.Inject
  6 import models.User
  7 import play.api.db.slick.DatabaseConfigProvider
  8 import play.api.db.slick.HasDatabaseConfigProvider
  9 import play.api.libs.concurrent.Execution.Implicits.defaultContext
 10 import slick.driver.JdbcProfile
 11
 12 class UserDAO @Inject()(protected val dbConfigProvider: DatabaseConfigProvider) extends HasDatabaseConfigProvider[JdbcProfile] {
 13   import driver.api._
 14
 15   private val Users = TableQuery[UsersTable]
 16
 17   def all(): Future[Seq[User]] = db.run(Users.result)
 18
 19   def insert(user: User): Future[Unit] = db.run(Users += user).map { _ => () }
 20
 21   private class UsersTable(tag: Tag) extends Table[User](tag, "user") {
 22
 23     def username = column[String]("username")
 24     def password = column[String]("password")
 25     def name = column[String]("name")
 26
 27     def * = (username, password, name) <> (User.tupled, User.unapply _)
 28   }
 29 }


ArticleDAO, CommentDAOも同様に定義したが、UserDAOと殆ど同じコードなので省略。



play consoleを起動しようとする。

activator console -Dplay.evolutions.db.default.autoApply=true


と、ブラウザが開いて下記ページが表示された。

Documentation - Lightbend Activator | @lightbend


To create new Lightbend projects Instead of using the Activator command, make sure you have sbt 0.13.13 (or higher), and use the “sbt new” command, providing the name of the template. For example, “$ sbt new akka/hello-akka.g8”. You can find a list of templates here.


Activatorコマンドは廃止になるらしく、今後はsbtコマンド使ってね、とのこと。
なるほど、Playに関してぐぐってるとactivator runやらsbt runやら
同じようなコマンドが出てきていたけど、そういうことだったのか。


というわけで、下記コマンドでコンソールを起動する。
問題なく起動した。

sbt console -Dplay.evolutions.db.default.autoApply=true


Userモデルをnewしてレコード追加してみる。

scala> import play.api._
scala> val env = Environment(new java.io.File("."), this.getClass.getClassLoader, Mode.Dev)
scala> val context = ApplicationLoader.createContext(env)
scala> val loader = ApplicationLoader(context)
scala> val app = loader.load(context)
scala> Play.start(app)
scala> import dao.UserDAO
scala> import models.User
scala> import scala.concurrent.Await
scala> import scala.concurrent.duration.DurationInt
scala> val app2dao = Application.instanceCache[UserDAO]
scala> val dao = app2dao(app)
scala> val user = new User("zettaittenani", "play", "zettaittenani")
scala> Await.result(dao.insert(user), 1.second)
scala> Await.result(dao.all, 1.second)
res5: Seq[models.User] = Vector(User(zettaittenani,play_test,zettaittenani))


レコードが入ったっぽいのでPostgreSQLの方で確認してみる。

playdb=> select * from user;
 current_user
--------------
 play


…どうやらuserというテーブル名がPostgreSQL自体のユーザのテーブル名とバッティングしていて
追加したレコードがあるかどうかわからない。

命名変更も面倒なのでArticleのレコードで試してみる。

scala> import play.api._
scala> val env = Environment(new java.io.File("."), this.getClass.getClassLoader, Mode.Dev)
scala> val context = ApplicationLoader.createContext(env)
scala> val loader = ApplicationLoader(context)
scala> val app = loader.load(context)
scala> Play.start(app)
scala> import dao.ArticleDAO
scala> import models.Article
scala> import scala.concurrent.Await
scala> import scala.concurrent.duration.DurationInt
scala> val app2dao = Application.instanceCache[ArticleDAO]
scala> val dao = app2dao(app)
scala> val article = new Article(1, "hogehoge")
scala> Await.result(dao.insert(article), 1.second)
scala> Await.result(dao.all, 1.second)
res2: Seq[models.Article] = Vector(Article(1,hogehoge))


playdb=> select * from article;
 user_id | content
---------+----------
       1 | hogehoge

よし、入ってる。


明日はview, controllerを使って画面上からレコードのCRUDをやりたい。