reading anorm 2.0

17
Reading Anorm 2.0 Kazuhiro Sera @seratch #akskscala

Upload: kazuhiro-sera

Post on 26-Jan-2015

107 views

Category:

Technology


0 download

DESCRIPTION

 

TRANSCRIPT

Page 1: Reading Anorm 2.0

Reading Anorm 2.0

Kazuhiro Sera@seratch

#akskscala

Page 2: Reading Anorm 2.0

Anorm のコンセプト

ドキュメントに明確に宣言されている

Anorm は ORM ではない

生 JDBC はつらい、よりよい API としての Anorm API

DB アクセスの DSL は SQL がベスト

SQL を生成する type safe DSL は誤った方向性だ

ORM と格闘するより SQL 書いた方がコスト小さい

Page 3: Reading Anorm 2.0

Anorm API 利用例 1

import anorm._implicit val conn: java.sql.Connection = ...

// insertSQL(“insert into emp values ({id},{name},{age})”) .on(‘id -> 1, ‘name -> “Andy”, ‘age -> 19) .executeUpdate()

// updateSQL(“update emp set name = {name} where id = {id}”) .on(‘id -> 1, ‘name -> “Brian”) .executeUpdate()

Page 4: Reading Anorm 2.0

Anorm API 利用例 2

// selectcase class Emp(id: Pk[Int], name: String, age: Option[Int])

import anorm.SqlParser._val emp: RowParser[Emp] = get[Pk[Int]](“id”) ~ get[String](“name”) ~ get[Option[Int]](“age”) map { case id ~ name ~ age => Emp(id, name, age) }

val emps: List[Emp] = SQL(“select * from emp”).as(emp.*)val emp: Option[Emp] = SQL(“select * from emp where id = {id}”) .on(‘id -> 1).as(emp.singleOpt)

Page 5: Reading Anorm 2.0

Anorm は Play 非依存

JDBC を隠蔽した使いやすい API を提供することを目的にしていて Play 本体に依存していない

コネクションやトランザクションの管理はスコープ外

拙作の実例(scalikejdbc-with-anorm20.g8)

Play アプリだけでなく、ちょっとした DB アクセスが必要な場合にも便利に使える

Page 7: Reading Anorm 2.0

Play20 になって変わった点

play.db.anorm から anorm に package 変更

Magic が不要ということで廃止になった(やり取り)

Typesafe の Maven リポジトリで配布されている(“play” %% “anorm” % “2.0”)

Play 本体にある play.db API でトランザクション管理が提供されている(play-scala にはない)

Page 8: Reading Anorm 2.0

ソースコードの構成

framework/src/anorm/src/main/scala

Anorm.scala

SqlParser.scala

SqlStatementParser.scala

TypeWrangler.scala

Utils.scala

ファイル構成はそのうち整理されるかも

Page 9: Reading Anorm 2.0

Anorm.scala

SQL(String): SqlQuery、まず SqlQuery から読む

SqlQuery は暗黙の型変換で SimpleSql や BatchSql としてもふるまうので、これらも読む

クエリの場合、getFilledStatement でバインド変数を set して as(...) 内部で executeQuery() の流れ

package object がここにいるのはちょっと行儀よくない

Page 10: Reading Anorm 2.0

Anorm API 利用例 3

case class Emp(id: Pk[Int], name: String, age: Option[Int])

val sql: SqlQuery = SQL(“select * from emp where id = {id}”)val query: SimpleSql[Row] = sql.on(‘id -> 1)val stream: Stream[Row] = query.apply()stream map { row => Emp(row[Pk[Int]]("id"), row[String]("name"), row[Option[Int]]("age")) }

Page 11: Reading Anorm 2.0

SqlParser.scala

SqlParser の API が RowParser を返すので、それを「~」メソッドの呼び出しで結合して使う感じ

SqlParser.get[T](String): RowParser[T]

SqlParser.str(String): RowParser[String]

中置型 case class ~[+A, +B](_1: A, _2: B)

RowParser[A]#~[B](RowParser[B]): RowParser[A ~ B]

Page 12: Reading Anorm 2.0

Anorm API 利用例 4

case class Emp(id: Pk[Int], name: String, age: Option[Int])

import anorm.SqlParser._

val rp: RowParser[Pk[Int] ~ String ~ Option[Int]] = get[Pk[Int]](“id”) ~ get[String](“name”) ~ get[Option[Int]](“age”) val emp: RowParser[Emp] = rp map { case id ~ name ~ age => Emp(id, name, age)}

val opt: ResultSetParser[Option[Emp]] = emp.singleOptval list: ResultSetParser[List[Emp]] = emp.*

Page 13: Reading Anorm 2.0

SqlStatementParser.scala

JavaTokenParsers を extends したパーザーコンビネータな実装クラス

“select id,name,age from emp where id = {id} and name = {name}” のような SQL からバインド変数名({id}、{name})を順序付きで取り出す処理

2.0 時点では Sql.sql(String) でのみ使用

Page 14: Reading Anorm 2.0

TypeWrangler.scala

Scala、Java の主要な型の scala.reflect.Manifest をつくって返すユーティリティ

Scala でいうと Byte、Short、Char、Int、Long、Float、Double、Boolean、Null、Unit が定義されている、対応する Java の型も同様

2.0 時点では使ってないようだが・・?

Page 15: Reading Anorm 2.0

Utils.scala

現状だと MayErr しかない

case class MayErr[+E, +A](e: Either[E, A])

MayErr と Either は相互に暗黙の型変換

Anorm.scala を見ると MayErr がかなり使われている

Page 16: Reading Anorm 2.0

play.db API

framework/src/play/src/main/scala/api/db/DB.scala

DBPlugin extends play.api.Plugin が Play の設定をもとに java.sql.DataSource を返す

object DB は内部的に DBApi(in DBPlugin)から java.sql.Connection をもらって withConnection、withTransaction ブロックを提供する

Page 17: Reading Anorm 2.0

まとめ・感想

Magic がなくなって実装がかなりシンプルになった

反面、object Emp extends Magic[Emp] を定義するだけで使うことができた Emp.find(“id = {id}”).on(‘id -> 1) のような API がなくなった

Scala の DB アクセスライブラリの中では、かなり使い勝手はよい API になっていると思うので、Play アプリ以外でも使ってみてください