PlayやScalaなど
最近、Play Frameworkを触っており、ついでにScala Pluginも少々触ってみた。
また、Scalaでログ解析処理を書いたりしてみたんだけど、やはりScalaは取っ付きにくいなぁ。
GroovyやRubyは簡単に使えるのに、Scalaは細かい所が使いにくい。まぁ、慣れの問題のような気もするけれど。
オブジェクト指向+関数型言語の、関数型言語部分が慣れないのだろうか。。
最初は、オブジェクト指向をメインで使って、ロジックを綺麗にするのに、関数型言語部分を使えばいいやぐらいに思っていたけれど、なかなかそうはいかないみたい。
関数型言語風に書かれたソースコードは、綺麗だけど理解するのに時間がかかる。まぁ、慣れればこっちの方が簡単に理解できるようになるのかも知れないけれど。
Play FrameworkのScala Pluginには、Anormと言うデータベース層のライブラリがある。これはよくあるORMではなく、SQLをラッピングして実行するライブラリでHibernateよりは、iBATISに似ている。
Anormのチュートリアルのコードをそのまま貼り付けると下のようになる。
case class Country( code:Id[String], name:String, population:Int, headOfState:Option[String] ) case class City( id:Pk[Int], name: String ) case class CountryLanguage( language:String, isOfficial:String ) object Country extends Magic[Country] object CountryLanguage extends Magic[CountryLanguage] object City extends Magic[City] val Some(country~languages~capital) = SQL( """ select * from Country c join CountryLanguage l on l.CountryCode = c.Code join City v on v.id = c.capital where c.code = {code} """ ) .on("code" -> "FRA") .as( Country.span( CountryLanguage * ) ~< City ? ) val countryName = country.name val capitalName = capital.name val headOfState = country.headOfState.getOrElse("No one?") val officialLanguage = languages.collect { case CountryLanguage(lang, "T") => lang }.headOption.getOrElse("No language?")
ジョインするselect文を書いて、SQLオブジェクトにくわせる。
SQL( """ select * from Country c join CountryLanguage l on l.CountryCode = c.Code join City v on v.id = c.capital where c.code = {code} """ )
※{code}はバインド変数だ。
fetchされた結果をケースクラスによるScalaのパターンマッチングで取得する。
マッチ文は以下のように書く
.as( Country.span( CountryLanguage * ) ~< City ? )
この取得部分に慣れが必要だけれど、すごくシンプルに書けるのが素敵だ。
Hibernate等のORMだと、ちょっと複雑なSQLを実行しようとすると「1+N問題」や「Eager Fetchの限界」とかがきて、パフォーマンスを考慮するとNative Queryとか使い出して、あまりORMのメリットを得られなくなってしまう気がする。
綺麗なコードを書くためには、もっと修行が必要だなぁ。