ScalaQuery で SQL を実行 - StaticQuery の使用
id:fits:20110702 で使った SQL を ScalaQuery でも実行してみました。
ScalaQuery ではテーブル毎にテーブルオブジェクトを定義するのが一般的なようですので、SQL を直接使うケースは少ないかもしれませんが、とりあえず StaticQuery で SQL を実行できます。
今回使用した環境は以下の通りです。
- sbt 0.10.0
- Scala 2.9.0.1
- ScalaQuery 0.9.4
- H2 1.3.157
サンプルソースは http://github.com/fits/try_samples/tree/master/blog/20110709/
準備
sbt 0.10 用のプロジェクト定義ファイル(build.sbt)を作成します。
以前の sbt で build.properties とプロジェクト定義ファイル(project/build/xxx.scala)に分かれていた設定を一つの build.sbt ファイルに定義できるようになっています。
libraryDependencies を使って依存モジュール(ScalaQuery と H2)の追加を、mainClass で実行クラスを定義しています。
build.sbt
name := "ScalaQuery StaticQuery Sample" version := "1.0" organization := "fits" scalaVersion := "2.9.0-1" libraryDependencies ++= Seq( "org.scalaquery" % "scalaquery_2.9.0" % "0.9.4", "com.h2database" % "h2" % "1.3.157" ) mainClass in(Compile, run) := Some("fits.sample.Sample")
なお、リポジトリに ScalaQuery の Scala 2.9.0.1 版が用意されていなかったため、Scala 2.9.0 版の ScalaQuery を使用する設定にしています。(Scala 2.9.0.1 を使わないのであれば "org.scalaquery" %% "scalaquery" % "0.9.4" でよい)
StaticQuery を使った SQL の実行
今回使用する機能は以下の通りです。
- Database.forXXX() で JDBC ドライバーを設定
- withSession で DB 接続を自動的にクローズ
- バインドパラメータを使わない SQL の実行に StaticQuery.queryNA[R]() を使用
- R は実行結果の型
- バインドパラメータを使う SQL の実行に StaticQuery.query[P,R]() を使用
- P はバインドパラメータの型、R は実行結果の型
以下では、Tuple を使った queryNA()、ケースクラスを使った queryNA()、Tuple を使った query() を試しています。
src/main/scala/fits.sample/Sample.scala
package fits.sample import org.scalaquery.session.{Database, Session} import org.scalaquery.session.Database.threadLocalSession import org.scalaquery.simple.StaticQuery import org.scalaquery.simple.GetResult object Sample { def main(args: Array[String]) = { Database.forURL("jdbc:h2:mem:", driver = "org.h2.Driver") withSession { val sql = """ SELECT * FROM ( SELECT pref_name, station_g_cd, station_name, count(*) as lines FROM CSVREAD('m_station.csv') S JOIN CSVREAD('m_pref.csv') P ON S.pref_cd=P.pref_cd GROUP BY station_g_cd, station_name ORDER BY lines DESC ) WHERE ROWNUM <= 10 """ //Tuple 版 - Tuple を使ってSQL実行結果の型を指定 StaticQuery.queryNA[(String, Int, String, Int)](sql) foreach {r => printf("%s駅 (%s) : %d\n", r._3, r._1, r._4) } println("---------------") //ケースクラス Station 版 case class Station(val prefName: String, val stationGroupCode: Int, val stationName: String, val lines: Int) //SQL実行結果を Station に変換する定義 implicit val getStationResult = GetResult(r => new Station(r <<, r <<, r <<, r <<)) //queryNA の暗黙の関数パラメータ GetResult を解決するために getStationResult が適用されるため、 //queryNA での型指定は不要 StaticQuery.queryNA(sql) foreach {r => printf("%s駅 (%s) : %d\n", r.stationName, r.prefName, r.lines) } println("---------------") val sql2 = """ SELECT rr_name, station_name FROM CSVREAD('m_station.csv') WHERE pref_cd = ? AND rr_name = ? """ //バインドパラメータを伴う SQL の実行例 // pref_cd=14, rr_name="JR" で検索 StaticQuery.query[(Int, String), (String, String)](sql2) foreach ( (14, "JR"), r => printf("%s %s駅\n", r._1, r._2) ) } } }
なお、threadLocalSession の import は重要で、これによって foreach 等での暗黙の関数パラメータ Session が解決されます。
そのため、threadLocalSession を import しなかった場合、以下のように自前で Session を implicit 定義するか、foreach に Session を明示的に渡す必要があります。
threadLocalSession を import しなかった場合
Database.forURL("jdbc:h2:mem:", driver = "org.h2.Driver") withSession {s: Session => implicit val session = s ・・・
実行
sbt を使って実行します。
実行例
scala_query_sql> java -jar "c:\sbt\sbt-launch-0.10.0.jar" ・・・ > run [info] Updating... [info] Done updating. [info] Compiling 1 Scala source to ・・・ [info] Running fits.sample.Sample 新宿駅 (東京都) : 12 横浜駅 (神奈川県) : 11 ・・・ --------------- 新宿駅 (東京都) : 12 横浜駅 (神奈川県) : 11 ・・・ --------------- JR 川崎駅 JR 横浜駅 ・・・