ラベル play の投稿を表示しています。 すべての投稿を表示
ラベル play の投稿を表示しています。 すべての投稿を表示

2014年5月15日木曜日

PlayのslickでのDBアクセス処理

PlayでSlickを使っていくつかブログ書いてたので、
http://syaka-syaka.blogspot.com/2014/04/play-frameworkslickevolution.html
http://syaka-syaka.blogspot.com/2014/05/play-frameworkslickevolution.html
DBアクセス処理についても載せてみます。
けっこういろんな書き方ができるのでいいところと難しいところあるなと感じました。
package models.h2
 
import play.api.db.slick.Config.driver.simple._
import models.dao.DAO
import java.sql.Timestamp
 
case class Twitter(
    id: Int, 
    word: String, 
    count:Int,
    disp_flg:Boolean,
    ng_flg : Boolean, 
    ins_date: Timestamp, 
    upd_date: Timestamp
)
  
class Twitters(tag: Tag) extends Table[Twitter](tag,"twitter"){
 
  def id = column[Int]("id",O.PrimaryKey,O.AutoInc)
  def word = column[String]("word",O.NotNull)
  def count = column[Int]("count",O.NotNull)
  def disp_flg = column[Boolean]("disp_flg",O.NotNull)
  def ng_flg = column[Boolean]("ng_flg",O.NotNull)
  def ins_date = column[Timestamp]("ins_date",O.NotNull)
  def upd_date = column[Timestamp]("upd_date",O.NotNull)
  def * = (id,word,count,disp_flg,ng_flg,ins_date,upd_date) <> (Twitter.tupled,Twitter.unapply)
}
 
object Twitters extends DAO{
  
  /**
   * 特定のカラムのみを引数指定でinsertする
   */
  def insert(word:String,ins_date:Timestamp,upd_date:Timestamp,count:Int,ng_flg:Boolean,disp_flg:Boolean)(implicit s: Session) = {
    Twitters map{
      r => (r.word,r.ins_date,r.upd_date,r.count,r.ng_flg,r.disp_flg)
    } +=(word,ins_date,upd_date,count,ng_flg,disp_flg)
    
  }
  
  /**
   *特定のカラムをkeyに該当するデータ(1カラム)をリストでselectする
   */ 
  def findByWord(word:String)(implicit s: Session):List[Int] = {
    Twitters filter {
      _.word === word
    } map {
     _.id
    } list
  }
 
  /**
   * 特定のカラムをkeyに該当するデータ(全カラム)を1件取得する
   */ 
  def findById(id:Int)(implicit s:Session):Twitter = {
    Twitters.where(_.id === id).first
  }
  
  /**
   * 特定のカラムをkeyに該当するデータ(全カラム)をリストでselectする
   */ 
  def findAllByWord(word:String)(implicit s: Session):List[Twitter] = {
   Twitters.where(_.word === word).list
  }
  
  /**
   * 全件select
   */ 
  def findAll()(implicit s:Session):List[Twitter] = {
    Twitters.list
  }
 
  /**
   * 全件select(idの降順)
   */ 
  def findAllOrderByIdDesc()(implicit s:Session):List[Twitter] = {
    Twitters.sortBy(_.id.desc).list
  }
 
  /**
   * select order byをしてlimitとoffsetつき
   */ 
  def findAllLimitOffsetOrderByIdDesc(limit:Int,offset:Int)(implicit s:Session):List[Twitter] = {
    val q = Twitters.sortBy(_.id.desc).drop(offset).take(limit)
//    println(q.selectStatement) //組み立てられたSQLを確認できる
    q.list
  }
  
  /**
   * select upd_date,idでorder byをしてlimitとoffsetつき
   */ 
  def findAllLimitOffsetOrderByUpdDateDescAndIdDesc(limit:Int,offset:Int)(implicit s:Session):List[Twitter] = {
    val q = Twitters.sortBy(_.id.desc).sortBy(_.upd_date.desc).drop(offset).take(limit) //order by upd_date desc,id descになる
//    println(q.selectStatement) //組み立てられたSQLを確認できる
    q.list
  }
  
  /**
   * 特定のカラムをkeyに該当するデータの1カラムをupdateする
   */ 
  def update(id:Int,updCount:Int)(implicit s:Session) = {
   Twitters.where(_.id === id).map(_.count).update(updCount)
  }
 
  /**
   * 特定のカラムをkeyに該当するデータの複数カラムをupdateする
   */ 
  def updateMultiColumn(id:Int,updCount:Int,updDate:Timestamp)(implicit s:Session) = {
   
   val q = (for 
  {twi <- Twitters if twi.id === id}
   yield
    (twi.count , twi.upd_date)
   )
   
//   println(q.updateStatement) //組み立てられたSQLを確認できる
   q.update(updCount,updDate)
  }
  
  /**
   * 件数をselectする
   */ 
  def count(implicit s:Session):Int = {
    Query(Twitters.length).first
  }
  
}


参考URL
https://github.com/freekh/play-slick
http://blog.lunatech.com/2013/09/25/play-slick-defining-queries
http://qiita.com/ryu1_okd/items/6871515ec42e88cd534f
http://zephiransas.github.io/blog/2013/04/24/slickwoshi-u/
http://tototoshi.hatenablog.com/entry/20121204/1354615421
http://krrrr.hatenablog.com/entry/20130213/1360687102

2014年5月1日木曜日

Play FrameworkのSlickのevolutionでカラム追加

Play FrameworkのSlickのevolutionで管理してるテーブルにカラムを追加してみました。

バージョン
Play Framework 2.2.2
slick 2.0.1
play-slick 0.6.0.1

前回作ったclassに変数upd_dateとcountを追加
case class Twitter(id: Int, word: String, ins_date: Date, upd_date: Date, count:Int)

class Twitters(tag: Tag) extends Table[Twitter](tag,"twitter"){
  def id = column[Int]("id",O.PrimaryKey,O.AutoInc)
  def word = column[String]("word",O.NotNull)
  def ins_date = column[Date]("ins_date",O.NotNull)
  def upd_date = column[Date]("upd_date",O.NotNull)
  def count = column[Int]("count",O.NotNull)
  def * = (id,word,ins_date,upd_date,count) <> (Twitter.tupled,Twitter.unapply)
}
その他、呼び出し元や取得先など関係するところを修正する。
アプリケーションを起動してhttp://localhost:9000/にアクセスするとコンソールに以下が表示

[info] Compiling 2 Scala sources to /Users/xxxx/play/sample/target/scala-2.10/classes...
[info] Compiling 4 Scala sources to /Users/xxxx/play/sample/target/scala-2.10/classes...
[info] play - database [default] connected at jdbc:h2:file:database/play
[info] o.r.Reflections - Reflections took 107 ms to scan 1 urls, producing 9 keys and 10 values 
[error] application - ! @6i1gdpfi5 - Internal server error, for (GET) [/] -> 
play.api.db.evolutions.InvalidDatabaseRevision: Database 'default' needs evolution![An SQL script need to be run on your database.]
 at play.api.db.evolutions.EvolutionsPlugin$$anonfun$onStart$1$$anonfun$apply$1.apply$mcV$sp(Evolutions.scala:486) ~[play-jdbc_2.10.jar:2.2.2]
 at play.api.db.evolutions.EvolutionsPlugin.withLock(Evolutions.scala:507) ~[play-jdbc_2.10.jar:2.2.2]
 at play.api.db.evolutions.EvolutionsPlugin$$anonfun$onStart$1.apply(Evolutions.scala:461) ~[play-jdbc_2.10.jar:2.2.2]
 at play.api.db.evolutions.EvolutionsPlugin$$anonfun$onStart$1.apply(Evolutions.scala:459) ~[play-jdbc_2.10.jar:2.2.2]
 at scala.collection.immutable.List.foreach(List.scala:318) ~[scala-library.jar:na]
 at play.api.db.evolutions.EvolutionsPlugin.onStart(Evolutions.scala:459) ~[play-jdbc_2.10.jar:2.2.2]
[info] a.e.s.Slf4jLogger - Slf4jLogger started

画面上に以下のものが表示される。
Database 'default' needs evolution!
An SQL script will be run on your database - [Apply this script now!]

[Apply this script now!]ボタンを実行すると正常な画面が表示される。
テーブルにカラムも追加されている。
alterではなくて、dropしてcreateしてる模様
コンソールには以下が表示される。
[info] c.j.b.BoneCP - Shutting down connection pool...
[info] c.j.b.BoneCP - Connection pool has been shutdown.
[info] play - database [default] connected at jdbc:h2:file:database/play
[info] o.r.Reflections - Reflections took 11 ms to scan 1 urls, producing 9 keys and 10 values

開発中にカラムの変更入ったときとかは便利ですね。
そもそもそういうこと起きないように開発したいですが。。。

2014年4月26日土曜日

Play FrameworkのSlickでevolutionを使ったテーブル管理をしてみた

今回はPlay FrameworkにSlickを入れて、evolutionを使ったテーブル管理をやってみました。

バージョン
Play Framework 2.2.2
slick 2.0.1
play-slick 0.6.0.1

build.sbtに下記を追加する。
  "com.typesafe.slick" %% "slick" % "2.0.1",
  "org.slf4j" % "slf4j-nop" % "1.7.6",
  "com.typesafe.play" % "play-slick_2.10" % "0.6.0.1"

application.confでevolutionを有効にし、slickでテーブルを作る対象をmodels.*で指定する。
evolutionplugin=enabled
slick.default="models.*"

上で指定したパッケージ配下にmodelを作成
package models
 
import play.api.db.slick.Config.driver.simple._
import java.sql.Date
import models.dao.DAO
 
case class Twitter(id: Int, word: String, ins_date: Date, upd_date: Date, ng_flg : Boolean)
  
class Twitters(tag: Tag) extends Table[Twitter](tag,"twitter"){
 
  def id = column[Int]("id",O.PrimaryKey,O.AutoInc)
  def word = column[String]("word",O.NotNull)
  def ins_date = column[Date]("ins_date",O.NotNull)
  def * = (id,word,ins_date) <> (Twitter.tupled,Twitter.unapply)
}
 
object Twitters extends DAO{
    
  def insert(word:String,ins_date:Date)(implicit s: Session) = {
    Twitters map{
      r => (r.word,r.ins_date)
    } +=(word,ins_date)
  }
  
  def findByWord(word:String)(implicit s: Session):List[Int] = {
    Twitters filter {
      _.word === word
    } map {
     _.id
    } list()
  }
}

DAOは分けてこう書いておくとよいと見かけたので、この形で作成。
ちなみにTableを継承してないclassがslickの対象パッケージにいるとエラー出るので別の階層にしないといけない。
package models.dao
 
import models.h2.Twitters
import scala.slick.lifted.TableQuery
 
private[models] trait DAO{
  val Twitters = TableQuery[Twitters]
}

ひととおり揃えたのでplay runして実行させるとエラーが出た。。。
エラーメッセージ
Found a matching object: modules.Twitter for modules.Twitter but it is not a Slick Table and a wildcard was not specified"

テーブルとして見てもらえてないようなメッセージ。
application.confの指定を*でなく指定する形の以下にしてみたもののだめ。
slick.default="models.Twitters"

結局、この形で記述してみたらうまくいきました。(h2はDBにh2database使ってるため)
最初のだとdaoとTwitter.scalaが同一ディレクトリにいるのがだめなようで、
指定した場所にTable以外のものが入ってないようにしないといけないようです。
slick.default="models.h2.*"


参考URL
http://akiomik.hatenablog.jp/entry/2014/02/23/033444
http://windymelt.hatenablog.com/entry/2014/03/30/191152
https://github.com/freekh/play-slick/blob/6b49ec019e00158c96df12d673856c53830ed1e6/code/src/main/scala/play/api/db/slick/ddl/TableScanner.scala
http://nilfigo.hatenablog.com/entry/2013/09/10/173552

2014年4月24日木曜日

Play FrameworkのAkkaでスケジューラ処理

Play FrameworkのAkkaでスケジューラ処理をさせました。
バージョン
Play Framework 2.2.2

GlobalSettingsを継承して、onStartをoverrideさせることで実現できました。
package common

import akka.actor._
import scala.concurrent.duration._
import play.api._
import play.api.libs.json._
import play.api.libs.concurrent._
import play.api.Play.current
import play.api.libs.concurrent.Execution.Implicits._
import service.TwitterService
import play.Logger
 
object Global extends GlobalSettings {
  
  override def onStart(app: Application){
   val actor = Akka.system.actorOf(Props(new TwitterService), "twitterservice")    
    Akka.system.scheduler.schedule(0.milliseconds, 1.minutes, actor, "tick")
  }  
}
TwitterServiceは↓に載せてあります。twitter4jを使って現在のtrendを取得する処理です。
Akkaに処理を渡す形になるのでActorを継承する必要があります。
Akka.system.scheduler.schedule(0.milliseconds, 1.minutes, actor, "tick")
は起動して0ミリ秒から1分置きにactorを動かして、actorに"tick"を送ります。
package service
 
import twitter4j.Twitter
import twitter4j.TwitterFactory
import twitter4j.Trend
import play.api.libs.concurrent.Akka
import akka.actor._
import akka.actor.Actor._
import org.slf4j.LoggerFactory
import play.Logger
import twitter4j.conf.ConfigurationBuilder
 
class TwitterService extends Actor{
   
  def receive = {
    case i:Int => {
      Logger.error("aa")
      println("aa")
    }
    case msg:String => {
      Logger.error(msg)
      println(msg)
      getTrend(23424856)
    }
    case _ => {
      Logger.error("tetete")
      println("tetete")
      getTrend(23424856)
    }
  }
  
  def getTrend(place:Int):Array[Trend] = {
    
    var twitter = twitterFactory.getInstance()
    var trendsResources = twitter.trends()
    var trends = trendsResources.getPlaceTrends(place)
    
    for(trend:Trend <- trends.getTrends()){
     println(trend.getName())
    }
  }  
}
def receiveにcase文で3つ処理を書きましたが、
"tick"を送っているので、case msg:Stringの処理が実行されます。

application.confのapplication.globalでGlobal.scalaの配置位置を指定できます
# Global object class
# ~~~~~
# Define the Global object class for this application.
# Default to Global in the root package.
# application.global=Global
application.global=common.Global

これでplay runで実行すると、1分置きにtwitterのtrendを取得しに行く処理が動きます。

参考URL
http://www.playframework.com/documentation/2.2.x/ScalaGlobal
http://stackoverflow.com/questions/20924195/play-framework-2-2-scala-onstart-doesnt-work

2014年4月22日火曜日

Play Frameworkで外部のライブラリを使えるようにする。

ScalaのPlay FrameworkではJavaのライブラリが使えるので、使えるようにしたい。
今回はtwitter4jのライブラリを追加します。

build.sbtに追加する
libraryDependencies ++= Seq(
  jdbc,
  anorm,
  cache,
  "org.twitter4j" % "twitter4j-core" % "4.0.1"
)     

使いたいライブラリはここで探すとよい
http://mvnrepository.com/
検索してライブラリを選択するとMavenなどの記述方法が出るので、
SBTのタブを選択して、そこの内容を元に追加する。

追加をしたらplayに反映させます。
$ play update
[info] Loading project definition from /Users/xxxx/play/sample/project
[info] Set current project to sample (in build file:/Users/xxxx/play/sample/)
[info] Updating {file:/Users/xxxx/play/sample/}sample...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] downloading file:/Users/xxxx/.m2/repository/org/twitter4j/twitter4j-core/4.0.1/twitter4j-core-4.0.1.jar ...
[info]  [SUCCESSFUL ] org.twitter4j#twitter4j-core;4.0.1!twitter4j-core.jar (13ms)
[info] Done updating.
[success] Total time: 4 s, completed 2014/04/15 14:54:05

playに反映させたら、eclipseからも読めるようにします。
$ play eclipse
[info] Loading project definition from /Users/xxxx/play/sample/project
[info] Set current project to sample (in build file:/Users/xxxx/play/sample/)
[info] About to create Eclipse project files for your project(s).
[info] Successfully created Eclipse project files for project(s):
[info] sample
eclipseで該当プロジェクトをrefresh(F5)すると、twitter4jのjarが現れます。


参考URL
http://qiita.com/p07025/items/b1be18f1c0db3b8fa3af
http://kagamihoge.hatenablog.com/entry/20130430/1367310191

2014年4月21日月曜日

Mac MavericksにPlay Frameworkをインストール

Mac MavericksにPlay Frameworkをインストールしました。

バージョン
Mac OS X 10.9.2
Play Framework2.2.2
Java 1.7.0_51
Eclipse 4.3

Macにはhomebrewでインストールできます。便利!
$ brew install play
==> Downloading http://downloads.typesafe.com/play/2.2.2/play-2.2.2.zip
######################################################################## 100.0%
🍺  /usr/local/Cellar/play/2.2.2: 2184 files, 130M, built in 37 seconds
インストールが完了したのでplayを起動
$ play
Getting com.typesafe.play console_2.10 2.2.2 ...
:: retrieving :: org.scala-sbt#boot-app
 confs: [default]
 6 artifacts copied, 0 already retrieved (2012kB/30ms)
Getting Scala 2.10.3 (for console)...
:: retrieving :: org.scala-sbt#boot-scala
 confs: [default]
 5 artifacts copied, 0 already retrieved (24447kB/145ms)
       _
 _ __ | | __ _ _  _
| '_ \| |/ _' | || |
|  __/|_|\____|\__ /
|_|            |__/
 
play 2.2.2 built with Scala 2.10.3 (running Java 1.7.0_51), http://www.playframework.com
 
This is not a play application!
 
Use `play new` to create a new Play application in the current directory,
or go to an existing application and launch the development console using `play`.
 
You can also browse the complete documentation at http://www.playframework.com.


無事に起動したので早速プロジェクトを作成
途中プロジェクト名聞かれるのでそのままEnterを押す
ScalaとJavaどちらにするか聞かれるので、今回はScalaで。
$ play new sample
       _
 _ __ | | __ _ _  _
| '_ \| |/ _' | || |
|  __/|_|\____|\__ /
|_|            |__/
 
play 2.2.2 built with Scala 2.10.3 (running Java 1.7.0_51), http://www.playframework.com
 
The new application will be created in /Users/xxxx/play/sample
 
What is the application name? [sample]
そのままEnter
 
Which template do you want to use for this new application? 
 
  1             - Create a simple Scala application
  2             - Create a simple Java application
 
> 1
OK, application sample is created.
 
Have fun!


プロジェクトを作成したので、とりあえず起動。
$ cd sample/
$ play run
Getting org.scala-sbt sbt 0.13.0 ...
:: retrieving :: org.scala-sbt#boot-app
 confs: [default]
 43 artifacts copied, 0 already retrieved (12440kB/196ms)
[info] Loading project definition from /Users/xxxx/play/sample/project
[info] Set current project to sample (in build file:/Users/xxxx/play/sample/)
[info] Updating {file:/Users/xxxx/play/sample/}sample...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
 
--- (Running the application from SBT, auto-reloading is enabled) ---
 
[info] play - Listening for HTTP on /0:0:0:0:0:0:0:0:9000
 
(Server started, use Ctrl+D to stop and go back to the console...)
http://localhost:9000 にアクセスしてYour new application is readyのページが出ればOK
アクセスするとコンソールに以下がでる
[info] Compiling 5 Scala sources and 1 Java source to /Users/xxxx/play/sample/target/scala-2.10/classes...
[info] 'compiler-interface' not yet compiled for Scala 2.10.3. Compiling...
[info]   Compilation completed in 11.059 s
[info] play - Application started (Dev)
最初はdevモードで起動するので、起動しただけだとコンパイルはされずに、
初めてアクセスをしたタイミングでコンパイルが走る。

Ctrl+Dで停止すると以下が出て終了
[success] Total time: 155 s, completed 2014/04/15 12:10:15

次にEclipseで開発できるように以下を実行
$ play eclipse
[info] Loading project definition from /Users/xxxx/play/sample/project
[info] Set current project to sample (in build file:/Users/xxxx/play/sample/)
[info] About to create Eclipse project files for your project(s).
[info] Successfully created Eclipse project files for project(s):
[info] sample

Eclipse側の対応もやっていく。
eclipseにScala IDEをインストール
eclipseはeclipse-jee-kepler-SR2-macosx-cocoa-x86_64.tar.gzをダウンロードしてきて使用。
ソフトウエアを追加する。
Help → Install New Software
Addで以下を追加
Name:Scala IDE for Eclipse
Location:http://download.scala-ide.org/sdk/e38/scala210/dev/site/

Scala IDE for Eclipse
Scala IDE for Eclipse development support
Scala IDE for Eclipse Source Feature
Scala IDE plugins(incubation)
Sources
の5つが表示されるのですべてチェックしてNext

Will not be installedに以下が表示されていたけど、
InSynth Feature
keep my installation the same and modify the items being installed tobe compatible
を選択してNext。
もう一回Next。
Review Licensesの画面になるのでI accept〜を選択してFinish

Security Warningが出るけどOK
再起動するか聞かれるのでOK

再起動すると
Upgrade of Scala plugin detected.
Run setup diagnostics to ensure correct plugin settings?
と聞かれるのでYesとして、内容を確認してOKする

File-Import-General-Existing Projects into Workspace
Select root directory:に先ほど作ったplayのプロジェクトのディレクトリを指定してFinish
これでEclipseからPlayのプロジェクトを開発できます。